From 21ba27913861f027677f3ca507330fe4e866ed79 Mon Sep 17 00:00:00 2001 From: Lowie Benoot Date: Mon, 6 Nov 2023 13:49:06 +0800 Subject: [PATCH 01/46] =?UTF-8?q?=E2=9C=A8=20`Bullet`:=20implement=20ref?= =?UTF-8?q?=20forwarding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/bullet/Bullet.tsx | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/components/bullet/Bullet.tsx b/src/components/bullet/Bullet.tsx index c7107c7de..781b369f7 100644 --- a/src/components/bullet/Bullet.tsx +++ b/src/components/bullet/Bullet.tsx @@ -1,9 +1,10 @@ -import React, { PureComponent } from 'react'; +import React, { forwardRef } from 'react'; import Box from '../box'; import cx from 'classnames'; import theme from './theme.css'; import { BoxProps } from '../box/Box'; import { COLORS, SIZES, TINTS } from '../../constants'; +import { GenericComponent } from '../../@types/types'; export interface BulletProps extends Omit { /** A border color to give to the counter */ @@ -18,9 +19,8 @@ export interface BulletProps extends Omit { size?: Exclude<(typeof SIZES)[number], 'tiny' | 'fullscreen' | 'smallest' | 'hero'>; } -class Bullet extends PureComponent { - render() { - const { className, color = 'neutral', size = 'medium', borderColor, borderTint, ...others } = this.props; +const Bullet: GenericComponent = forwardRef( + ({ className, color = 'neutral', size = 'medium', borderColor, borderTint, ...others }, ref) => { const classNames = cx( theme['bullet'], theme[color], @@ -32,8 +32,10 @@ class Bullet extends PureComponent { className, ); - return ; - } -} + return ; + }, +); + +Bullet.displayName = 'Bullet'; export default Bullet; From 723f620d332a56c292e408510d15511024764b00 Mon Sep 17 00:00:00 2001 From: Lowie Benoot Date: Mon, 6 Nov 2023 14:17:56 +0800 Subject: [PATCH 02/46] =?UTF-8?q?=E2=9C=A8=20`Label`:=20implement=20ref=20?= =?UTF-8?q?forwarding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/label/Label.tsx | 102 ++++++++++++++++----------------- 1 file changed, 49 insertions(+), 53 deletions(-) diff --git a/src/components/label/Label.tsx b/src/components/label/Label.tsx index 5fb3594be..ce92d1015 100644 --- a/src/components/label/Label.tsx +++ b/src/components/label/Label.tsx @@ -1,5 +1,5 @@ import { IconInfoBadgedSmallFilled } from '@teamleader/ui-icons'; -import React, { ReactNode } from 'react'; +import React, { ReactNode, forwardRef } from 'react'; import { GenericComponent } from '../../@types/types'; import { SIZES } from '../../constants'; import Box from '../box'; @@ -19,59 +19,55 @@ export interface LabelProps extends Omit { tooltipProps?: Record; } -const Label: GenericComponent = ({ - children, - inverse = false, - required = false, - size = 'medium', - tooltip, - tooltipProps, - ...others -}) => { - const childProps = { - inverse, - marginTop: 1, - size, - }; +const Label: GenericComponent = forwardRef( + ({ children, inverse = false, required = false, size = 'medium', tooltip, tooltipProps, ...others }, ref) => { + const childProps = { + inverse, + marginTop: 1, + size, + }; - const Element = { - small: TextBodyCompact, - medium: TextBodyCompact, - large: TextDisplay, - }[size]; + const Element = { + small: TextBodyCompact, + medium: TextBodyCompact, + large: TextDisplay, + }[size]; - return ( - - {React.Children.map(children, (child) => - typeof child !== 'string' && React.isValidElement(child) ? ( - React.cloneElement(child, { ...childProps, ...child.props }) - ) : ( - - - {child} - - {required && ( - - * - - )} - {tooltip && ( - {tooltip}} - tooltipSize="small" - color={inverse ? 'neutral' : 'teal'} - tint={inverse ? 'lightest' : 'darkest'} - marginLeft={1} - {...tooltipProps} - > - - - )} - - ), - )} - - ); -}; + return ( + + {React.Children.map(children, (child) => + typeof child !== 'string' && React.isValidElement(child) ? ( + React.cloneElement(child, { ...childProps, ...child.props }) + ) : ( + + + {child} + + {required && ( + + * + + )} + {tooltip && ( + {tooltip}} + tooltipSize="small" + color={inverse ? 'neutral' : 'teal'} + tint={inverse ? 'lightest' : 'darkest'} + marginLeft={1} + {...tooltipProps} + > + + + )} + + ), + )} + + ); + }, +); + +Label.displayName = 'Label'; export default Label; From 3187e7d496bf5cfb7cc0f2b59d79b9c11a74f9ea Mon Sep 17 00:00:00 2001 From: Lowie Benoot Date: Mon, 6 Nov 2023 14:23:16 +0800 Subject: [PATCH 03/46] =?UTF-8?q?=E2=9C=A8=20`MarketingMenuItem`:=20implem?= =?UTF-8?q?ent=20ref=20forwarding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../marketingMenuItem/MarketingMenuItem.tsx | 95 +++++++++---------- 1 file changed, 45 insertions(+), 50 deletions(-) diff --git a/src/components/marketingMenuItem/MarketingMenuItem.tsx b/src/components/marketingMenuItem/MarketingMenuItem.tsx index f7cc1375e..ccb09b7c0 100644 --- a/src/components/marketingMenuItem/MarketingMenuItem.tsx +++ b/src/components/marketingMenuItem/MarketingMenuItem.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode, MouseEvent } from 'react'; +import React, { ReactNode, MouseEvent, forwardRef } from 'react'; import Box, { omitBoxProps, pickBoxProps } from '../box'; import Icon from '../icon'; import { TextBodyCompact } from '../typography'; @@ -25,61 +25,56 @@ export interface MarketingMenuItemProps extends Omit = ({ - onClick, - icon, - caption, - className = '', - element = 'button', - label, - selected = false, - ...others -}) => { - const classNames = cx( - theme['marketing-menu-item'], - { - [theme['is-selected']]: selected, - }, - className, - ); +const MarketingMenuItem: GenericComponent = forwardRef( + ({ onClick, icon, caption, className = '', element = 'button', label, selected = false, ...others }, ref) => { + const classNames = cx( + theme['marketing-menu-item'], + { + [theme['is-selected']]: selected, + }, + className, + ); - const boxProps = pickBoxProps(others); - const restProps = omitBoxProps(others); + const boxProps = pickBoxProps(others); + const restProps = omitBoxProps(others); - return ( - - - + return ( + - {label} - {caption && ( - - {caption} - - )} + + + {label} + {caption && ( + + {caption} + + )} + + {icon && {icon}} - {icon && {icon}} - - ); -}; + ); + }, +); + +MarketingMenuItem.displayName = 'MarketingMenuItem'; export default MarketingMenuItem; From b6be0a257207c233e0a6716089ff304b0181b32d Mon Sep 17 00:00:00 2001 From: Lowie Benoot Date: Mon, 6 Nov 2023 14:25:30 +0800 Subject: [PATCH 04/46] =?UTF-8?q?=E2=9C=A8=20`MenuItem`:=20implement=20ref?= =?UTF-8?q?=20forwarding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/menu/MenuItem.tsx | 145 ++++++++++++++++--------------- 1 file changed, 76 insertions(+), 69 deletions(-) diff --git a/src/components/menu/MenuItem.tsx b/src/components/menu/MenuItem.tsx index e88787d62..c8117465c 100644 --- a/src/components/menu/MenuItem.tsx +++ b/src/components/menu/MenuItem.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode, MouseEvent, CSSProperties } from 'react'; +import React, { ReactNode, MouseEvent, CSSProperties, forwardRef } from 'react'; import Box, { omitBoxProps, pickBoxProps } from '../box'; import Icon from '../icon'; import { TextBody } from '../typography'; @@ -32,84 +32,91 @@ export interface MenuItemProps extends Omit = ({ - onClick, - disabled = false, - icon, - caption, - children, - className = '', - style, - destructive = false, - element = 'button', - label, - selected = false, - ...others -}) => { - const handleClick = (event: MouseEvent) => { - if (onClick && !disabled) { - onClick(event); - } - }; - - const classNames = cx( - theme['menu-item'], +const MenuItem: GenericComponent = forwardRef( + ( { - [theme['is-selected']]: selected, - [theme['is-disabled']]: disabled, + onClick, + disabled = false, + icon, + caption, + children, + className = '', + style, + destructive = false, + element = 'button', + label, + selected = false, + ...others }, - className, - ); + ref, + ) => { + const handleClick = (event: MouseEvent) => { + if (onClick && !disabled) { + onClick(event); + } + }; + + const classNames = cx( + theme['menu-item'], + { + [theme['is-selected']]: selected, + [theme['is-disabled']]: disabled, + }, + className, + ); - const color = destructive ? 'ruby' : disabled ? 'neutral' : 'teal'; - const tint = disabled && destructive ? 'light' : disabled || destructive ? 'dark' : 'darkest'; + const color = destructive ? 'ruby' : disabled ? 'neutral' : 'teal'; + const tint = disabled && destructive ? 'light' : disabled || destructive ? 'dark' : 'darkest'; - const boxProps = pickBoxProps(others); - const restProps = omitBoxProps(others); + const boxProps = pickBoxProps(others); + const restProps = omitBoxProps(others); - return ( - - - {icon && ( - - {icon} - - )} + return ( + - {children} - {label && ( - - {label} - - )} - {caption && ( - - {caption} - + {icon && ( + + {icon} + )} + + {children} + {label && ( + + {label} + + )} + {caption && ( + + {caption} + + )} + - - ); -}; + ); + }, +); + +MenuItem.displayName = 'MenuItem'; export default MenuItem; From b55efa02584df0cb2aecd16b17b6c02cc08cbcca Mon Sep 17 00:00:00 2001 From: Lowie Benoot Date: Mon, 6 Nov 2023 15:06:13 +0800 Subject: [PATCH 05/46] =?UTF-8?q?=E2=9C=A8=20`MarketingStatusLabel`:=20imp?= =?UTF-8?q?lement=20ref=20forwarding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MarketingStatusLabel.tsx | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/components/marketingStatusLabel/MarketingStatusLabel.tsx b/src/components/marketingStatusLabel/MarketingStatusLabel.tsx index 253aa1ed1..f5d2a319e 100644 --- a/src/components/marketingStatusLabel/MarketingStatusLabel.tsx +++ b/src/components/marketingStatusLabel/MarketingStatusLabel.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode } from 'react'; +import React, { ReactNode, forwardRef } from 'react'; import Box from '../box'; import Icon from '../icon'; import cx from 'classnames'; @@ -16,14 +16,10 @@ export interface MarketingStatusLabelProps extends Omit = ({ - children, - className, - fullWidth = false, - size = 'medium', - icon, - ...others -}) => { +const MarketingStatusLabel: GenericComponent = forwardRef< + HTMLElement, + MarketingStatusLabelProps +>(({ children, className, fullWidth = false, size = 'medium', icon, ...others }, ref) => { const classNames = cx(theme['wrapper'], theme[`is-${size}`], className); const TextElement = size === 'small' ? UITextSmall : UITextBody; @@ -37,6 +33,7 @@ const MarketingStatusLabel: GenericComponent = ({ justifyContent="center" className={classNames} paddingHorizontal={2} + ref={ref} > {children} {icon && ( @@ -46,6 +43,8 @@ const MarketingStatusLabel: GenericComponent = ({ )} ); -}; +}); + +MarketingStatusLabel.displayName = 'MarketingStatusLabel'; export default MarketingStatusLabel; From 88c8c75d2d15aabd966e3467cb69256c3dd0864c Mon Sep 17 00:00:00 2001 From: Lowie Benoot Date: Mon, 6 Nov 2023 15:10:51 +0800 Subject: [PATCH 06/46] =?UTF-8?q?=E2=9C=A8=20`AdvancedCollapsible`:=20impl?= =?UTF-8?q?ement=20ref=20forwarding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AdvancedCollapsible.tsx | 75 ++++++++++--------- 1 file changed, 38 insertions(+), 37 deletions(-) diff --git a/src/components/advancedCollapsible/AdvancedCollapsible.tsx b/src/components/advancedCollapsible/AdvancedCollapsible.tsx index 1a3d26ff4..ef52a6a2a 100644 --- a/src/components/advancedCollapsible/AdvancedCollapsible.tsx +++ b/src/components/advancedCollapsible/AdvancedCollapsible.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode, useState } from 'react'; +import React, { ReactNode, forwardRef, useState } from 'react'; import { TextBody, Heading3 } from '../typography'; import Icon from '../icon'; import Box, { pickBoxProps } from '../box'; @@ -26,46 +26,47 @@ export interface AdvancedCollapsibleProps extends Omit { onChange?: (collapsed: boolean, event: React.MouseEvent) => void; } -const AdvancedCollapsible: GenericComponent = ({ - children, - color = 'teal', - indent = true, - size = 'medium', - title, - defaultIsCollapsed = true, - onChange, - ...others -}) => { - const [collapsed, setCollapsed] = useState(defaultIsCollapsed); +const AdvancedCollapsible: GenericComponent = forwardRef< + HTMLDivElement, + AdvancedCollapsibleProps +>( + ( + { children, color = 'teal', indent = true, size = 'medium', title, defaultIsCollapsed = true, onChange, ...others }, + ref, + ) => { + const [collapsed, setCollapsed] = useState(defaultIsCollapsed); - const boxProps = pickBoxProps(others); - const TitleElement = size === 'large' ? Heading3 : TextBody; + const boxProps = pickBoxProps(others); + const TitleElement = size === 'large' ? Heading3 : TextBody; - const handleTitleClick = (event: React.MouseEvent) => { - if (onChange) { - onChange(!collapsed, event); - } + const handleTitleClick = (event: React.MouseEvent) => { + if (onChange) { + onChange(!collapsed, event); + } - setCollapsed(!collapsed); - }; + setCollapsed(!collapsed); + }; - return ( - - - - {collapsed ? : } - - - {size === 'medium' ? {title} : title} - - - {!collapsed && ( - - {children} + return ( + + + + {collapsed ? : } + + + {size === 'medium' ? {title} : title} + - )} - - ); -}; + {!collapsed && ( + + {children} + + )} + + ); + }, +); + +AdvancedCollapsible.displayName = 'AdvancedCollapsible'; export default AdvancedCollapsible; From 686c279f088c325eb772bbdfdc5f134d3b992b15 Mon Sep 17 00:00:00 2001 From: Lowie Benoot Date: Mon, 6 Nov 2023 15:12:35 +0800 Subject: [PATCH 07/46] =?UTF-8?q?=E2=9C=A8=20`Banner`:=20implement=20ref?= =?UTF-8?q?=20forwarding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/banner/Banner.tsx | 75 ++++++++++++++++---------------- 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/src/components/banner/Banner.tsx b/src/components/banner/Banner.tsx index fdb49c52f..79548bde0 100644 --- a/src/components/banner/Banner.tsx +++ b/src/components/banner/Banner.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { forwardRef } from 'react'; import cx from 'classnames'; import Box, { BoxProps, Padding } from '../box'; @@ -30,43 +30,42 @@ export interface BannerProps extends Omit { size?: Exclude<(typeof SIZES)[number], 'tiny' | 'smallest' | 'hero' | 'fullscreen'>; } -const Banner = ({ - children, - className, - color = 'white', - size = 'medium', - icon, - onClose, - fullWidth, - ...others -}: BannerProps) => { - const classNames = cx(className, theme[color], theme['banner'], { [theme['banner_full-width']]: fullWidth }); +const Banner = forwardRef( + ( + { children, className, color = 'white', size = 'medium', icon, onClose, fullWidth, ...others }: BannerProps, + ref, + ) => { + const classNames = cx(className, theme[color], theme['banner'], { [theme['banner_full-width']]: fullWidth }); - return ( - -
- {icon && {icon}} - - {children} - - {onClose && ( - } - color={color === 'white' ? 'neutral' : color} - onClick={onClose} - /> - )} -
-
- ); -}; + return ( + +
+ {icon && {icon}} + + {children} + + {onClose && ( + } + color={color === 'white' ? 'neutral' : color} + onClick={onClose} + /> + )} +
+
+ ); + }, +); + +Banner.displayName = 'Banner'; export default Banner; From 5c7bde76f09f2762ac2d9296fddb3a62a7a50b7a Mon Sep 17 00:00:00 2001 From: Lowie Benoot Date: Mon, 6 Nov 2023 15:14:09 +0800 Subject: [PATCH 08/46] =?UTF-8?q?=E2=9C=A8=20`Container`:=20implement=20re?= =?UTF-8?q?f=20forwarding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/container/Container.tsx | 34 ++++++++++++++------------ 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/src/components/container/Container.tsx b/src/components/container/Container.tsx index 00e1b775b..0259da3e3 100644 --- a/src/components/container/Container.tsx +++ b/src/components/container/Container.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode } from 'react'; +import React, { ReactNode, forwardRef } from 'react'; import Box from '../box'; import cx from 'classnames'; import theme from './theme.css'; @@ -11,20 +11,24 @@ export interface ContainerProps extends Omit { fixed?: boolean; } -const Container: GenericComponent = ({ children, className, fixed, ...others }) => { - const classNames = cx( - theme['container'], - { - [theme['is-fixed']]: fixed, - }, - className, - ); +const Container: GenericComponent = forwardRef( + ({ children, className, fixed, ...others }, ref) => { + const classNames = cx( + theme['container'], + { + [theme['is-fixed']]: fixed, + }, + className, + ); - return ( - - {children} - - ); -}; + return ( + + {children} + + ); + }, +); + +Container.displayName = 'Container'; export default Container; From 406d2b122dce87820049174012af16850a78fd8f Mon Sep 17 00:00:00 2001 From: Lowie Benoot Date: Mon, 6 Nov 2023 15:16:15 +0800 Subject: [PATCH 09/46] =?UTF-8?q?=E2=9C=A8=20`Counter`:=20implement=20ref?= =?UTF-8?q?=20forwarding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/counter/Counter.tsx | 55 ++++++++++++++---------------- 1 file changed, 26 insertions(+), 29 deletions(-) diff --git a/src/components/counter/Counter.tsx b/src/components/counter/Counter.tsx index bb24b770d..46f508556 100644 --- a/src/components/counter/Counter.tsx +++ b/src/components/counter/Counter.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode } from 'react'; +import React, { ReactNode, forwardRef } from 'react'; import Box from '../box'; import cx from 'classnames'; import theme from './theme.css'; @@ -27,34 +27,31 @@ export interface CounterProps extends Omit { size: 'small' | 'medium'; } -const Counter: GenericComponent = ({ - children, - className, - color = 'neutral', - count, - maxCount, - size = 'medium', - borderColor, - borderTint, - ...others -}) => { - const classNames = cx( - uiUtilities['reset-font-smoothing'], - theme['counter'], - theme[color], - theme[size], - { - [theme[`border-${borderColor}-${borderTint}`]]: borderColor && borderTint, - [theme[`border-${borderColor}`]]: borderColor && !borderTint, - }, - className, - ); +const Counter: GenericComponent = forwardRef( + ( + { children, className, color = 'neutral', count, maxCount, size = 'medium', borderColor, borderTint, ...others }, + ref, + ) => { + const classNames = cx( + uiUtilities['reset-font-smoothing'], + theme['counter'], + theme[color], + theme[size], + { + [theme[`border-${borderColor}-${borderTint}`]]: borderColor && borderTint, + [theme[`border-${borderColor}`]]: borderColor && !borderTint, + }, + className, + ); - return ( - - {count > maxCount ? `${maxCount}+` : count} {children} - - ); -}; + return ( + + {count > maxCount ? `${maxCount}+` : count} {children} + + ); + }, +); + +Counter.displayName = 'Counter'; export default Counter; From cf3c0f76d0220df43c2e25a57d9be719c63e4b23 Mon Sep 17 00:00:00 2001 From: Lowie Benoot Date: Mon, 6 Nov 2023 15:32:53 +0800 Subject: [PATCH 10/46] =?UTF-8?q?=E2=9C=A8=20`DataGrid`:=20implement=20ref?= =?UTF-8?q?=20forwarding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/datagrid/Cell.tsx | 65 +-- src/components/datagrid/DataGrid.tsx | 369 +++++++++--------- src/components/datagrid/HeaderCell.tsx | 68 ++-- .../HeaderRowOverlay/HeaderRowOverlay.tsx | 50 ++- 4 files changed, 276 insertions(+), 276 deletions(-) diff --git a/src/components/datagrid/Cell.tsx b/src/components/datagrid/Cell.tsx index 289743211..b8477993c 100644 --- a/src/components/datagrid/Cell.tsx +++ b/src/components/datagrid/Cell.tsx @@ -1,4 +1,4 @@ -import React, { MouseEventHandler, ReactNode } from 'react'; +import React, { MouseEventHandler, ReactNode, forwardRef } from 'react'; import Box from '../box'; import cx from 'classnames'; import theme from './theme.css'; @@ -29,38 +29,43 @@ export interface CellProps extends Omit { onClick?: MouseEventHandler; } -const Cell: GenericComponent = ({ - align = 'left', - backgroundColor, - border, - children, - className, - flex = 1, - preventOverflow = true, - soft = false, - strong = false, - ...others -}) => { - const classNames = cx( - uiUtilities['reset-font-smoothing'], - theme['cell'], - theme[`align-${align}`], - theme[`flex-${flex}`], - theme[`has-background-${backgroundColor}`], - theme[`has-border-${border}`], +const Cell: GenericComponent = forwardRef( + ( { - [theme['is-soft']]: soft, - [theme['is-strong']]: strong, + align = 'left', + backgroundColor, + border, + children, + className, + flex = 1, + preventOverflow = true, + soft = false, + strong = false, + ...others }, - className, - ); + ref, + ) => { + const classNames = cx( + uiUtilities['reset-font-smoothing'], + theme['cell'], + theme[`align-${align}`], + theme[`flex-${flex}`], + theme[`has-background-${backgroundColor}`], + theme[`has-border-${border}`], + { + [theme['is-soft']]: soft, + [theme['is-strong']]: strong, + }, + className, + ); - return ( - - {preventOverflow ?
{children}
: children} -
- ); -}; + return ( + + {preventOverflow ?
{children}
: children} +
+ ); + }, +); Cell.displayName = 'DataGrid.Cell'; export default Cell; diff --git a/src/components/datagrid/DataGrid.tsx b/src/components/datagrid/DataGrid.tsx index f2c064991..68aace438 100644 --- a/src/components/datagrid/DataGrid.tsx +++ b/src/components/datagrid/DataGrid.tsx @@ -1,6 +1,6 @@ import cx from 'classnames'; import omit from 'lodash.omit'; -import React, { ChangeEvent, ReactElement, ReactNode, useEffect, useMemo, useRef, useState } from 'react'; +import React, { ChangeEvent, ReactElement, ReactNode, forwardRef, useEffect, useMemo, useRef, useState } from 'react'; import ReactResizeDetector from 'react-resize-detector'; import { GenericComponent } from '../../@types/types'; import Box from '../box'; @@ -47,221 +47,226 @@ interface DatagridComponent extends GenericComponent { FooterRow: GenericComponent; } -export const DataGrid: DatagridComponent = ({ - bordered = false, - children, - className, - processing = false, - comparableId, - selectable, - stickyFromLeft = 0, - stickyFromRight = 0, - onSelectionChange, - ...others -}) => { - const [hoveredRow, setHoveredRow] = useState(null); - const [isOverflowing, setOverflowing] = useState(false); - const [selectedRows, setSelectedRows] = useState([]); - const scrollableNode = useRef(null); - const [rowNodes, setRowNodes] = useState>(new Map()); - const totalRowChildrenWidth = useMemo( - () => - Array.from(rowNodes.values()) - .filter((rowDOMNode) => rowDOMNode.children) - .map((rowDOMNode) => - Array.from(rowDOMNode.children) - .map((child) => child.clientWidth) - .reduce((accumulatedChildWidth, currentChildWidth) => accumulatedChildWidth + currentChildWidth, 0), - ) - .reduce((maxRowWidth, currentRowWidth) => (currentRowWidth > maxRowWidth ? currentRowWidth : maxRowWidth), 0), - // eslint-disable-next-line react-hooks/exhaustive-deps - [rowNodes, rowNodes.size], - ); - const childrenArray: (ReactElement | ReactElement[])[] = !Array.isArray(children) ? [children] : children; - const bodyRowCount = (childrenArray.find((child) => Array.isArray(child)) as ReactElement[] | undefined)?.length; - - const handleSelectionChange = (selection: React.Key[], event: ChangeEvent | null = null) => { - if (onSelectionChange) { - onSelectionChange(selection, event); - } - }; +export const DataGrid: DatagridComponent = forwardRef( + ( + { + bordered = false, + children, + className, + processing = false, + comparableId, + selectable, + stickyFromLeft = 0, + stickyFromRight = 0, + onSelectionChange, + ...others + }, + ref, + ) => { + const [hoveredRow, setHoveredRow] = useState(null); + const [isOverflowing, setOverflowing] = useState(false); + const [selectedRows, setSelectedRows] = useState([]); + const scrollableNode = useRef(null); + const [rowNodes, setRowNodes] = useState>(new Map()); + const totalRowChildrenWidth = useMemo( + () => + Array.from(rowNodes.values()) + .filter((rowDOMNode) => rowDOMNode.children) + .map((rowDOMNode) => + Array.from(rowDOMNode.children) + .map((child) => child.clientWidth) + .reduce((accumulatedChildWidth, currentChildWidth) => accumulatedChildWidth + currentChildWidth, 0), + ) + .reduce((maxRowWidth, currentRowWidth) => (currentRowWidth > maxRowWidth ? currentRowWidth : maxRowWidth), 0), + // eslint-disable-next-line react-hooks/exhaustive-deps + [rowNodes, rowNodes.size], + ); + const childrenArray: (ReactElement | ReactElement[])[] = !Array.isArray(children) ? [children] : children; + const bodyRowCount = (childrenArray.find((child) => Array.isArray(child)) as ReactElement[] | undefined)?.length; - const handleHeaderRowSelectionChange = (checked: boolean, event: ChangeEvent) => { - const allBodyRowIndexes = React.Children.map(children, (child) => { - if (isReactElement(child) && isComponentOfType(BodyRow, child)) { - return child.key; + const handleSelectionChange = (selection: React.Key[], event: ChangeEvent | null = null) => { + if (onSelectionChange) { + onSelectionChange(selection, event); } - }); + }; - const selectedBodyRowIndexes = checked ? allBodyRowIndexes ?? [] : []; - setSelectedRows(selectedBodyRowIndexes); - handleSelectionChange(selectedBodyRowIndexes, event); - }; + const handleHeaderRowSelectionChange = (checked: boolean, event: ChangeEvent) => { + const allBodyRowIndexes = React.Children.map(children, (child) => { + if (isReactElement(child) && isComponentOfType(BodyRow, child)) { + return child.key; + } + }); - const handleBodyRowMouseEnter = (row: ReactElement, event: MouseEvent) => { - const { onClick, onMouseOver } = row.props; + const selectedBodyRowIndexes = checked ? allBodyRowIndexes ?? [] : []; + setSelectedRows(selectedBodyRowIndexes); + handleSelectionChange(selectedBodyRowIndexes, event); + }; - onClick && setHoveredRow(row.key); - onMouseOver && onMouseOver(event); - }; + const handleBodyRowMouseEnter = (row: ReactElement, event: MouseEvent) => { + const { onClick, onMouseOver } = row.props; - const handleBodyRowMouseLeave = (row: ReactElement, event: MouseEvent) => { - const { onClick, onMouseOut } = row.props; + onClick && setHoveredRow(row.key); + onMouseOver && onMouseOver(event); + }; - onClick && setHoveredRow(null); - onMouseOut && onMouseOut(event); - }; + const handleBodyRowMouseLeave = (row: ReactElement, event: MouseEvent) => { + const { onClick, onMouseOut } = row.props; - const handleBodyRowSelectionChange = (rowIndex: React.Key | null, event: ChangeEvent) => { - if (rowIndex === null) { - return; - } + onClick && setHoveredRow(null); + onMouseOut && onMouseOut(event); + }; - const rows = selectedRows.includes(rowIndex) - ? selectedRows.filter((row) => row !== rowIndex) - : [...selectedRows, rowIndex]; + const handleBodyRowSelectionChange = (rowIndex: React.Key | null, event: ChangeEvent) => { + if (rowIndex === null) { + return; + } - setSelectedRows(rows); - handleSelectionChange(rows, event); - }; + const rows = selectedRows.includes(rowIndex) + ? selectedRows.filter((row) => row !== rowIndex) + : [...selectedRows, rowIndex]; - const handleResize = () => { - if (isElementOverflowingX(scrollableNode.current)) { - setOverflowing(true); - } else { - setOverflowing(false); - } - }; + setSelectedRows(rows); + handleSelectionChange(rows, event); + }; - useEffect(() => { - handleResize(); - }); + const handleResize = () => { + if (isElementOverflowingX(scrollableNode.current)) { + setOverflowing(true); + } else { + setOverflowing(false); + } + }; - useEffect(() => { - setSelectedRows([]); - handleSelectionChange([]); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [comparableId]); + useEffect(() => { + handleResize(); + }); - const classNames = cx( - theme['data-grid'], - { - [theme['is-bordered']]: bordered, - [theme['is-overflowing']]: isOverflowing, - }, - className, - ); + useEffect(() => { + setSelectedRows([]); + handleSelectionChange([]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [comparableId]); - const rest = omit(others, ['comparableId', 'onSelectionChange']); + const classNames = cx( + theme['data-grid'], + { + [theme['is-bordered']]: bordered, + [theme['is-overflowing']]: isOverflowing, + }, + className, + ); - const sectionLeftClassNames = cx(theme['section'], theme['is-sticky-left'], { - [theme['has-blend-right']]: selectable || stickyFromLeft > 0, - [theme['has-border-right']]: selectable || stickyFromLeft > 0, - }); + const rest = omit(others, ['comparableId', 'onSelectionChange']); - return ( - - {processing && ( -
- -
- )} - {selectedRows.length > 0 && - React.Children.map(children, (child) => { - if (isReactElement(child) && isComponentOfType(HeaderRowOverlay, child)) { - return React.cloneElement(child, { - numSelectedRows: selectedRows.length, - }); - } - })} - - {(selectable || stickyFromLeft > 0) && ( -
- {React.Children.map(children, (child) => { - if (isReactElement(child)) { - if (isComponentOfType(HeaderRow, child)) { - return React.cloneElement(child, { - onSelectionChange: handleHeaderRowSelectionChange, - selected: selectedRows.length === bodyRowCount, - selectable, - sliceTo: stickyFromLeft > 0 ? stickyFromLeft : 0, - }); - } else if (isComponentOfType(BodyRow, child)) { - return React.cloneElement(child, { - hovered: hoveredRow === child.key, - onMouseEnter: (event: MouseEvent) => handleBodyRowMouseEnter(child, event), - onMouseLeave: (event: MouseEvent) => handleBodyRowMouseLeave(child, event), - onSelectionChange: (checked: boolean, event: ChangeEvent) => - handleBodyRowSelectionChange(child.key, event), - selected: child.key ? selectedRows.indexOf(child.key) !== -1 : false, - selectable, - sliceTo: stickyFromLeft > 0 ? stickyFromLeft : 0, - }); - } else if (isComponentOfType(FooterRow, child)) { - return React.cloneElement(child, { - preserveSelectableSpace: selectable, - sliceTo: stickyFromLeft > 0 ? stickyFromLeft : 0, - }); - } - } - })} + const sectionLeftClassNames = cx(theme['section'], theme['is-sticky-left'], { + [theme['has-blend-right']]: selectable || stickyFromLeft > 0, + [theme['has-border-right']]: selectable || stickyFromLeft > 0, + }); + + return ( + + {processing && ( +
+
)} -
- {React.Children.map(children, (child, key) => { - if (isReactElement(child)) { - if (isComponentOfType(HeaderRow, child) || isComponentOfType(FooterRow, child)) { - return React.cloneElement(child, { - sliceFrom: stickyFromLeft > 0 ? stickyFromLeft : 0, - sliceTo: stickyFromRight > 0 ? -stickyFromRight : undefined, - ref: (rowNode: HTMLElement | null) => rowNode && setRowNodes(rowNodes.set(key, rowNode)), - style: isOverflowing - ? { - minWidth: `${totalRowChildrenWidth - 10}px`, - } - : undefined, - }); - } else if (isComponentOfType(BodyRow, child)) { - return React.cloneElement(child, { - hovered: hoveredRow === child.key, - onMouseEnter: (event: MouseEvent) => handleBodyRowMouseEnter(child, event), - onMouseLeave: (event: MouseEvent) => handleBodyRowMouseLeave(child, event), - sliceFrom: stickyFromLeft > 0 ? stickyFromLeft : 0, - sliceTo: stickyFromRight > 0 ? -stickyFromRight : undefined, - ref: (rowNode: HTMLElement | null) => rowNode && setRowNodes(rowNodes.set(key, rowNode)), - style: isOverflowing - ? { - minWidth: `${totalRowChildrenWidth - 10}px`, - } - : undefined, - }); - } + {selectedRows.length > 0 && + React.Children.map(children, (child) => { + if (isReactElement(child) && isComponentOfType(HeaderRowOverlay, child)) { + return React.cloneElement(child, { + numSelectedRows: selectedRows.length, + }); } })} -
- {stickyFromRight > 0 && ( -
- {React.Children.map(children, (child) => { + + {(selectable || stickyFromLeft > 0) && ( +
+ {React.Children.map(children, (child) => { + if (isReactElement(child)) { + if (isComponentOfType(HeaderRow, child)) { + return React.cloneElement(child, { + onSelectionChange: handleHeaderRowSelectionChange, + selected: selectedRows.length === bodyRowCount, + selectable, + sliceTo: stickyFromLeft > 0 ? stickyFromLeft : 0, + }); + } else if (isComponentOfType(BodyRow, child)) { + return React.cloneElement(child, { + hovered: hoveredRow === child.key, + onMouseEnter: (event: MouseEvent) => handleBodyRowMouseEnter(child, event), + onMouseLeave: (event: MouseEvent) => handleBodyRowMouseLeave(child, event), + onSelectionChange: (checked: boolean, event: ChangeEvent) => + handleBodyRowSelectionChange(child.key, event), + selected: child.key ? selectedRows.indexOf(child.key) !== -1 : false, + selectable, + sliceTo: stickyFromLeft > 0 ? stickyFromLeft : 0, + }); + } else if (isComponentOfType(FooterRow, child)) { + return React.cloneElement(child, { + preserveSelectableSpace: selectable, + sliceTo: stickyFromLeft > 0 ? stickyFromLeft : 0, + }); + } + } + })} +
+ )} +
+ {React.Children.map(children, (child, key) => { if (isReactElement(child)) { if (isComponentOfType(HeaderRow, child) || isComponentOfType(FooterRow, child)) { - return React.cloneElement(child, { sliceFrom: -stickyFromRight }); + return React.cloneElement(child, { + sliceFrom: stickyFromLeft > 0 ? stickyFromLeft : 0, + sliceTo: stickyFromRight > 0 ? -stickyFromRight : undefined, + ref: (rowNode: HTMLElement | null) => rowNode && setRowNodes(rowNodes.set(key, rowNode)), + style: isOverflowing + ? { + minWidth: `${totalRowChildrenWidth - 10}px`, + } + : undefined, + }); } else if (isComponentOfType(BodyRow, child)) { return React.cloneElement(child, { hovered: hoveredRow === child.key, onMouseEnter: (event: MouseEvent) => handleBodyRowMouseEnter(child, event), onMouseLeave: (event: MouseEvent) => handleBodyRowMouseLeave(child, event), - sliceFrom: -stickyFromRight, + sliceFrom: stickyFromLeft > 0 ? stickyFromLeft : 0, + sliceTo: stickyFromRight > 0 ? -stickyFromRight : undefined, + ref: (rowNode: HTMLElement | null) => rowNode && setRowNodes(rowNodes.set(key, rowNode)), + style: isOverflowing + ? { + minWidth: `${totalRowChildrenWidth - 10}px`, + } + : undefined, }); } } })}
- )} + {stickyFromRight > 0 && ( +
+ {React.Children.map(children, (child) => { + if (isReactElement(child)) { + if (isComponentOfType(HeaderRow, child) || isComponentOfType(FooterRow, child)) { + return React.cloneElement(child, { sliceFrom: -stickyFromRight }); + } else if (isComponentOfType(BodyRow, child)) { + return React.cloneElement(child, { + hovered: hoveredRow === child.key, + onMouseEnter: (event: MouseEvent) => handleBodyRowMouseEnter(child, event), + onMouseLeave: (event: MouseEvent) => handleBodyRowMouseLeave(child, event), + sliceFrom: -stickyFromRight, + }); + } + } + })} +
+ )} +
+ - - - ); -}; + ); + }, +); DataGrid.HeaderRow = HeaderRow; DataGrid.HeaderRowOverlay = HeaderRowOverlay; diff --git a/src/components/datagrid/HeaderCell.tsx b/src/components/datagrid/HeaderCell.tsx index 2078b6979..334328df8 100644 --- a/src/components/datagrid/HeaderCell.tsx +++ b/src/components/datagrid/HeaderCell.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { forwardRef } from 'react'; import theme from './theme.css'; import Cell, { CellProps } from './Cell'; import Icon from '../icon'; @@ -14,46 +14,40 @@ export interface HeaderCellProps extends CellProps { sorted?: 'asc' | 'desc'; } -const HeaderCell: GenericComponent = ({ - align = 'left', - children, - className, - onClick, - sortable, - sorted, - ...others -}) => { - const renderSortedIndicators = () => { - if (sorted === 'asc' || (!sorted && sortable)) { - return ; - } +const HeaderCell: GenericComponent = forwardRef( + ({ align = 'left', children, className, onClick, sortable, sorted, ...others }, ref) => { + const renderSortedIndicators = () => { + if (sorted === 'asc' || (!sorted && sortable)) { + return ; + } - if (sorted === 'desc') { - return ; - } + if (sorted === 'desc') { + return ; + } - return null; - }; + return null; + }; - const classNames = cx( - theme['header-cell'], - { - [theme['is-sortable']]: sortable, - [theme['is-sorted']]: sorted === 'asc' || sorted === 'desc', - }, - className, - ); + const classNames = cx( + theme['header-cell'], + { + [theme['is-sortable']]: sortable, + [theme['is-sorted']]: sorted === 'asc' || sorted === 'desc', + }, + className, + ); - return ( - - {sortable && align === 'right' && {renderSortedIndicators()}} - - {children} - - {sortable && align === 'left' && {renderSortedIndicators()}} - - ); -}; + return ( + + {sortable && align === 'right' && {renderSortedIndicators()}} + + {children} + + {sortable && align === 'left' && {renderSortedIndicators()}} + + ); + }, +); HeaderCell.displayName = 'DataGrid.HeaderCell'; export default HeaderCell; diff --git a/src/components/datagrid/HeaderRowOverlay/HeaderRowOverlay.tsx b/src/components/datagrid/HeaderRowOverlay/HeaderRowOverlay.tsx index 0843fab33..c87ef80ec 100644 --- a/src/components/datagrid/HeaderRowOverlay/HeaderRowOverlay.tsx +++ b/src/components/datagrid/HeaderRowOverlay/HeaderRowOverlay.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode } from 'react'; +import React, { ReactNode, forwardRef } from 'react'; import cx from 'classnames'; import Box from '../../box'; import BulkActions from './BulkActions'; @@ -19,33 +19,29 @@ export interface HeaderRowOverlayProps { numSelectedRowsLabel?: (numSelectedRows?: number) => string; } -const HeaderRowOverlay: GenericComponent = ({ - children, - className, - headerCellCheckboxSize, - numSelectedRows = 0, - numSelectedRowsLabel, - ...others -}) => { - const classNames = cx( - theme['header-row-overlay'], - theme[`data-grid-checkbox-size-${headerCellCheckboxSize}`], - className, - ); +const HeaderRowOverlay: GenericComponent = forwardRef( + ({ children, className, headerCellCheckboxSize, numSelectedRows = 0, numSelectedRowsLabel, ...others }, ref) => { + const classNames = cx( + theme['header-row-overlay'], + theme[`data-grid-checkbox-size-${headerCellCheckboxSize}`], + className, + ); - return ( - - - - - ); -}; + return ( + + + + + ); + }, +); export default HeaderRowOverlay; HeaderRowOverlay.displayName = 'DataGrid.HeaderRowOverlay'; From cb2cd7d16b1f4eaf3318a44c5758a3cc542acf83 Mon Sep 17 00:00:00 2001 From: Lowie Benoot Date: Mon, 6 Nov 2023 15:33:04 +0800 Subject: [PATCH 11/46] =?UTF-8?q?=E2=9C=A8=20`DatePicker`:=20implement=20r?= =?UTF-8?q?ef=20forwarding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/datepicker/DatePicker.tsx | 171 ++++++++++++----------- 1 file changed, 89 insertions(+), 82 deletions(-) diff --git a/src/components/datepicker/DatePicker.tsx b/src/components/datepicker/DatePicker.tsx index 0c8825c62..13f0d39e4 100644 --- a/src/components/datepicker/DatePicker.tsx +++ b/src/components/datepicker/DatePicker.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, forwardRef } from 'react'; import DayPicker, { DayModifiers, DayPickerProps } from 'react-day-picker'; import Box, { pickBoxProps } from '../box'; import NavigationBar from './NavigationBar'; @@ -37,95 +37,102 @@ export interface DatePickerProps onDayClick?: (day: Date, modifiers: DayModifiers, event: React.MouseEvent) => void; } -const DatePicker: GenericComponent = ({ - bordered = true, - className, - modifiers, - size = 'medium', - withMonthPicker, - showWeekNumbers, - initialMonth, - onChange, - onDayClick, - ...others -}) => { - const [selectedDate, setSelectedDate] = useState(others.selectedDate); - const [selectedMonth, setSelectedMonth] = useState(); +const DatePicker: GenericComponent = forwardRef( + ( + { + bordered = true, + className, + modifiers, + size = 'medium', + withMonthPicker, + showWeekNumbers, + initialMonth, + onChange, + onDayClick, + ...others + }, + ref, + ) => { + const [selectedDate, setSelectedDate] = useState(others.selectedDate); + const [selectedMonth, setSelectedMonth] = useState(); - useEffect(() => { - setSelectedDate(others.selectedDate); - setSelectedMonth(others.selectedDate); - }, [others.selectedDate]); + useEffect(() => { + setSelectedDate(others.selectedDate); + setSelectedMonth(others.selectedDate); + }, [others.selectedDate]); - const handleDayClick = (day: Date, modifiers: DayModifiers, event: React.MouseEvent) => { - if (modifiers[theme['disabled']]) { - return; - } + const handleDayClick = (day: Date, modifiers: DayModifiers, event: React.MouseEvent) => { + if (modifiers[theme['disabled']]) { + return; + } - setSelectedDate(day); - onChange && onChange(day); - onDayClick && onDayClick(day, modifiers, event); - }; + setSelectedDate(day); + onChange && onChange(day); + onDayClick && onDayClick(day, modifiers, event); + }; - const handleYearMonthChange = (selectedMonth: Date) => { - setSelectedMonth(selectedMonth); - }; + const handleYearMonthChange = (selectedMonth: Date) => { + setSelectedMonth(selectedMonth); + }; - const getMonthPickerSize = () => { - const monthPickerSizeByDatePickerSize: Record< - string, - Exclude<(typeof SIZES)[number], 'tiny' | 'fullscreen' | 'hero'> - > = { - small: 'smallest', - medium: showWeekNumbers ? 'medium' : 'small', - large: 'large', + const getMonthPickerSize = () => { + const monthPickerSizeByDatePickerSize: Record< + string, + Exclude<(typeof SIZES)[number], 'tiny' | 'fullscreen' | 'hero'> + > = { + small: 'smallest', + medium: showWeekNumbers ? 'medium' : 'small', + large: 'large', + }; + + return monthPickerSizeByDatePickerSize[size]; }; - return monthPickerSizeByDatePickerSize[size]; - }; + const classNames = cx( + uiUtilities['reset-font-smoothing'], + theme['date-picker'], + theme[`is-${size}`], + { + [theme['is-bordered']]: bordered, + }, + className, + ); - const classNames = cx( - uiUtilities['reset-font-smoothing'], - theme['date-picker'], - theme[`is-${size}`], - { - [theme['is-bordered']]: bordered, - }, - className, - ); + return ( + + } + onDayClick={handleDayClick} + selectedDays={selectedDate} + weekdayElement={({ ...props }) => } + showWeekNumbers={showWeekNumbers} + fixedWeeks + captionElement={ + withMonthPicker + ? ({ date, locale, localeUtils }) => ( + + ) + : undefined + } + /> + + ); + }, +); - return ( - - } - onDayClick={handleDayClick} - selectedDays={selectedDate} - weekdayElement={({ ...props }) => } - showWeekNumbers={showWeekNumbers} - fixedWeeks - captionElement={ - withMonthPicker - ? ({ date, locale, localeUtils }) => ( - - ) - : undefined - } - /> - - ); -}; +DatePicker.displayName = 'DatePicker'; export default DatePicker; From 6e12077efef7441ba0ec717536573fd49309fc0b Mon Sep 17 00:00:00 2001 From: Lowie Benoot Date: Mon, 6 Nov 2023 15:33:16 +0800 Subject: [PATCH 12/46] =?UTF-8?q?=E2=9C=A8=20`DetailPage`:=20implement=20r?= =?UTF-8?q?ef=20forwarding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/detailPage/DetailPage.tsx | 10 ++- src/components/detailPage/DetailPageBody.tsx | 18 ++-- .../detailPage/DetailPageHeader.tsx | 86 +++++++++---------- 3 files changed, 57 insertions(+), 57 deletions(-) diff --git a/src/components/detailPage/DetailPage.tsx b/src/components/detailPage/DetailPage.tsx index ee381d9ff..8110f6253 100644 --- a/src/components/detailPage/DetailPage.tsx +++ b/src/components/detailPage/DetailPage.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode } from 'react'; +import React, { ReactNode, forwardRef } from 'react'; import DetailPageBody, { DetailPageBodyProps } from './DetailPageBody'; import DetailPageHeader, { DetailPageHeaderProps } from './DetailPageHeader'; import Box from '../box'; @@ -14,13 +14,15 @@ interface DetailPageComponent extends GenericComponent { Header: GenericComponent; } -const DetailPage: DetailPageComponent = ({ children, ...others }) => { +const DetailPage: DetailPageComponent = forwardRef(({ children, ...others }, ref) => { return ( - + {children} ); -}; +}); + +DetailPage.displayName = 'DetailPage'; DetailPage.Body = DetailPageBody; DetailPage.Header = DetailPageHeader; diff --git a/src/components/detailPage/DetailPageBody.tsx b/src/components/detailPage/DetailPageBody.tsx index 63d254cbc..708282cc2 100644 --- a/src/components/detailPage/DetailPageBody.tsx +++ b/src/components/detailPage/DetailPageBody.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode } from 'react'; +import React, { ReactNode, forwardRef } from 'react'; import { GenericComponent } from '../../@types/types'; import Container from '../container'; import { ContainerProps } from '../container/Container'; @@ -7,13 +7,15 @@ export interface DetailPageBodyProps extends Omit = ({ children, ...others }) => { - return ( - - {children} - - ); -}; +const DetailPageBody: GenericComponent = forwardRef( + ({ children, ...others }, ref) => { + return ( + + {children} + + ); + }, +); DetailPageBody.displayName = 'DetailPage.Body'; diff --git a/src/components/detailPage/DetailPageHeader.tsx b/src/components/detailPage/DetailPageHeader.tsx index 42f9ffb9d..7cdc2d810 100644 --- a/src/components/detailPage/DetailPageHeader.tsx +++ b/src/components/detailPage/DetailPageHeader.tsx @@ -1,5 +1,5 @@ import { IconArrowLeftSmallOutline } from '@teamleader/ui-icons'; -import React, { ReactNode } from 'react'; +import React, { ReactNode, forwardRef } from 'react'; import { GenericComponent } from '../../@types/types'; import BadgedLink from '../badgedLink'; import { BadgedLinkProps } from '../badgedLink/BadgedLink'; @@ -18,52 +18,48 @@ export interface DetailPageHeaderProps extends Omit { titleSuffix?: React.ReactNode; } -const DetailPageHeader: GenericComponent = ({ - backLinkProps, - children, - title, - titleColor = 'teal', - titleSuffix, - ...others -}) => { - return ( - - - {backLinkProps && ( - - } inherit={false} /> - - )} - - - - {title} - - {titleSuffix && {titleSuffix}} +const DetailPageHeader: GenericComponent = forwardRef( + ({ backLinkProps, children, title, titleColor = 'teal', titleSuffix, ...others }, ref) => { + return ( + + + {backLinkProps && ( + + } inherit={false} /> + + )} + + + + {title} + + {titleSuffix && {titleSuffix}} + + {children && ( + + {children} + + )} - {children && ( - - {children} - - )} - - - ); -}; + + ); + }, +); DetailPageHeader.displayName = 'DetailPage.Header'; From 8dd0a779d77a22ac750a1938b92c9044fee5af78 Mon Sep 17 00:00:00 2001 From: Lowie Benoot Date: Mon, 6 Nov 2023 15:33:27 +0800 Subject: [PATCH 13/46] =?UTF-8?q?=E2=9C=A8=20`EmptyState`:=20implement=20r?= =?UTF-8?q?ef=20forwarding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/emptyState/EmptyState.tsx | 89 ++++++++++++------------ 1 file changed, 43 insertions(+), 46 deletions(-) diff --git a/src/components/emptyState/EmptyState.tsx b/src/components/emptyState/EmptyState.tsx index f4afaadd5..277484507 100644 --- a/src/components/emptyState/EmptyState.tsx +++ b/src/components/emptyState/EmptyState.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode } from 'react'; +import React, { ReactNode, forwardRef } from 'react'; import Box from '../box'; import cx from 'classnames'; import { @@ -28,51 +28,48 @@ const illustrationMap = { large: , }; -const EmptyState: GenericComponent = ({ - className, - metaText, - hidePointer = false, - size = 'medium', - title, - action, - ...others -}) => { - const classNames = cx( - theme['wrapper'], - theme[`is-${size}`], - { - [theme['has-pointer']]: title, - }, - className as string, - ); +const EmptyState: GenericComponent = forwardRef( + ({ className, metaText, hidePointer = false, size = 'medium', title, action, ...others }, ref) => { + const classNames = cx( + theme['wrapper'], + theme[`is-${size}`], + { + [theme['has-pointer']]: title, + }, + className as string, + ); - return ( - - {title && !hidePointer &&
{illustrationMap[size]}
} -
- {title && {title}} - {metaText && ( - - {metaText} - - )} - {action && ( - - } inherit={false} /> - - )} -
-
- ); -}; + return ( + + {title && !hidePointer &&
{illustrationMap[size]}
} +
+ {title && {title}} + {metaText && ( + + {metaText} + + )} + {action && ( + + } inherit={false} /> + + )} +
+
+ ); + }, +); + +EmptyState.displayName = 'EmptyState'; export default EmptyState; From 6bd7e1278e83077b95a7b2b97fdaf504bab21694 Mon Sep 17 00:00:00 2001 From: Lowie Benoot Date: Mon, 6 Nov 2023 15:34:45 +0800 Subject: [PATCH 14/46] =?UTF-8?q?=E2=9C=A8=20`Grid.GridItem`:=20implement?= =?UTF-8?q?=20ref=20forwarding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/grid/GridItem.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/grid/GridItem.tsx b/src/components/grid/GridItem.tsx index 65f4fc4a9..23d2014d0 100644 --- a/src/components/grid/GridItem.tsx +++ b/src/components/grid/GridItem.tsx @@ -1,12 +1,14 @@ -import React, { ReactNode } from 'react'; +import React, { ReactNode, forwardRef } from 'react'; import { GenericComponent } from '../../@types/types'; export type GridItemProps = Partial<{ children: ReactNode; area: string }>; -const GridItem: GenericComponent = ({ children, area }) => { +const GridItem: GenericComponent = forwardRef(({ children, area }) => { const gridItemStyles = { gridArea: area }; return
{children}
; -}; +}); + +GridItem.displayName = 'Grid'; export default GridItem; From c2dae02f614bb4737c5944992a8eeb658b40c49c Mon Sep 17 00:00:00 2001 From: Lowie Benoot Date: Mon, 6 Nov 2023 15:36:18 +0800 Subject: [PATCH 15/46] =?UTF-8?q?=E2=9C=A8=20`Island`:=20implement=20ref?= =?UTF-8?q?=20forwarding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/island/Island.tsx | 62 ++++++++++++++++---------------- 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/src/components/island/Island.tsx b/src/components/island/Island.tsx index bb323eb38..e9446e6cd 100644 --- a/src/components/island/Island.tsx +++ b/src/components/island/Island.tsx @@ -1,4 +1,4 @@ -import React, { MouseEvent, ReactNode } from 'react'; +import React, { MouseEvent, ReactNode, forwardRef } from 'react'; import Box, { pickBoxProps } from '../box'; import cx from 'classnames'; import theme from './theme.css'; @@ -25,37 +25,35 @@ export interface IslandProps extends Omit { size?: Exclude<(typeof SIZES)[number], 'tiny' | 'smallest' | 'hero' | 'fullscreen'>; } -const Island: GenericComponent = ({ - children, - className, - color = 'white', - size = 'medium', - onClick, - ...others -}: IslandProps) => { - const classNames = cx(theme[color], className); - const boxProps = pickBoxProps(others); +const Island: GenericComponent = forwardRef( + ({ children, className, color = 'white', size = 'medium', onClick, ...others }: IslandProps, ref) => { + const classNames = cx(theme[color], className); + const boxProps = pickBoxProps(others); - return ( - - {children} - - ); -}; + return ( + + {children} + + ); + }, +); + +Island.displayName = 'Island'; export default Island; From 41b613d731d0ee4a10323a4ddff2d65892b882cc Mon Sep 17 00:00:00 2001 From: Lowie Benoot Date: Mon, 6 Nov 2023 15:42:48 +0800 Subject: [PATCH 16/46] =?UTF-8?q?=E2=9C=A8=20`LabelValuePair`:=20implement?= =?UTF-8?q?=20ref=20forwarding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/labelValuePair/Label.tsx | 31 ++++++---- .../labelValuePair/LabelValuePair.tsx | 60 ++++++++++--------- .../labelValuePair/LabelValuePairGroup.tsx | 13 ++-- src/components/labelValuePair/Value.tsx | 10 ++-- 4 files changed, 64 insertions(+), 50 deletions(-) diff --git a/src/components/labelValuePair/Label.tsx b/src/components/labelValuePair/Label.tsx index a356edec5..1c54dcdd8 100644 --- a/src/components/labelValuePair/Label.tsx +++ b/src/components/labelValuePair/Label.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode } from 'react'; +import React, { ReactNode, forwardRef } from 'react'; import { GenericComponent } from '../../@types/types'; import { BoxProps } from '../box/Box'; import { Heading5 } from '../typography'; @@ -8,18 +8,23 @@ export interface LabelProps extends Omit { children?: ReactNode; } -const Label: GenericComponent = ({ children, inline, ...others }) => ( - - {children} - +const Label: GenericComponent = forwardRef( + ({ children, inline, ...others }, ref) => ( + + {children} + + ), ); +Label.displayName = 'LabelValuePair.Label'; + export default Label; diff --git a/src/components/labelValuePair/LabelValuePair.tsx b/src/components/labelValuePair/LabelValuePair.tsx index a952ff1ef..3b2095cb3 100644 --- a/src/components/labelValuePair/LabelValuePair.tsx +++ b/src/components/labelValuePair/LabelValuePair.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode } from 'react'; +import React, { ReactNode, forwardRef } from 'react'; import { GenericComponent } from '../../@types/types'; import Box from '../box'; import { BoxProps } from '../box/Box'; @@ -18,38 +18,40 @@ interface LabelValuePairComponent extends GenericComponent Value: GenericComponent; } -const LabelValuePair: LabelValuePairComponent = ({ alignValue = 'left', children, inline = true, ...others }) => ( - - {React.Children.map(children, (child) => { - if (!isReactElement(child)) { - return null; - } - if (isComponentOfType(Label, child) && React.isValidElement(child)) { - return React.cloneElement(child, { inline, ...child.props }); - } +const LabelValuePair: LabelValuePairComponent = forwardRef( + ({ alignValue = 'left', children, inline = true, ...others }, ref) => ( + + {React.Children.map(children, (child) => { + if (!isReactElement(child)) { + return null; + } + if (isComponentOfType(Label, child) && React.isValidElement(child)) { + return React.cloneElement(child, { inline, ...child.props }); + } - if (isComponentOfType(Value, child) && React.isValidElement(child)) { - return React.cloneElement(child, { - justifyContent: alignValue === 'left' ? 'flex-start' : 'flex-end', - paddingVertical: inline ? 1 : 0, - textAlign: alignValue, - // @ts-ignore TS acting weird, child.props is there - ...child.props, - }); - } - })} - + if (isComponentOfType(Value, child) && React.isValidElement(child)) { + return React.cloneElement(child, { + justifyContent: alignValue === 'left' ? 'flex-start' : 'flex-end', + paddingVertical: inline ? 1 : 0, + textAlign: alignValue, + // @ts-ignore TS acting weird, child.props is there + ...child.props, + }); + } + })} + + ), ); +LabelValuePair.displayName = 'LabelValuePair'; LabelValuePair.Label = Label; -LabelValuePair.Label.displayName = 'LabelValuePair.Label'; LabelValuePair.Value = Value; -LabelValuePair.Value.displayName = 'LabelValuePair.Value'; export default LabelValuePair; diff --git a/src/components/labelValuePair/LabelValuePairGroup.tsx b/src/components/labelValuePair/LabelValuePairGroup.tsx index 81ab1ac7f..4b3c3bc10 100644 --- a/src/components/labelValuePair/LabelValuePairGroup.tsx +++ b/src/components/labelValuePair/LabelValuePairGroup.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode } from 'react'; +import React, { ReactNode, forwardRef } from 'react'; import { GenericComponent } from '../../@types/types'; import Box from '../box'; import { BoxProps } from '../box/Box'; @@ -9,15 +9,20 @@ export interface LabelValuePairGroupProps extends Omit { title?: string | ReactNode; } -const LabelValuePairGroup: GenericComponent = ({ children, title, ...others }) => { +const LabelValuePairGroup: GenericComponent = forwardRef< + HTMLElement, + LabelValuePairGroupProps +>(({ children, title, ...others }, ref) => { return ( - + {title} {children} ); -}; +}); + +LabelValuePairGroup.displayName = 'LabelValuePairGroup'; export default LabelValuePairGroup; diff --git a/src/components/labelValuePair/Value.tsx b/src/components/labelValuePair/Value.tsx index f89d5d38c..7fea33aad 100644 --- a/src/components/labelValuePair/Value.tsx +++ b/src/components/labelValuePair/Value.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode } from 'react'; +import React, { ReactNode, forwardRef } from 'react'; import { GenericComponent } from '../../@types/types'; import Box from '../box'; import { BoxProps } from '../box/Box'; @@ -7,10 +7,12 @@ export interface ValueProps extends Omit { children?: ReactNode; } -const Value: GenericComponent = ({ children, ...others }) => ( - +const Value: GenericComponent = forwardRef(({ children, ...others }, ref) => ( + {children} -); +)); + +Value.displayName = 'LabelValuePair.Value'; export default Value; From b651a0fa9f526442534a4a3c42756f2a2d593999 Mon Sep 17 00:00:00 2001 From: Lowie Benoot Date: Mon, 6 Nov 2023 15:43:57 +0800 Subject: [PATCH 17/46] =?UTF-8?q?=E2=9C=A8=20`LoadingBar`:=20implement=20r?= =?UTF-8?q?ef=20forwarding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/loadingBar/LoadingBar.tsx | 40 +++++++++++------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/src/components/loadingBar/LoadingBar.tsx b/src/components/loadingBar/LoadingBar.tsx index a33afdffd..1ac6ab752 100644 --- a/src/components/loadingBar/LoadingBar.tsx +++ b/src/components/loadingBar/LoadingBar.tsx @@ -1,5 +1,5 @@ import cx from 'classnames'; -import React from 'react'; +import React, { forwardRef } from 'react'; import { GenericComponent } from '../../@types/types'; import { COLORS, SIZES, TINTS } from '../../constants'; import Box from '../box'; @@ -17,25 +17,23 @@ export interface LoadingBarProps extends Omit { tint?: (typeof TINTS)[number]; } -const LoadingBar: GenericComponent = ({ - className, - color = 'mint', - size = 'small', - tint = 'normal', - ...others -}) => { - const classNames = cx( - theme['loading-bar'], - theme[`is-${color}`], - theme[`is-${size}`], - theme[`is-${tint}`], - className, - ); - return ( - -
- - ); -}; +const LoadingBar: GenericComponent = forwardRef( + ({ className, color = 'mint', size = 'small', tint = 'normal', ...others }, ref) => { + const classNames = cx( + theme['loading-bar'], + theme[`is-${color}`], + theme[`is-${size}`], + theme[`is-${tint}`], + className, + ); + return ( + +
+ + ); + }, +); + +LoadingBar.displayName = 'LoadingBar'; export default LoadingBar; From f24e5849b8e58d7147e8138896ffb23cc2794591 Mon Sep 17 00:00:00 2001 From: Lowie Benoot Date: Mon, 6 Nov 2023 15:45:36 +0800 Subject: [PATCH 18/46] =?UTF-8?q?=E2=9C=A8=20`MarketingButtonGroup`:=20imp?= =?UTF-8?q?lement=20ref=20forwarding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MarketingButtonGroup.tsx | 73 ++++++++++--------- 1 file changed, 38 insertions(+), 35 deletions(-) diff --git a/src/components/marketingButtonGroup/MarketingButtonGroup.tsx b/src/components/marketingButtonGroup/MarketingButtonGroup.tsx index c03949db0..0a15a3e5f 100644 --- a/src/components/marketingButtonGroup/MarketingButtonGroup.tsx +++ b/src/components/marketingButtonGroup/MarketingButtonGroup.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode } from 'react'; +import React, { ReactNode, forwardRef } from 'react'; import omit from 'lodash.omit'; import Box, { omitBoxProps } from '../box'; import Button, { ButtonProps } from './Button'; @@ -23,47 +23,50 @@ interface MarketingButtonGroupComponent extends GenericComponent; } -const MarketingButtonGroup: MarketingButtonGroupComponent = ({ children, className, value, onChange, ...others }) => { - const handleChange = (value: string, event: React.ChangeEvent) => { - if (onChange) { - onChange(value, event); - } - }; +const MarketingButtonGroup: MarketingButtonGroupComponent = forwardRef( + ({ children, className, value, onChange, ...others }, ref) => { + const handleChange = (value: string, event: React.ChangeEvent) => { + if (onChange) { + onChange(value, event); + } + }; - const classNames = cx(theme['group'], theme['segmented'], className); + const classNames = cx(theme['group'], theme['segmented'], className); - return ( - - {React.Children.map(children, (child) => { - if (!React.isValidElement(child)) { - return; - } + return ( + + {React.Children.map(children, (child) => { + if (!React.isValidElement(child)) { + return; + } - if (!isComponentOfType(Button, child)) { - return child; - } + if (!isComponentOfType(Button, child)) { + return child; + } - let optionalChildProps = {}; - if (value) { - optionalChildProps = { - active: child.props.value === value, - onClick: (event: React.ChangeEvent) => handleChange(child.props.value || '', event), - }; - } + let optionalChildProps = {}; + if (value) { + optionalChildProps = { + active: child.props.value === value, + onClick: (event: React.ChangeEvent) => handleChange(child.props.value || '', event), + }; + } - const childProps = omit(child.props, ['value']); - const groupProps = omit(others, ['onChange']); + const childProps = omit(child.props, ['value']); + const groupProps = omit(others, ['onChange']); - return React.createElement(child.type, { - ...childProps, - ...optionalChildProps, - ...omitBoxProps(groupProps), - }); - })} - - ); -}; + return React.createElement(child.type, { + ...childProps, + ...optionalChildProps, + ...omitBoxProps(groupProps), + }); + })} + + ); + }, +); +MarketingButtonGroup.displayName = 'MarketingButtonGroup'; MarketingButtonGroup.Button = Button; export default MarketingButtonGroup; From 2c4c844cde6017ef6cf22b7aee5dd5fec86f7c00 Mon Sep 17 00:00:00 2001 From: Lowie Benoot Date: Mon, 6 Nov 2023 15:47:02 +0800 Subject: [PATCH 19/46] =?UTF-8?q?=E2=9C=A8=20`MarketingLockBadge`:=20imple?= =?UTF-8?q?ment=20ref=20forwarding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../marketingLockBadge/MarketingLockBadge.tsx | 43 +++++++++++-------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/src/components/marketingLockBadge/MarketingLockBadge.tsx b/src/components/marketingLockBadge/MarketingLockBadge.tsx index 9de86e910..bedef8848 100644 --- a/src/components/marketingLockBadge/MarketingLockBadge.tsx +++ b/src/components/marketingLockBadge/MarketingLockBadge.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { forwardRef } from 'react'; import Box from '../box'; import Icon from '../icon'; import cx from 'classnames'; @@ -13,24 +13,29 @@ export interface MarketingLockBadgeProps extends Omit { size?: Exclude<(typeof SIZES)[number], 'tiny' | 'large' | 'fullscreen' | 'smallest' | 'hero'>; } -const MarketingLockBadge: GenericComponent = ({ className, size = 'medium', ...others }) => { - const classNames = cx(theme['wrapper'], theme[`is-${size}`], className); +const MarketingLockBadge: GenericComponent = forwardRef( + ({ className, size = 'medium', ...others }, ref) => { + const classNames = cx(theme['wrapper'], theme[`is-${size}`], className); - return ( - - - - - - ); -}; + return ( + + + + + + ); + }, +); + +MarketingLockBadge.displayName = 'MarketingLockBadge'; export default MarketingLockBadge; From d275ad1da872e4102bd2126e979aa9c92183213a Mon Sep 17 00:00:00 2001 From: Lowie Benoot Date: Mon, 6 Nov 2023 15:47:59 +0800 Subject: [PATCH 20/46] =?UTF-8?q?=E2=9C=A8=20`MarketingMarker`:=20implemen?= =?UTF-8?q?t=20ref=20forwarding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../marketingMarker/MarketingMarker.tsx | 37 +++++++++++-------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/src/components/marketingMarker/MarketingMarker.tsx b/src/components/marketingMarker/MarketingMarker.tsx index 761ba5279..c4d371aab 100644 --- a/src/components/marketingMarker/MarketingMarker.tsx +++ b/src/components/marketingMarker/MarketingMarker.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode } from 'react'; +import React, { ReactNode, forwardRef } from 'react'; import Box from '../box'; import cx from 'classnames'; import theme from './theme.css'; @@ -10,21 +10,26 @@ export interface MarketingMarkerProps extends Omit { className?: string; } -const MarketingMarker: GenericComponent = ({ children, className, ...others }) => { - const classNames = cx(theme['marker'], className); +const MarketingMarker: GenericComponent = forwardRef( + ({ children, className, ...others }, ref) => { + const classNames = cx(theme['marker'], className); - return ( - - {children} - - ); -}; + return ( + + {children} + + ); + }, +); + +MarketingMarker.displayName = 'MarketingMarker'; export default MarketingMarker; From 763c9a2e1c495eab944cae7fb36c9313f827ce63 Mon Sep 17 00:00:00 2001 From: Lowie Benoot Date: Mon, 6 Nov 2023 15:49:38 +0800 Subject: [PATCH 21/46] =?UTF-8?q?=E2=9C=A8=20`MarketingHeading1`:=20implem?= =?UTF-8?q?ent=20ref=20forwarding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../marketingTypography/MarketingHeading1.tsx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/components/marketingTypography/MarketingHeading1.tsx b/src/components/marketingTypography/MarketingHeading1.tsx index aa193a2eb..5fe69fd56 100644 --- a/src/components/marketingTypography/MarketingHeading1.tsx +++ b/src/components/marketingTypography/MarketingHeading1.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode } from 'react'; +import React, { ReactNode, forwardRef } from 'react'; import Box from '../box'; import cx from 'classnames'; import theme from './theme.css'; @@ -10,14 +10,19 @@ export interface MarketingHeadingProps extends Omit { className?: string; } -const MarketingHeading1: GenericComponent = ({ children, className, ...others }) => { +const MarketingHeading1: GenericComponent = forwardRef< + HTMLHeadingElement, + MarketingHeadingProps +>(({ children, className, ...others }, ref) => { const classNames = cx(theme['heading-1'], className); return ( - + {children} ); -}; +}); + +MarketingHeading1.displayName = 'MarketingHeading1'; export default MarketingHeading1; From 90fd1196d46faf0f22eb18b23ffe4db579334388 Mon Sep 17 00:00:00 2001 From: Lowie Benoot Date: Mon, 6 Nov 2023 15:50:38 +0800 Subject: [PATCH 22/46] =?UTF-8?q?=E2=9C=A8=20`MarketingHeading2`:=20implem?= =?UTF-8?q?ent=20ref=20forwarding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../marketingTypography/MarketingHeading2.tsx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/components/marketingTypography/MarketingHeading2.tsx b/src/components/marketingTypography/MarketingHeading2.tsx index 9e9e67d80..eade5acde 100644 --- a/src/components/marketingTypography/MarketingHeading2.tsx +++ b/src/components/marketingTypography/MarketingHeading2.tsx @@ -1,18 +1,23 @@ -import React from 'react'; +import React, { forwardRef } from 'react'; import Box from '../box'; import cx from 'classnames'; import theme from './theme.css'; import { GenericComponent } from '../../@types/types'; import { MarketingHeadingProps } from './MarketingHeading1'; -const MarketingHeading2: GenericComponent = ({ children, className, ...others }) => { +const MarketingHeading2: GenericComponent = forwardRef< + HTMLHeadingElement, + MarketingHeadingProps +>(({ children, className, ...others }, ref) => { const classNames = cx(theme['heading-2'], className); return ( - + {children} ); -}; +}); + +MarketingHeading2.displayName = 'MarketingHeading2'; export default MarketingHeading2; From cd1b09bc98570b1341a10d4cd5af4287ce62b201 Mon Sep 17 00:00:00 2001 From: Lowie Benoot Date: Mon, 6 Nov 2023 15:51:48 +0800 Subject: [PATCH 23/46] =?UTF-8?q?=E2=9C=A8=20`Message`:=20implement=20ref?= =?UTF-8?q?=20forwarding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/message/Message.tsx | 126 ++++++++++++++--------------- 1 file changed, 60 insertions(+), 66 deletions(-) diff --git a/src/components/message/Message.tsx b/src/components/message/Message.tsx index cf7750cb9..7ac5ca07d 100644 --- a/src/components/message/Message.tsx +++ b/src/components/message/Message.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { forwardRef } from 'react'; import { IconBellMediumOutline, IconCheckmarkBadgedMediumFilled, @@ -71,76 +71,70 @@ export interface MessageProps extends Omit { title?: React.ReactNode; } -const Message: GenericComponent = ({ - children, - inline, - onClose, - primaryAction, - secondaryAction, - showIcon, - status = 'info', - title, - ...others -}) => { - const hasActions = Boolean(primaryAction || secondaryAction); - const IconToRender = iconMap[status]; +const Message: GenericComponent = forwardRef( + ({ children, inline, onClose, primaryAction, secondaryAction, showIcon, status = 'info', title, ...others }, ref) => { + const hasActions = Boolean(primaryAction || secondaryAction); + const IconToRender = iconMap[status]; - return ( - - {status && ( + return ( + + {status && ( + + {showIcon && ( + + + + )} + + )} - {showIcon && ( - - - - )} - - )} - - - {title && ( - - {title} - - )} - {children} - {hasActions && ( - - {secondaryAction &&