diff --git a/cspell.json b/cspell.json index f45a580357..71f1680b85 100644 --- a/cspell.json +++ b/cspell.json @@ -99,6 +99,7 @@ "disttags", "dragbar", "draggable", + "interstitialscreenviewmodule", "draghandle", "dragmode", "editinplace", diff --git a/e2e/components/Tearsheet/TearsheetNarrow-test.avt.e2e.js b/e2e/components/Tearsheet/TearsheetNarrow-test.avt.e2e.js index d657f043da..0cc3222f74 100644 --- a/e2e/components/Tearsheet/TearsheetNarrow-test.avt.e2e.js +++ b/e2e/components/Tearsheet/TearsheetNarrow-test.avt.e2e.js @@ -168,7 +168,7 @@ test.describe('TearsheetNarrow @avt', () => { await expect(page).toHaveNoACViolations( 'TearsheetNarrow @avt-all-header-items' ); - await expect(page.locator('.slug-container')).toBeInViewport(); + await expect(page.locator('.ai-label-container')).toBeInViewport(); // Selecting the first slug button const slugButton1 = page diff --git a/packages/ibm-products-styles/src/components/Tearsheet/_tearsheet.scss b/packages/ibm-products-styles/src/components/Tearsheet/_tearsheet.scss index c21a8a16cd..ae573cc8cd 100644 --- a/packages/ibm-products-styles/src/components/Tearsheet/_tearsheet.scss +++ b/packages/ibm-products-styles/src/components/Tearsheet/_tearsheet.scss @@ -110,6 +110,8 @@ $motion-duration: $duration-moderate-02; } &.#{$block-class}.#{$block-class}.#{$block-class}.#{$block-class}--has-slug + .#{$block-class}__container, + &.#{$block-class}.#{$block-class}.#{$block-class}.#{$block-class}--has-ai-label .#{$block-class}__container { border: 1px solid transparent; border-bottom: 0; @@ -276,11 +278,15 @@ $motion-duration: $duration-moderate-02; &.#{$block-class}--wide .#{$block-class}__header.#{$block-class}__header--with-close-icon, - &.#{$block-class}--has-slug .#{$block-class}__header.#{$block-class}__header { + &.#{$block-class}--has-slug .#{$block-class}__header.#{$block-class}__header, + &.#{$block-class}--has-ai-label + .#{$block-class}__header.#{$block-class}__header { padding-inline-end: $spacing-11; } &.#{$block-class}--wide.#{$block-class}--has-slug + .#{$block-class}__header.#{$block-class}__header--with-close-icon, + &.#{$block-class}--wide.#{$block-class}--has-ai-label .#{$block-class}__header.#{$block-class}__header--with-close-icon { /* spacing 11 plus additional space for slug/close */ /* stylelint-disable-next-line carbon/layout-token-use */ @@ -362,7 +368,8 @@ $motion-duration: $duration-moderate-02; flex-grow: 1; } - &.#{$block-class}--has-slug .#{$block-class}__content { + &.#{$block-class}--has-slug .#{$block-class}__content, + &.#{$block-class}--has-ai-label .#{$block-class}__content { @include utilities.ai-popover-gradient('default', 0); box-shadow: inset 0 -80px 70px -65px $ai-inner-shadow; @@ -418,12 +425,15 @@ $motion-duration: $duration-moderate-02; background: $background; } - &.#{$block-class}--has-slug { + &.#{$block-class}--has-slug, + &.#{$block-class}--has-ai-label { /* stylelint-disable-next-line carbon/theme-token-use */ --overlay-color: #{$ai-overlay}; } &.#{$block-class}--has-slug:not(.#{$block-class}--has-close) + .#{$carbon-prefix}--slug, + &.#{$block-class}--has-ai-label:not(.#{$block-class}--has-close) .#{$carbon-prefix}--slug { inset-inline-end: 0; margin-block: 6px; diff --git a/packages/ibm-products/src/components/Tearsheet/Tearsheet.stories.jsx b/packages/ibm-products/src/components/Tearsheet/Tearsheet.stories.jsx index 8f1f5d3942..3d062b3200 100644 --- a/packages/ibm-products/src/components/Tearsheet/Tearsheet.stories.jsx +++ b/packages/ibm-products/src/components/Tearsheet/Tearsheet.stories.jsx @@ -23,8 +23,8 @@ import { TabPanel, TabList, TextInput, - unstable__Slug as Slug, - unstable__SlugContent as SlugContent, + AILabel, + AILabelContent, } from '@carbon/react'; import { Tearsheet, deprecatedProps } from './Tearsheet'; @@ -102,15 +102,29 @@ export default { navigation: { control: { disable: true } }, open: { control: { disable: true } }, portalTarget: { control: { disable: true } }, + aiLabel: { + control: { + type: 'select', + labels: { + 0: 'No AI Label', + 1: 'with AI Label', + }, + default: 0, + }, + description: + 'Optional prop that is intended for any scenario where something is being generated by AI to reinforce AI transparency, accountability, and explainability at the UI level.', + options: [0, 1], + }, slug: { control: { type: 'select', labels: { - 0: 'No AI slug', + 0: 'No AI Slug', 1: 'with AI Slug', }, default: 0, }, + description: 'Deprecated: Property replaced by "aiLabel"', options: [0, 1], }, }, @@ -158,9 +172,9 @@ const mainContent = ( const title = 'Title of the tearsheet'; -const sampleSlug = ( - - +const sampleAILabel = ( + +

AI Explained

84%

@@ -174,13 +188,13 @@ const sampleSlug = (

Model type

Foundation model

-
-
+ + ); // Template. // eslint-disable-next-line react/prop-types -const Template = ({ actions, slug, ...args }) => { +const Template = ({ actions, aiLabel, slug, ...args }) => { const [open, setOpen] = useState(false); const wiredActions = @@ -211,7 +225,8 @@ const Template = ({ actions, slug, ...args }) => { actions={wiredActions} open={open} onClose={() => setOpen(false)} - slug={slug && sampleSlug} + aiLabel={aiLabel && sampleAILabel} + slug={slug && sampleAILabel} > {mainContent} @@ -231,7 +246,7 @@ const tabs = ( ); -const TemplateWithNav = ({ actions, slug, ...args }) => { +const TemplateWithNav = ({ actions, aiLabel, slug, ...args }) => { const [open, setOpen] = useState(false); const wiredActions = @@ -263,7 +278,8 @@ const TemplateWithNav = ({ actions, slug, ...args }) => { actions={wiredActions} open={open} onClose={() => setOpen(false)} - slug={slug && sampleSlug} + aiLabel={aiLabel && sampleAILabel} + slug={slug && sampleAILabel} > Tab 1 @@ -278,7 +294,7 @@ const TemplateWithNav = ({ actions, slug, ...args }) => { ); }; -const ReturnFocusTemplate = ({ actions, slug, ...args }) => { +const ReturnFocusTemplate = ({ actions, aiLabel, slug, ...args }) => { const [open, setOpen] = useState(false); const buttonRef = useRef(undefined); @@ -312,7 +328,8 @@ const ReturnFocusTemplate = ({ actions, slug, ...args }) => { actions={wiredActions} open={open} onClose={() => setOpen(false)} - slug={slug && sampleSlug} + aiLabel={aiLabel && sampleAILabel} + slug={slug && sampleAILabel} launcherButtonRef={buttonRef} > {mainContent} @@ -322,9 +339,8 @@ const ReturnFocusTemplate = ({ actions, slug, ...args }) => { ); }; -const FirstElementDisabledTemplate = ({ actions, slug, ...args }) => { +const FirstElementDisabledTemplate = ({ actions, aiLabel, slug, ...args }) => { const [open, setOpen] = useState(false); - const wiredActions = actions && Array.prototype.map.call(actions, (action) => { @@ -342,7 +358,6 @@ const FirstElementDisabledTemplate = ({ actions, slug, ...args }) => { }); const ref = useRef(undefined); - return ( <> @@ -353,7 +368,8 @@ const FirstElementDisabledTemplate = ({ actions, slug, ...args }) => { actions={wiredActions} open={open} onClose={() => setOpen(false)} - slug={slug && sampleSlug} + aiLabel={aiLabel && sampleAILabel} + slug={slug && sampleAILabel} >
@@ -389,7 +405,7 @@ const FirstElementDisabledTemplate = ({ actions, slug, ...args }) => { }; // eslint-disable-next-line react/prop-types -const StackedTemplate = ({ mixedSizes, actions, slug, ...args }) => { +const StackedTemplate = ({ mixedSizes, actions, aiLabel, slug, ...args }) => { const [open1, setOpen1] = useState(false); const [open2, setOpen2] = useState(false); const [open3, setOpen3] = useState(false); @@ -485,7 +501,8 @@ const StackedTemplate = ({ mixedSizes, actions, slug, ...args }) => { open={open1} onClose={() => setOpen1(false)} selectorPrimaryFocus="#stacked-input-1" - slug={slug && sampleSlug} + aiLabel={aiLabel && sampleAILabel} + slug={slug && sampleAILabel} >
Main content 1 @@ -515,7 +532,8 @@ const StackedTemplate = ({ mixedSizes, actions, slug, ...args }) => { open={open2} onClose={() => setOpen2(false)} selectorPrimaryFocus="#stacked-input-2" - slug={slug && sampleSlug} + aiLabel={aiLabel && sampleAILabel} + slug={slug && sampleAILabel} >
Main content 2 @@ -533,7 +551,8 @@ const StackedTemplate = ({ mixedSizes, actions, slug, ...args }) => { open={open3} onClose={() => setOpen3(false)} selectorPrimaryFocus="#stacked-input-3" - slug={slug && sampleSlug} + aiLabel={aiLabel && sampleAILabel} + slug={slug && sampleAILabel} >
Main content 3 @@ -622,7 +641,8 @@ fullyLoaded.args = { onClose: action('onClose called'), title, actions: 0, - slug: 1, + aiLabel: 1, + slug: 0, }; // eslint-disable-next-line react/prop-types diff --git a/packages/ibm-products/src/components/Tearsheet/TearsheetNarrow.stories.jsx b/packages/ibm-products/src/components/Tearsheet/TearsheetNarrow.stories.jsx index e97fd4ed3c..0bd9f849b7 100644 --- a/packages/ibm-products/src/components/Tearsheet/TearsheetNarrow.stories.jsx +++ b/packages/ibm-products/src/components/Tearsheet/TearsheetNarrow.stories.jsx @@ -16,8 +16,8 @@ import { Form, FormGroup, TextInput, - unstable__Slug as Slug, - unstable__SlugContent as SlugContent, + AILabel, + AILabelContent, } from '@carbon/react'; import { TearsheetNarrow, deprecatedProps } from './TearsheetNarrow'; @@ -59,15 +59,29 @@ export default { onClose: { control: { disable: true } }, open: { control: { disable: true } }, portalTarget: { control: { disable: true } }, + aiLabel: { + control: { + type: 'select', + labels: { + 0: 'No AI Label', + 1: 'with AI Label', + }, + default: 0, + }, + description: + 'Optional prop that is intended for any scenario where something is being generated by AI to reinforce AI transparency, accountability, and explainability at the UI level.', + options: [0, 1], + }, slug: { control: { type: 'select', labels: { - 0: 'No AI slug', + 0: 'No AI Slug', 1: 'with AI Slug', }, default: 0, }, + description: 'deprecated Property replaced by "aiLabel"', options: [0, 1], }, }, @@ -99,9 +113,9 @@ const mainContent = ( const title = 'Title of the tearsheet'; -const sampleSlug = ( - - +const sampleAILabel = ( + +

AI Explained

84%

@@ -115,13 +129,13 @@ const sampleSlug = (

Model type

Foundation model

-
-
+ + ); // Template. // eslint-disable-next-line react/prop-types -const Template = ({ actions, slug, ...args }) => { +const Template = ({ actions, aiLabel, slug, ...args }) => { const [open, setOpen] = useState(false); const ref = useRef(undefined); @@ -149,7 +163,8 @@ const Template = ({ actions, slug, ...args }) => { actions={wiredActions} open={open} onClose={() => setOpen(false)} - slug={slug && sampleSlug} + aiLabel={aiLabel && sampleAILabel} + slug={slug && sampleAILabel} > {mainContent} @@ -159,7 +174,7 @@ const Template = ({ actions, slug, ...args }) => { }; // eslint-disable-next-line react/prop-types -const StackedTemplate = ({ actions, slug, ...args }) => { +const StackedTemplate = ({ actions, aiLabel, slug, ...args }) => { const [open1, setOpen1] = useState(false); const [open2, setOpen2] = useState(false); const [open3, setOpen3] = useState(false); @@ -231,7 +246,8 @@ const StackedTemplate = ({ actions, slug, ...args }) => { title="Tearsheet #1" open={open1} onClose={() => setOpen1(false)} - slug={slug && sampleSlug} + aiLabel={aiLabel && sampleAILabel} + slug={slug && sampleAILabel} >
Main content 1 @@ -243,7 +259,8 @@ const StackedTemplate = ({ actions, slug, ...args }) => { title="Tearsheet #2" open={open2} onClose={() => setOpen2(false)} - slug={slug && sampleSlug} + aiLabel={aiLabel && sampleAILabel} + slug={slug && sampleAILabel} selectorPrimaryFocus="#main-content" >
@@ -256,7 +273,8 @@ const StackedTemplate = ({ actions, slug, ...args }) => { title="Tearsheet #3" open={open3} onClose={() => setOpen3(false)} - slug={slug && sampleSlug} + aiLabel={aiLabel && sampleAILabel} + slug={slug && sampleAILabel} selectorPrimaryFocus="#main-content" >
@@ -289,7 +307,8 @@ fullyLoaded.args = { onClose: action('onClose called'), title, actions: 0, - slug: 1, + aiLabel: 1, + slug: 0, }; export const stacked = StackedTemplate.bind({}); diff --git a/packages/ibm-products/src/components/Tearsheet/TearsheetShell.story.jsx b/packages/ibm-products/src/components/Tearsheet/TearsheetShell.story.jsx index 1e69672f47..df49a38b10 100644 --- a/packages/ibm-products/src/components/Tearsheet/TearsheetShell.story.jsx +++ b/packages/ibm-products/src/components/Tearsheet/TearsheetShell.story.jsx @@ -12,12 +12,7 @@ import { createPortal } from 'react-dom'; import styles from './_storybook-styles.scss?inline'; import { TearsheetShell, deprecatedProps } from './TearsheetShell'; import { getDeprecatedArgTypes } from '../../global/js/utils/props-helper'; -import { - Button, - unstable__Slug as Slug, - unstable__SlugContent as SlugContent, - TextInput, -} from '@carbon/react'; +import { Button, AILabel, AILabelContent, TextInput } from '@carbon/react'; // import mdx from './TearsheetShell.mdx'; @@ -37,12 +32,12 @@ export default { }, }, portalTarget: { control: { disable: true } }, - slug: { + aiLabel: { control: { type: 'select', labels: { - 0: 'No AI slug', - 1: 'with AI Slug', + 0: 'No AI Label', + 1: 'with AI Label', }, default: 0, }, @@ -76,9 +71,9 @@ const dummyContent = (
); -const sampleSlug = ( - - +const sampleAILabel = ( + +

AI Explained

84%

@@ -92,12 +87,12 @@ const sampleSlug = (

Model type

Foundation model

-
-
+ + ); // Template. -const Template = ({ influencer, open: _open, slug, ...args }, context) => { +const Template = ({ influencer, open: _open, aiLabel, ...args }, context) => { const ref = useRef(undefined); const [open, setOpen] = useState(context.viewMode !== 'docs' && _open); const [beenOpen, setBeenOpen] = useState(false); @@ -120,7 +115,7 @@ const Template = ({ influencer, open: _open, slug, ...args }, context) => { } open={open} onClose={() => setOpen(false)} - slug={slug && sampleSlug} + aiLabel={aiLabel && sampleAILabel} title={'Tearsheet title'} > {dummyContent} @@ -130,7 +125,7 @@ const Template = ({ influencer, open: _open, slug, ...args }, context) => { }; const ReturnFocusTemplate = ( - { influencer, open: _open, slug, ...args }, + { influencer, open: _open, aiLabel, ...args }, context ) => { const ref = useRef(undefined); @@ -156,7 +151,7 @@ const ReturnFocusTemplate = ( } open={open} onClose={() => setOpen(false)} - slug={slug && sampleSlug} + aiLabel={aiLabel && sampleAILabel} title={'Tearsheet title'} launcherButtonRef={buttonRef} > diff --git a/packages/ibm-products/src/components/Tearsheet/TearsheetShell.tsx b/packages/ibm-products/src/components/Tearsheet/TearsheetShell.tsx index f861942d7d..95977eed46 100644 --- a/packages/ibm-products/src/components/Tearsheet/TearsheetShell.tsx +++ b/packages/ibm-products/src/components/Tearsheet/TearsheetShell.tsx @@ -25,7 +25,7 @@ import cx from 'classnames'; import { pkg } from '../../settings'; import pconsole from '../../global/js/utils/pconsole'; import { getNodeTextContent } from '../../global/js/utils/getNodeTextContent'; - +import { deprecateProp } from '../../global/js/utils/props-helper'; // Carbon and package components we use. import { Button, @@ -52,6 +52,12 @@ const maxDepth = 3; interface TearsheetShellProps extends PropsWithChildren { actions?: ButtonProps<'button'>[]; + /** + * Optional prop that is intended for any scenario where something is being generated by AI to reinforce AI transparency, + * accountability, and explainability at the UI level. + */ + aiLabel?: ReactNode; + ariaLabel?: string; /** @@ -155,17 +161,18 @@ interface TearsheetShellProps extends PropsWithChildren { */ size: 'narrow' | 'wide'; - /** - * **Experimental:** Provide a `Slug` component to be rendered inside the `Tearsheet` component - */ - slug?: ReactNode; - /** * The main title of the tearsheet, displayed in the header area. */ title?: ReactNode; verticalPosition?: 'normal' | 'lower'; + + // Deprecated props + /** + * @deprecated Property replaced by `aiLabel` + */ + slug?: ReactNode; } export type CloseIconDescriptionTypes = @@ -230,6 +237,7 @@ export const TearsheetShell = React.forwardRef( { // The component props, in alphabetical order (for consistency). actions, + aiLabel, ariaLabel, children, className, @@ -248,7 +256,7 @@ export const TearsheetShell = React.forwardRef( selectorPrimaryFocus, selectorsFloatingMenus = [], size, - slug, + slug: deprecated_slug, title, verticalPosition, launcherButtonRef, @@ -465,7 +473,6 @@ export const TearsheetShell = React.forwardRef( [`--${bc}--stacking-scale-factor-double`]: (width - 64) / width, }; }; - return renderPortalUse( 1 || (depth === 1 && (prevDepth?.current ?? 0) > 1), [`${bc}--wide`]: wide, [`${bc}--narrow`]: !wide, - [`${bc}--has-slug`]: slug, + [`${bc}--has-slug`]: deprecated_slug, + [`${bc}--has-ai-label`]: aiLabel, [`${bc}--has-close`]: effectiveHasCloseIcon, })} - slug={slug} + slug={aiLabel || deprecated_slug} style={setScaleValues()} containerClassName={cx(`${bc}__container`, { [`${bc}__container--lower`]: verticalPosition === 'lower', @@ -622,6 +630,11 @@ export const portalType = PropTypes.instanceOf(Element); export const deprecatedProps = { + /** + * @deprecated Property replaced by `aiLabel` + */ + slug: deprecateProp(PropTypes.node, 'Property replaced by `aiLabel`'), + /** * **Deprecated** * @@ -676,6 +689,12 @@ TearsheetShell.propTypes = { }) ), + /** + * Optional prop that is intended for any scenario where something is being generated by AI to reinforce AI transparency, + * accountability, and explainability at the UI level. + */ + aiLabel: PropTypes.oneOfType([PropTypes.node, PropTypes.bool]), + /** * The main content of the tearsheet. */ @@ -797,16 +816,9 @@ TearsheetShell.propTypes = { */ /**@ts-ignore*/ size: PropTypes.oneOf(['narrow', 'wide']).isRequired, - - /** - * **Experimental:** Provide a `Slug` component to be rendered inside the `Tearsheet` component - */ - slug: PropTypes.node, - /** * The main title of the tearsheet, displayed in the header area. */ title: PropTypes.node, - ...deprecatedProps, };