From c8dcddcb20c3d0dae63b134c5334debb14a7de65 Mon Sep 17 00:00:00 2001 From: Tresau-IBM <148357638+Tresau-IBM@users.noreply.github.com> Date: Tue, 31 Oct 2023 16:03:29 -0400 Subject: [PATCH] feat: Add Typescript typings to Breadcrumb, BreadcrumbItems, and BreadcrumbSkeleton (#15033) * feat: add types for Breadcrumbs * revert: undo PropType changes and supress TS error instead --------- Co-authored-by: Andrea N. Cardona --- .all-contributorsrc | 9 ++ README.md | 1 + ...mb.Skeleton.js => Breadcrumb.Skeleton.tsx} | 10 +- .../src/components/Breadcrumb/Breadcrumb.js | 63 --------- .../src/components/Breadcrumb/Breadcrumb.tsx | 82 +++++++++++ .../components/Breadcrumb/BreadcrumbItem.js | 106 -------------- .../components/Breadcrumb/BreadcrumbItem.tsx | 130 ++++++++++++++++++ 7 files changed, 231 insertions(+), 170 deletions(-) rename packages/react/src/components/Breadcrumb/{Breadcrumb.Skeleton.js => Breadcrumb.Skeleton.tsx} (78%) delete mode 100644 packages/react/src/components/Breadcrumb/Breadcrumb.js create mode 100644 packages/react/src/components/Breadcrumb/Breadcrumb.tsx delete mode 100644 packages/react/src/components/Breadcrumb/BreadcrumbItem.js create mode 100644 packages/react/src/components/Breadcrumb/BreadcrumbItem.tsx diff --git a/.all-contributorsrc b/.all-contributorsrc index d788de393aad..35cfde183594 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1314,6 +1314,15 @@ "contributions": [ "code" ] + }, + { + "login": "Tresau-IBM", + "name": "Tresau-IBM", + "avatar_url": "https://avatars.githubusercontent.com/u/148357638?v=4", + "profile": "https://github.com/Tresau-IBM", + "contributions": [ + "code" + ] } ], "commitConvention": "none" diff --git a/README.md b/README.md index 433ac4f52f05..59462a443f5f 100644 --- a/README.md +++ b/README.md @@ -261,6 +261,7 @@ check out our [Contributing Guide](/.github/CONTRIBUTING.md) and our
Niraj Sah

💻
Allison Ishida

💻
Alex Lewitt

💻 +
Tresau-IBM

💻 diff --git a/packages/react/src/components/Breadcrumb/Breadcrumb.Skeleton.js b/packages/react/src/components/Breadcrumb/Breadcrumb.Skeleton.tsx similarity index 78% rename from packages/react/src/components/Breadcrumb/Breadcrumb.Skeleton.js rename to packages/react/src/components/Breadcrumb/Breadcrumb.Skeleton.tsx index 75980d815ae8..2e1540405ff1 100644 --- a/packages/react/src/components/Breadcrumb/Breadcrumb.Skeleton.js +++ b/packages/react/src/components/Breadcrumb/Breadcrumb.Skeleton.tsx @@ -10,6 +10,14 @@ import React from 'react'; import cx from 'classnames'; import { usePrefix } from '../../internal/usePrefix'; +export interface BreadcrumbSkeletonProps + extends React.HTMLAttributes { + /** + * Specify an optional className to add. + */ + className?: string; +} + function Item() { const prefix = usePrefix(); @@ -20,7 +28,7 @@ function Item() { ); } -function BreadcrumbSkeleton({ className, ...rest }) { +function BreadcrumbSkeleton({ className, ...rest }: BreadcrumbSkeletonProps) { const prefix = usePrefix(); const classes = cx(`${prefix}--breadcrumb`, `${prefix}--skeleton`, className); diff --git a/packages/react/src/components/Breadcrumb/Breadcrumb.js b/packages/react/src/components/Breadcrumb/Breadcrumb.js deleted file mode 100644 index 0aeafe22b21c..000000000000 --- a/packages/react/src/components/Breadcrumb/Breadcrumb.js +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2023 - * - * 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 PropTypes from 'prop-types'; -import React from 'react'; -import cx from 'classnames'; -import { usePrefix } from '../../internal/usePrefix'; - -const Breadcrumb = React.forwardRef(function Breadcrumb( - { - 'aria-label': ariaLabel, - children, - className: customClassNameNav, - noTrailingSlash, - ...rest - }, - ref -) { - const prefix = usePrefix(); - const className = cx({ - [`${prefix}--breadcrumb`]: true, - [`${prefix}--breadcrumb--no-trailing-slash`]: noTrailingSlash, - }); - - return ( - - ); -}); - -Breadcrumb.displayName = 'Breadcrumb'; -Breadcrumb.propTypes = { - /** - * Specify the label for the breadcrumb container - */ - 'aria-label': PropTypes.string, - - /** - * Pass in the BreadcrumbItem's for your Breadcrumb - */ - children: PropTypes.node, - - /** - * Specify an optional className to be applied to the container node - */ - className: PropTypes.string, - - /** - * Optional prop to omit the trailing slash for the breadcrumbs - */ - noTrailingSlash: PropTypes.bool, -}; - -export default Breadcrumb; diff --git a/packages/react/src/components/Breadcrumb/Breadcrumb.tsx b/packages/react/src/components/Breadcrumb/Breadcrumb.tsx new file mode 100644 index 000000000000..7b1e11a76919 --- /dev/null +++ b/packages/react/src/components/Breadcrumb/Breadcrumb.tsx @@ -0,0 +1,82 @@ +/** + * Copyright IBM Corp. 2016, 2023 + * + * 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 PropTypes from 'prop-types'; +import React, { PropsWithChildren } from 'react'; +import cx from 'classnames'; +import { usePrefix } from '../../internal/usePrefix'; +import { ForwardRefReturn } from '../../types/common'; + +export interface BreadcrumbProps extends React.HTMLAttributes { + /** + * Specify the label for the breadcrumb container + */ + 'aria-label'?: string; + + /** + * Specify an optional className to be applied to the container node + */ + className?: string; + + /** + * Optional prop to omit the trailing slash for the breadcrumbs + */ + noTrailingSlash?: boolean; +} + +const Breadcrumb: ForwardRefReturn = + React.forwardRef(function Breadcrumb( + { + 'aria-label': ariaLabel, + children, + className: customClassNameNav, + noTrailingSlash, + ...rest + }: PropsWithChildren, + ref: React.Ref + ) { + const prefix = usePrefix(); + const className = cx({ + [`${prefix}--breadcrumb`]: true, + [`${prefix}--breadcrumb--no-trailing-slash`]: noTrailingSlash, + }); + + return ( + + ); + }); + +Breadcrumb.displayName = 'Breadcrumb'; +Breadcrumb.propTypes = { + /** + * Specify the label for the breadcrumb container + */ + 'aria-label': PropTypes.string, + + /** + * Pass in the BreadcrumbItem's for your Breadcrumb + */ + children: PropTypes.node, + + /** + * Specify an optional className to be applied to the container node + */ + className: PropTypes.string, + + /** + * Optional prop to omit the trailing slash for the breadcrumbs + */ + noTrailingSlash: PropTypes.bool, +}; + +export default Breadcrumb; diff --git a/packages/react/src/components/Breadcrumb/BreadcrumbItem.js b/packages/react/src/components/Breadcrumb/BreadcrumbItem.js deleted file mode 100644 index 18b3e8eb960e..000000000000 --- a/packages/react/src/components/Breadcrumb/BreadcrumbItem.js +++ /dev/null @@ -1,106 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2023 - * - * 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 PropTypes from 'prop-types'; -import React from 'react'; -import cx from 'classnames'; -import Link from '../Link'; -import { OverflowMenuHorizontal } from '@carbon/icons-react'; -import { usePrefix } from '../../internal/usePrefix'; -import { Text } from '../Text'; - -const BreadcrumbItem = React.forwardRef(function BreadcrumbItem( - { - 'aria-current': ariaCurrent, - children, - className: customClassName, - href, - isCurrentPage, - ...rest - }, - ref -) { - const prefix = usePrefix(); - const className = cx({ - [`${prefix}--breadcrumb-item`]: true, - // We set the current class only if `isCurrentPage` is passed in and we do - // not have an `aria-current="page"` set for the breadcrumb item - [`${prefix}--breadcrumb-item--current`]: - isCurrentPage && ariaCurrent !== 'page', - [customClassName]: !!customClassName, - }); - - if ( - children.type && - children.type.displayName !== undefined && - children.type.displayName.includes('OverflowMenu') - ) { - const horizontalOverflowIcon = ( - - ); - return ( -
  • - {React.cloneElement(children, { - menuOptionsClass: `${prefix}--breadcrumb-menu-options`, - menuOffset: { top: 10, left: 59 }, - renderIcon: () => horizontalOverflowIcon, - })} -
  • - ); - } - - if (typeof children === 'string') { - return ( -
  • - {href ? ( - - {children} - - ) : ( - {children} - )} -
  • - ); - } - - return ( -
  • - {React.cloneElement(children, { - 'aria-current': ariaCurrent, - className: cx(`${prefix}--link`, children.props.className), - })} -
  • - ); -}); - -BreadcrumbItem.displayName = 'BreadcrumbItem'; - -BreadcrumbItem.propTypes = { - 'aria-current': PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), - - /** - * Pass in content that will be inside of the BreadcrumbItem - */ - children: PropTypes.node, - - /** - * Specify an optional className to be applied to the container node - */ - className: PropTypes.string, - - /** - * Optional string representing the link location for the BreadcrumbItem - */ - href: PropTypes.string, - - /** - * Provide if this breadcrumb item represents the current page - */ - isCurrentPage: PropTypes.bool, -}; - -export default BreadcrumbItem; diff --git a/packages/react/src/components/Breadcrumb/BreadcrumbItem.tsx b/packages/react/src/components/Breadcrumb/BreadcrumbItem.tsx new file mode 100644 index 000000000000..f1207286cb17 --- /dev/null +++ b/packages/react/src/components/Breadcrumb/BreadcrumbItem.tsx @@ -0,0 +1,130 @@ +/** + * Copyright IBM Corp. 2016, 2023 + * + * 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 PropTypes from 'prop-types'; +import React, { AriaAttributes, PropsWithChildren } from 'react'; +import cx from 'classnames'; +import Link from '../Link'; +import { OverflowMenuHorizontal } from '@carbon/icons-react'; +import { usePrefix } from '../../internal/usePrefix'; +import { ForwardRefReturn } from '../../types/common'; +import { Text } from '../Text'; + +export interface BreadcrumbItemProps + extends React.HTMLAttributes { + 'aria-current'?: AriaAttributes['aria-current']; + + /** + * Specify an optional className to be applied to the container node + */ + className?: string; + + /** + * Optional string representing the link location for the BreadcrumbItem + */ + href?: string; + + /** + * Provide if this breadcrumb item represents the current page + */ + isCurrentPage?: boolean; +} + +const BreadcrumbItem: ForwardRefReturn = + React.forwardRef(function BreadcrumbItem( + { + 'aria-current': ariaCurrent, + children, + className: customClassName = '', + href, + isCurrentPage, + ...rest + }: PropsWithChildren, + ref: React.Ref + ) { + const prefix = usePrefix(); + const className = cx({ + [`${prefix}--breadcrumb-item`]: true, + // We set the current class only if `isCurrentPage` is passed in and we do + // not have an `aria-current="page"` set for the breadcrumb item + [`${prefix}--breadcrumb-item--current`]: + isCurrentPage && ariaCurrent !== 'page', + [customClassName]: !!customClassName, + }); + + const child = children as React.FunctionComponentElement; + if ( + child.type && + child.type.displayName !== undefined && + child.type.displayName.includes('OverflowMenu') + ) { + const horizontalOverflowIcon = ( + + ); + return ( +
  • + {React.cloneElement(child, { + menuOptionsClass: `${prefix}--breadcrumb-menu-options`, + menuOffset: { top: 10, left: 59 }, + renderIcon: () => horizontalOverflowIcon, + })} +
  • + ); + } + + if (typeof children === 'string') { + return ( +
  • + {href ? ( + + {children} + + ) : ( + {children} + )} +
  • + ); + } + + return ( +
  • + {React.cloneElement(child, { + 'aria-current': ariaCurrent, + className: cx(`${prefix}--link`, child.props.className), + })} +
  • + ); + }); + +BreadcrumbItem.displayName = 'BreadcrumbItem'; + +BreadcrumbItem.propTypes = { + // @ts-expect-error - v12 TODO: BREAKING: This should match AriaAttributes['aria-current'] + 'aria-current': PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), + + /** + * Pass in content that will be inside of the BreadcrumbItem + */ + children: PropTypes.node, + + /** + * Specify an optional className to be applied to the container node + */ + className: PropTypes.string, + + /** + * Optional string representing the link location for the BreadcrumbItem + */ + href: PropTypes.string, + + /** + * Provide if this breadcrumb item represents the current page + */ + isCurrentPage: PropTypes.bool, +}; + +export default BreadcrumbItem;