Skip to content

Commit

Permalink
Added decorator to DatepickerInput component (#18064)
Browse files Browse the repository at this point in the history
* feat: added decorator prop and it's styles

* fix:  changed datepicker position

* Update DatePickerInput.tsx to fix the format

* fix: fixed test and styles

* Update _number-input.scss

* Update packages/styles/scss/components/date-picker/_date-picker.scss

Co-authored-by: Ariella Gilmore <[email protected]>

* fix: fixed style with revert

* Update packages/react/src/components/DatePickerInput/DatePickerInput.tsx

Co-authored-by: Alison Joseph <[email protected]>

---------

Co-authored-by: Ariella Gilmore <[email protected]>
Co-authored-by: Alison Joseph <[email protected]>
  • Loading branch information
3 people authored Nov 14, 2024
1 parent a71c1b4 commit ff55263
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 20 deletions.
7 changes: 4 additions & 3 deletions packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -2725,6 +2725,9 @@ Map {
],
"type": "oneOf",
},
"decorator": Object {
"type": "node",
},
"disabled": Object {
"type": "bool",
},
Expand Down Expand Up @@ -2771,9 +2774,7 @@ Map {
],
"type": "oneOf",
},
"slug": Object {
"type": "node",
},
"slug": [Function],
"type": Object {
"type": "string",
},
Expand Down
20 changes: 19 additions & 1 deletion packages/react/src/components/DatePicker/DatePicker-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,24 @@ describe('DatePicker', () => {
expect(ref).toHaveBeenCalledWith(container.firstChild);
});

it('should respect slug prop', () => {
it('should respect decorator prop', () => {
render(
<DatePickerInput
id="date-picker-input-id-start"
placeholder="mm/dd/yyyy"
labelText="Date Picker label"
data-testid="input-value"
decorator={<AILabel />}
/>
);

expect(screen.getByRole('button')).toHaveClass(
`${prefix}--ai-label__button`
);
});

it('should respect deprecated slug prop', () => {
const spy = jest.spyOn(console, 'warn').mockImplementation(() => {});
render(
<DatePickerInput
id="date-picker-input-id-start"
Expand All @@ -210,6 +227,7 @@ describe('DatePicker', () => {
expect(screen.getByRole('button')).toHaveClass(
`${prefix}--ai-label__button`
);
spy.mockRestore();
});

it('should respect parseDate prop', async () => {
Expand Down
22 changes: 19 additions & 3 deletions packages/react/src/components/DatePicker/DatePicker.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -165,14 +166,29 @@ const aiLabel = (
);

export const withAILabel = () => (
<div style={{ width: 400 }}>
<div style={{ width: 400, display: 'flex', gap: '1rem' }}>
<DatePicker datePickerType="single">
<DatePickerInput
placeholder="mm/dd/yyyy"
labelText="Date Picker label"
size="md"
id="date-picker"
slug={aiLabel}
decorator={aiLabel}
/>
</DatePicker>

{/* Test decorator with Tooltip TO BE REMOVED!! */}
<DatePicker datePickerType="single">
<DatePickerInput
placeholder="mm/dd/yyyy"
labelText="Date Picker label"
size="md"
id="date-picker"
decorator={
<Tooltip>
<Information />
</Tooltip>
}
/>
</DatePicker>
</div>
Expand Down
55 changes: 43 additions & 12 deletions packages/react/src/components/DatePickerInput/DatePickerInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand All @@ -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
*/
Expand Down Expand Up @@ -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;
Expand All @@ -137,6 +144,7 @@ const DatePickerInput = React.forwardRef(function DatePickerInput(
) {
const {
datePickerType,
decorator,
disabled = false,
helperText,
hideLabel,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -215,12 +224,20 @@ const DatePickerInput = React.forwardRef(function DatePickerInput(
}
const input = <input {...inputProps} />;

// Slug is always size `mini`
let normalizedSlug;
if (slug && slug['type']?.displayName === 'AILabel') {
normalizedSlug = React.cloneElement(slug as React.ReactElement<any>, {
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<any>,
{
size: 'mini',
}
);
}

return (
Expand All @@ -233,7 +250,16 @@ const DatePickerInput = React.forwardRef(function DatePickerInput(
<div className={wrapperClasses}>
<span>
{input}
{normalizedSlug}
{slug ? (
normalizedDecorator
) : decorator ? (
<div
className={`${prefix}--date-picker-input-inner-wrapper--decorator`}>
{normalizedDecorator}
</div>
) : (
''
)}
{isFluid && <DatePickerIcon datePickerType={datePickerType} />}
<DatePickerIcon
datePickerType={datePickerType}
Expand Down Expand Up @@ -280,6 +306,11 @@ DatePickerInput.propTypes = {
*/
datePickerType: PropTypes.oneOf(['simple', 'single', 'range']),

/**
* **Experimental**: Provide a decorator component to be rendered inside the `RadioButton` component
*/
decorator: PropTypes.node,

/**
* Specify whether or not the input should be disabled
*/
Expand Down Expand Up @@ -358,10 +389,10 @@ DatePickerInput.propTypes = {
*/
size: PropTypes.oneOf(['sm', 'md', 'lg']),

/**
* **Experimental**: Provide a `Slug` component to be rendered inside the `DatePickerInput` component
*/
slug: PropTypes.node,
slug: deprecate(
PropTypes.node,
'The `slug` prop has been deprecated and will be removed in the next major version. Use the decorator prop instead.'
),

/**
* Specify the type of the `<input>`
Expand Down
2 changes: 1 addition & 1 deletion packages/react/src/components/Form/Form.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ export const withAILabel = (args) => {
labelText="Date Picker label"
size="md"
id="date-picker"
slug={aiLabel}
decorator={aiLabel}
{...rest}
/>
</DatePicker>
Expand Down
21 changes: 21 additions & 0 deletions packages/styles/scss/components/date-picker/_date-picker.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down

0 comments on commit ff55263

Please sign in to comment.