From cfba9567dd22f34a84c773e5baaa9d7322836685 Mon Sep 17 00:00:00 2001 From: Ariella Gilmore Date: Wed, 11 Sep 2024 09:57:23 -0600 Subject: [PATCH] feat(ai-label): add ts types (#17295) * fix(ai-label): new tsx extension * fix(ai-label): add types * fix(snapshots): update * fix(prop-mapping): use mapPopoverAlignProp * fix(popover): restructure * fix(popover): leave as is --------- Co-authored-by: Nikhil Tomar <63502271+2nikhiltom@users.noreply.github.com> --- .../__snapshots__/PublicAPI-test.js.snap | 56 +-- .../react/src/components/AILabel/index.js | 274 -------------- .../react/src/components/AILabel/index.tsx | 356 ++++++++++++++++++ 3 files changed, 358 insertions(+), 328 deletions(-) delete mode 100644 packages/react/src/components/AILabel/index.js create mode 100644 packages/react/src/components/AILabel/index.tsx diff --git a/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap b/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap index f2be17fd003e..ef8a9eb4f750 100644 --- a/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap +++ b/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap @@ -12,33 +12,7 @@ Map { "type": "string", }, "aiTextLabel": [Function], - "align": Object { - "args": Array [ - Array [ - "top", - "top-left", - "top-start", - "top-right", - "top-end", - "bottom", - "bottom-left", - "bottom-start", - "bottom-right", - "bottom-end", - "left", - "left-bottom", - "left-end", - "left-top", - "left-start", - "right", - "right-bottom", - "right-end", - "right-top", - "right-start", - ], - ], - "type": "oneOf", - }, + "align": [Function], "aria-label": Object { "type": "string", }, @@ -10952,33 +10926,7 @@ Map { "type": "string", }, "aiTextLabel": [Function], - "align": Object { - "args": Array [ - Array [ - "top", - "top-left", - "top-start", - "top-right", - "top-end", - "bottom", - "bottom-left", - "bottom-start", - "bottom-right", - "bottom-end", - "left", - "left-bottom", - "left-end", - "left-top", - "left-start", - "right", - "right-bottom", - "right-end", - "right-top", - "right-start", - ], - ], - "type": "oneOf", - }, + "align": [Function], "aria-label": Object { "type": "string", }, diff --git a/packages/react/src/components/AILabel/index.js b/packages/react/src/components/AILabel/index.js deleted file mode 100644 index 0ef644a03c3e..000000000000 --- a/packages/react/src/components/AILabel/index.js +++ /dev/null @@ -1,274 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2024 - * - * This source code is licensed under the Apache-2.0 license found in the - * LICENSE file in the root directory of this source tree. - */ - -import cx from 'classnames'; -import PropTypes from 'prop-types'; -import React from 'react'; - -import { usePrefix } from '../../internal/usePrefix'; -import { - Toggletip, - ToggletipButton, - ToggletipContent, - ToggletipActions, -} from '../Toggletip'; -import { IconButton } from '../IconButton'; -import { Undo } from '@carbon/icons-react'; -import { useId } from '../../internal/useId'; -import deprecate from '../../prop-types/deprecate'; - -export const AILabelContent = React.forwardRef(function AILabelContent( - { children, className }, - ref -) { - const prefix = usePrefix(); - - const hasAILabelActions = React.Children.toArray(children).some( - (child) => child.type?.displayName === 'AILabelActions' - ); - - const aiLabelContentClasses = cx(className, { - [`${prefix}--slug-content`]: true, - [`${prefix}--slug-content--with-actions`]: hasAILabelActions, - }); - - return ( - - {children} - - ); -}); - -AILabelContent.displayName = 'AILabelContent'; -AILabelContent.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 AILabelActions = React.forwardRef(function AILabelActions( - { children, className }, - ref -) { - const prefix = usePrefix(); - - const aiLabelActionsClasses = cx(className, { - [`${prefix}--slug-actions`]: true, - }); - - return ( - - {children} - - ); -}); - -AILabelActions.displayName = 'AILabelActions'; -AILabelActions.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 AILabel = React.forwardRef(function AILabel( - { - aiText = 'AI', - aiTextLabel, - textLabel, - align, - autoAlign = true, - children, - className, - kind = 'default', - onRevertClick, - revertActive, - revertLabel = 'Revert to AI input', - slugLabel = 'Show information', - ['aria-label']: ariaLabel = 'Show information', - size = 'xs', - ...rest - }, - ref -) { - const prefix = usePrefix(); - const id = useId('AILabel'); - - const aiLabelClasses = cx(className, { - [`${prefix}--slug`]: true, - [`${prefix}--slug--revert`]: revertActive, - }); - - const aiLabelButtonClasses = cx({ - [`${prefix}--slug__button`]: true, - [`${prefix}--slug__button--${size}`]: size, - [`${prefix}--slug__button--${kind}`]: kind, - [`${prefix}--slug__button--inline-with-content`]: - kind === 'inline' && (aiTextLabel || textLabel), - }); - - const handleOnRevertClick = (evt) => { - if (onRevertClick) { - onRevertClick(evt); - } - }; - - const ariaLabelText = - !aiTextLabel && !textLabel - ? `${aiText} - ${slugLabel || ariaLabel}` - : `${aiText} - ${aiTextLabel || textLabel}`; - - return ( -
- {revertActive ? ( - - - - ) : ( - - - {aiText} - {kind === 'inline' && (aiTextLabel || textLabel) && ( - - {aiTextLabel || textLabel} - - )} - - {children} - - )} -
- ); -}); - -AILabel.displayName = 'AILabel'; -AILabel.propTypes = { - /** - * Specify the content you want rendered inside the `AILabel` ToggleTip - */ - AILabelContent: PropTypes.node, - - /** - * Specify the correct translation of the AI text - */ - aiText: PropTypes.string, - - /** - * @deprecated - * Specify additional text to be rendered next to the AI label in the inline variant - */ - aiTextLabel: deprecate( - PropTypes.string, - '`aiTextLabel` on `AILabel` has been deprecated - Please use the `textLabel` prop instead' - ), - - /** - * Specify how the popover should align with the button - */ - align: PropTypes.oneOf([ - 'top', - 'top-left', // deprecated use top-start instead - 'top-start', - 'top-right', // deprecated use top-end instead - 'top-end', - - 'bottom', - 'bottom-left', // deprecated use bottom-start instead - 'bottom-start', - 'bottom-right', // deprecated use bottom-end instead - 'bottom-end', - - 'left', - 'left-bottom', // deprecated use left-end instead - 'left-end', - 'left-top', // deprecated use left-start instead - 'left-start', - - 'right', - 'right-bottom', // deprecated use right-end instead - 'right-end', - 'right-top', // deprecated use right-start instead - 'right-start', - ]), - - /** - * Specify the text that will be provided to the aria-label of the `AILabel` button - */ - 'aria-label': PropTypes.string, - - /** - * Will auto-align the popover. This prop is currently experimental and is subject to future changes. - */ - autoAlign: PropTypes.bool, - - /** - * Specify the content you want rendered inside the `AILabel` ToggleTip - */ - children: PropTypes.node, - - /** - * Specify an optional className to be added to the `AILabel` - */ - className: PropTypes.string, - - /** - * Specify the type of `AILabel`, from the following list of types: - */ - kind: PropTypes.oneOf(['default', 'inline']), - - /** - * Callback function that fires when the revert button is clicked - */ - onRevertClick: PropTypes.func, - - /** - * Specify whether the revert button should be visible - */ - revertActive: PropTypes.bool, - - /** - * Specify the text that should be shown when the revert button is hovered - */ - revertLabel: PropTypes.string, - - /** - * Specify the size of the button, from the following list of sizes: - */ - size: PropTypes.oneOf(['mini', '2xs', 'xs', 'sm', 'md', 'lg', 'xl']), - - /** - * @deprecated - * Specify the text that will be provided to the aria-label of the `AILabel` button - */ - slugLabel: deprecate( - PropTypes.string, - '`slugLabel` on `AILabel` has been deprecated - Please use the `ariaLabel` prop instead' - ), - - /** - * Specify additional text to be rendered next to the AI label in the inline variant - */ - textLabel: PropTypes.string, -}; diff --git a/packages/react/src/components/AILabel/index.tsx b/packages/react/src/components/AILabel/index.tsx new file mode 100644 index 000000000000..82d16a8e854a --- /dev/null +++ b/packages/react/src/components/AILabel/index.tsx @@ -0,0 +1,356 @@ +/** + * Copyright IBM Corp. 2016, 2024 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +import cx from 'classnames'; +import PropTypes from 'prop-types'; +import deprecateValuesWithin from '../../prop-types/deprecateValuesWithin'; +import React from 'react'; + +import { usePrefix } from '../../internal/usePrefix'; +import { + Toggletip, + ToggletipButton, + ToggletipContent, + ToggletipActions, +} from '../Toggletip'; +import { IconButton } from '../IconButton'; +import { mapPopoverAlignProp } from '../../tools/createPropAdapter'; +import { Undo } from '@carbon/icons-react'; +import { useId } from '../../internal/useId'; +import deprecate from '../../prop-types/deprecate'; + +export type AILabelContentProps = React.HTMLAttributes; + +export const AILabelContent = React.forwardRef(function AILabelContent( + { className, children, ...rest }: AILabelContentProps, + ref +) { + const prefix = usePrefix(); + + const hasAILabelActions = React.Children.toArray(children).some((child) => { + const item = child as any; + item.type?.displayName === 'AILabelActions'; + }); + + const aiLabelContentClasses = cx(className, { + [`${prefix}--slug-content`]: true, + [`${prefix}--slug-content--with-actions`]: hasAILabelActions, + }); + + return ( + + {children} + + ); +}); + +AILabelContent.displayName = 'AILabelContent'; +AILabelContent.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 type AILabelActionsProps = React.HTMLAttributes; + +export const AILabelActions = React.forwardRef(function AILabelActions( + { className, children, ...rest }: AILabelActionsProps, + ref +) { + const prefix = usePrefix(); + + const aiLabelActionsClasses = cx(className, { + [`${prefix}--slug-actions`]: true, + }); + + return ( + + {children} + + ); +}); + +AILabelActions.displayName = 'AILabelActions'; +AILabelActions.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, +}; + +/** + * Deprecated popover alignment values. + * @deprecated Use NewPopoverAlignment instead. + */ +export type DeprecatedAlignment = + | 'top-left' + | 'top-right' + | 'bottom-left' + | 'bottom-right' + | 'left-bottom' + | 'left-top' + | 'right-bottom' + | 'right-top'; + +export type NewAlignment = + | 'top' + | 'bottom' + | 'left' + | 'right' + | 'top-start' + | 'top-end' + | 'bottom-start' + | 'bottom-end' + | 'left-end' + | 'left-start' + | 'right-end' + | 'right-start'; + +export type Alignment = DeprecatedAlignment | NewAlignment; + +const propMappingFunction = (deprecatedValue) => { + return mapPopoverAlignProp(deprecatedValue); +}; + +interface AILabelProps { + AILabelContent?: React.ReactNode; + aiText?: string; + aiTextLabel?: string; + textLabel?: string; + align?: Alignment; + autoAlign?: boolean; + children?: React.ReactNode; + className?: string; + kind?: 'default' | 'inline'; + onRevertClick?: (evt: React.MouseEvent) => void; + revertActive?: boolean; + revertLabel?: string; + size?: 'mini' | '2xs' | 'xs' | 'sm' | 'md' | 'lg' | 'xl'; + 'aria-label'?: string; + slugLabel?: string; +} + +export const AILabel = React.forwardRef( + function AILabel( + { + aiText = 'AI', + aiTextLabel, + textLabel, + align, + autoAlign = true, + children, + className, + kind = 'default', + onRevertClick, + revertActive, + revertLabel = 'Revert to AI input', + slugLabel = 'Show information', + ['aria-label']: ariaLabel = 'Show information', + size = 'xs', + ...rest + }, + ref + ) { + const prefix = usePrefix(); + const id = useId('AILabel'); + + const aiLabelClasses = cx(className, { + [`${prefix}--slug`]: true, + [`${prefix}--slug--revert`]: revertActive, + }); + + const aiLabelButtonClasses = cx({ + [`${prefix}--slug__button`]: true, + [`${prefix}--slug__button--${size}`]: size, + [`${prefix}--slug__button--${kind}`]: kind, + [`${prefix}--slug__button--inline-with-content`]: + kind === 'inline' && (aiTextLabel || textLabel), + }); + + const handleOnRevertClick = (evt) => { + if (onRevertClick) { + onRevertClick(evt); + } + }; + + const ariaLabelText = + !aiTextLabel && !textLabel + ? `${aiText} - ${slugLabel || ariaLabel}` + : `${aiText} - ${aiTextLabel || textLabel}`; + + return ( +
+ {revertActive ? ( + + + + ) : ( + + + {aiText} + {kind === 'inline' && (aiTextLabel || textLabel) && ( + + {aiTextLabel || textLabel} + + )} + + {children} + + )} +
+ ); + } +); + +AILabel.displayName = 'AILabel'; +AILabel.propTypes = { + /** + * Specify the content you want rendered inside the `AILabel` ToggleTip + */ + AILabelContent: PropTypes.node, + + /** + * Specify the correct translation of the AI text + */ + aiText: PropTypes.string, + + /** + * @deprecated + * Specify additional text to be rendered next to the AI label in the inline variant + */ + aiTextLabel: deprecate( + PropTypes.string, + '`aiTextLabel` on `AILabel` has been deprecated - Please use the `textLabel` prop instead' + ), + + /** + * Specify how the popover should align with the button + */ + align: deprecateValuesWithin( + PropTypes.oneOf([ + 'top', + 'top-left', // deprecated use top-start instead + 'top-right', // deprecated use top-end instead + + 'bottom', + 'bottom-left', // deprecated use bottom-start instead + 'bottom-right', // deprecated use bottom-end instead + + 'left', + 'left-bottom', // deprecated use left-end instead + 'left-top', // deprecated use left-start instead + + 'right', + 'right-bottom', // deprecated use right-end instead + 'right-top', // deprecated use right-start instead + + // new values to match floating-ui + 'top-start', + 'top-end', + 'bottom-start', + 'bottom-end', + 'left-end', + 'left-start', + 'right-end', + 'right-start', + ]), + //allowed prop values + [ + 'top', + 'top-start', + 'top-end', + 'bottom', + 'bottom-start', + 'bottom-end', + 'left', + 'left-start', + 'left-end', + 'right', + 'right-start', + 'right-end', + ], + //optional mapper function + propMappingFunction + ), + + /** + * Specify the text that will be provided to the aria-label of the `AILabel` button + */ + 'aria-label': PropTypes.string, + + /** + * Will auto-align the popover. This prop is currently experimental and is subject to future changes. + */ + autoAlign: PropTypes.bool, + + /** + * Specify the content you want rendered inside the `AILabel` ToggleTip + */ + children: PropTypes.node, + + /** + * Specify an optional className to be added to the `AILabel` + */ + className: PropTypes.string, + + /** + * Specify the type of `AILabel`, from the following list of types: + */ + kind: PropTypes.oneOf(['default', 'inline']), + + /** + * Callback function that fires when the revert button is clicked + */ + onRevertClick: PropTypes.func, + + /** + * Specify whether the revert button should be visible + */ + revertActive: PropTypes.bool, + + /** + * Specify the text that should be shown when the revert button is hovered + */ + revertLabel: PropTypes.string, + + /** + * Specify the size of the button, from the following list of sizes: + */ + size: PropTypes.oneOf(['mini', '2xs', 'xs', 'sm', 'md', 'lg', 'xl']), + + /** + * @deprecated + * Specify the text that will be provided to the aria-label of the `AILabel` button + */ + slugLabel: deprecate( + PropTypes.string, + '`slugLabel` on `AILabel` has been deprecated - Please use the `ariaLabel` prop instead' + ), + + /** + * Specify additional text to be rendered next to the AI label in the inline variant + */ + textLabel: PropTypes.string, +};