diff --git a/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap b/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap index 36c35b84c536..ca1928c973db 100644 --- a/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap +++ b/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap @@ -2725,6 +2725,9 @@ Map { ], "type": "oneOf", }, + "decorator": Object { + "type": "node", + }, "disabled": Object { "type": "bool", }, @@ -2771,9 +2774,7 @@ Map { ], "type": "oneOf", }, - "slug": Object { - "type": "node", - }, + "slug": [Function], "type": Object { "type": "string", }, diff --git a/packages/react/src/components/DatePicker/DatePicker-test.js b/packages/react/src/components/DatePicker/DatePicker-test.js index 2a99afeb8b81..ff2da4cdcd14 100644 --- a/packages/react/src/components/DatePicker/DatePicker-test.js +++ b/packages/react/src/components/DatePicker/DatePicker-test.js @@ -196,7 +196,24 @@ describe('DatePicker', () => { expect(ref).toHaveBeenCalledWith(container.firstChild); }); - it('should respect slug prop', () => { + it('should respect decorator prop', () => { + render( + } + /> + ); + + expect(screen.getByRole('button')).toHaveClass( + `${prefix}--ai-label__button` + ); + }); + + it('should respect deprecated slug prop', () => { + const spy = jest.spyOn(console, 'warn').mockImplementation(() => {}); render( { expect(screen.getByRole('button')).toHaveClass( `${prefix}--ai-label__button` ); + spy.mockRestore(); }); it('should respect parseDate prop', async () => { diff --git a/packages/react/src/components/DatePicker/DatePicker.stories.js b/packages/react/src/components/DatePicker/DatePicker.stories.js index 89088abcb86d..868e2e139546 100644 --- a/packages/react/src/components/DatePicker/DatePicker.stories.js +++ b/packages/react/src/components/DatePicker/DatePicker.stories.js @@ -15,9 +15,10 @@ import DatePickerInput from '../DatePickerInput'; import Button from '../Button'; import { AILabel, AILabelContent, AILabelActions } from '../AILabel'; import { IconButton } from '../IconButton'; -import { View, FolderOpen, Folders } from '@carbon/icons-react'; +import { View, FolderOpen, Folders, Information } from '@carbon/icons-react'; import mdx from './DatePicker.mdx'; +import { Tooltip } from '../Tooltip'; export default { title: 'Components/DatePicker', @@ -165,14 +166,29 @@ const aiLabel = ( ); export const withAILabel = () => ( -
+
+ + + {/* Test decorator with Tooltip TO BE REMOVED!! */} + + + + + } />
diff --git a/packages/react/src/components/DatePickerInput/DatePickerInput.tsx b/packages/react/src/components/DatePickerInput/DatePickerInput.tsx index 2a230263a17b..e1b8a4913536 100644 --- a/packages/react/src/components/DatePickerInput/DatePickerInput.tsx +++ b/packages/react/src/components/DatePickerInput/DatePickerInput.tsx @@ -8,12 +8,13 @@ import { Calendar, WarningFilled, WarningAltFilled } from '@carbon/icons-react'; import cx from 'classnames'; import PropTypes, { ReactElementLike, ReactNodeArray } from 'prop-types'; -import React, { ForwardedRef, useContext } from 'react'; +import React, { ForwardedRef, ReactNode, useContext } from 'react'; import { usePrefix } from '../../internal/usePrefix'; import { FormContext } from '../FluidForm'; import { useId } from '../../internal/useId'; import { ReactAttr } from '../../types/common'; import { Text } from '../Text'; +import deprecate from '../../prop-types/deprecate'; type ExcludedAttributes = 'value' | 'onChange' | 'locale' | 'children'; export type ReactNodeLike = @@ -38,6 +39,11 @@ export interface DatePickerInputProps */ datePickerType?: 'simple' | 'single' | 'range'; + /** + * **Experimental**: Provide a `decorator` component to be rendered inside the `DatePickerInput` component + */ + decorator?: ReactNode; + /** * Specify whether or not the input should be disabled */ @@ -111,6 +117,7 @@ export interface DatePickerInputProps size?: 'sm' | 'md' | 'lg'; /** + * @deprecated please use decorator instead. * **Experimental**: Provide a `Slug` component to be rendered inside the `DatePickerInput` component */ slug?: ReactNodeLike; @@ -137,6 +144,7 @@ const DatePickerInput = React.forwardRef(function DatePickerInput( ) { const { datePickerType, + decorator, disabled = false, helperText, hideLabel, @@ -178,6 +186,7 @@ const DatePickerInput = React.forwardRef(function DatePickerInput( [`${prefix}--date-picker-input__wrapper--invalid`]: invalid, [`${prefix}--date-picker-input__wrapper--warn`]: warn, [`${prefix}--date-picker-input__wrapper--slug`]: slug, + [`${prefix}--date-picker-input__wrapper--decorator`]: decorator, }); const labelClasses = cx(`${prefix}--label`, { [`${prefix}--visually-hidden`]: hideLabel, @@ -215,12 +224,20 @@ const DatePickerInput = React.forwardRef(function DatePickerInput( } const input = ; - // Slug is always size `mini` - let normalizedSlug; - if (slug && slug['type']?.displayName === 'AILabel') { - normalizedSlug = React.cloneElement(slug as React.ReactElement, { - size: 'mini', - }); + // AILabel always size `mini` + let normalizedDecorator = React.isValidElement(slug ?? decorator) + ? (slug ?? decorator) + : null; + if ( + normalizedDecorator && + normalizedDecorator['type']?.displayName === 'AILabel' + ) { + normalizedDecorator = React.cloneElement( + normalizedDecorator as React.ReactElement, + { + size: 'mini', + } + ); } return ( @@ -233,7 +250,16 @@ const DatePickerInput = React.forwardRef(function DatePickerInput(
{input} - {normalizedSlug} + {slug ? ( + normalizedDecorator + ) : decorator ? ( +
+ {normalizedDecorator} +
+ ) : ( + '' + )} {isFluid && } ` diff --git a/packages/react/src/components/Form/Form.stories.js b/packages/react/src/components/Form/Form.stories.js index 45811d5a8782..506a6fe1f75e 100644 --- a/packages/react/src/components/Form/Form.stories.js +++ b/packages/react/src/components/Form/Form.stories.js @@ -313,7 +313,7 @@ export const withAILabel = (args) => { labelText="Date Picker label" size="md" id="date-picker" - slug={aiLabel} + decorator={aiLabel} {...rest} /> diff --git a/packages/styles/scss/components/date-picker/_date-picker.scss b/packages/styles/scss/components/date-picker/_date-picker.scss index b6113ed1cb57..2191b65f3398 100644 --- a/packages/styles/scss/components/date-picker/_date-picker.scss +++ b/packages/styles/scss/components/date-picker/_date-picker.scss @@ -191,6 +191,9 @@ } // Styles for `AILabel` rendered inside `DatePickerInput` + .#{$prefix}--date-picker-input__wrapper--decorator + .#{$prefix}--date-picker-input-inner-wrapper--decorator + > *, .#{$prefix}--date-picker-input__wrapper--slug .#{$prefix}--ai-label, .#{$prefix}--date-picker-input__wrapper--slug .#{$prefix}--slug { position: absolute; @@ -199,6 +202,24 @@ transform: translateY(-50%); } + .#{$prefix}--date-picker-input__wrapper--decorator + .#{$prefix}--date-picker-input-inner-wrapper--decorator:not( + :has(.#{$prefix}--ai-label) + ) + > * { + block-size: 1rem; + } + + .#{$prefix}--date-picker-input__wrapper--decorator + .#{$prefix}--date-picker__input:has( + ~ .#{$prefix}--date-picker-input-inner-wrapper--decorator + .#{$prefix}--ai-label + ):not( + :has( + ~ .#{$prefix}--date-picker-input-inner-wrapper--decorator + .#{$prefix}--ai-label--revert + ) + ), .#{$prefix}--date-picker-input__wrapper--slug .#{$prefix}--date-picker__input:has(~ .#{$prefix}--ai-label):not( :has(~ .#{$prefix}--ai-label--revert) diff --git a/packages/styles/scss/components/number-input/_number-input.scss b/packages/styles/scss/components/number-input/_number-input.scss index 3ae16830f911..98ef203865ba 100644 --- a/packages/styles/scss/components/number-input/_number-input.scss +++ b/packages/styles/scss/components/number-input/_number-input.scss @@ -438,6 +438,14 @@ transform: translateY(-50%); } + .#{$prefix}--number__input-wrapper--decorator + .#{$prefix}--number__input-inner-wrapper--decorator:not( + :has(.#{$prefix}--ai-label) + ) + > * { + block-size: 1rem; + } + .#{$prefix}--number__input-wrapper--decorator .#{$prefix}--number__input-inner-wrapper--decorator > *::before,