Skip to content

Commit

Permalink
feat(select): add new decorator prop (#18107)
Browse files Browse the repository at this point in the history
* feat(combobox): replace slug prop with decorator

* chore: update tests

* feat: add decorator prop to multiselect

* feat: add decorator prop to filterablemultiselect

* feat: add decorator prop to select

* feat: add styles

* fix: fix revert styles with error and warning

* chore: revert unrelated changes

* chore: update form story

* chore: revert changes

* chore: revert changes

* chore: format

* fix: styles for revert

* chore: fluid styles

* fix: fluid ailabel styles

* chore: format

* fix: styles

* chore: remove test story

* fix: fluid select css selector
  • Loading branch information
alisonjoseph authored Nov 21, 2024
1 parent 5da347d commit ab1861d
Show file tree
Hide file tree
Showing 8 changed files with 184 additions and 32 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 @@ -6831,6 +6831,9 @@ Map {
"className": Object {
"type": "string",
},
"decorator": Object {
"type": "node",
},
"defaultValue": Object {
"type": "any",
},
Expand Down Expand Up @@ -6879,9 +6882,7 @@ Map {
],
"type": "oneOf",
},
"slug": Object {
"type": "node",
},
"slug": [Function],
"warn": Object {
"type": "bool",
},
Expand Down
51 changes: 50 additions & 1 deletion packages/react/src/components/FluidSelect/FluidSelect.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ import {
ToggletipButton,
ToggletipContent,
} from '../Toggletip';
import { Information } from '@carbon/icons-react';
import Button from '../Button';
import { AILabel, AILabelContent, AILabelActions } from '../AILabel';
import { IconButton } from '../IconButton';
import { Information, View, FolderOpen, Folders } from '@carbon/icons-react';
import mdx from './FluidSelect.mdx';

export default {
Expand Down Expand Up @@ -113,6 +116,52 @@ export const Default = () => (
</div>
);

const aiLabel = (
<AILabel className="ai-label-container">
<AILabelContent>
<div>
<p className="secondary">AI Explained</p>
<h1>84%</h1>
<p className="secondary bold">Confidence score</p>
<p className="secondary">
Lorem ipsum dolor sit amet, di os consectetur adipiscing elit, sed do
eiusmod tempor incididunt ut fsil labore et dolore magna aliqua.
</p>
<hr />
<p className="secondary">Model type</p>
<p className="bold">Foundation model</p>
</div>
<AILabelActions>
<IconButton kind="ghost" label="View">
<View />
</IconButton>
<IconButton kind="ghost" label="Open Folder">
<FolderOpen />
</IconButton>
<IconButton kind="ghost" label="Folders">
<Folders />
</IconButton>
<Button>View details</Button>
</AILabelActions>
</AILabelContent>
</AILabel>
);

export const withAILabel = () => (
<div style={{ width: 400 }}>
<FluidSelect id="select-1" labelText="Select an option" decorator={aiLabel}>
<SelectItem value="" text="" />
<SelectItem
value="An example option that is really long to show what should be done to handle long text"
text="An example option that is really long to show what should be done to handle long text"
/>
<SelectItem value="Option 2" text="Option 2" />
<SelectItem value="Option 3" text="Option 3" />
<SelectItem value="Option 4" text="Option 4" />
</FluidSelect>
</div>
);

export const Playground = (args) => (
<div style={{ width: args.playgroundWidth }}>
<FluidSelect {...args} id="select-1">
Expand Down
4 changes: 2 additions & 2 deletions packages/react/src/components/Form/Form.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ export const withAILabel = (args) => {
id="select-1"
labelText="Select an option"
helperText="Optional helper text"
slug={aiLabel}
decorator={aiLabel}
{...rest}>
<SelectItem value="" text="" />
<SelectItem
Expand Down Expand Up @@ -476,7 +476,7 @@ export const withAILabel = (args) => {
/>
</div>
<div style={{ display: 'flex' }}>
<FluidSelect slug={aiLabel} {...rest} id="select-2">
<FluidSelect decorator={aiLabel} {...rest} id="select-2">
<SelectItem value="" text="" />
<SelectItem value="option-1" text="Option 1" />
<SelectItem value="option-2" text="Option 2" />
Expand Down
5 changes: 3 additions & 2 deletions packages/react/src/components/Select/Select.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { AILabel, AILabelContent, AILabelActions } from '../AILabel';
import { IconButton } from '../IconButton';
import { View, FolderOpen, Folders } from '@carbon/icons-react';
import mdx from './Select.mdx';
import { Tooltip } from '../Tooltip';

export default {
title: 'Components/Select',
Expand Down Expand Up @@ -166,12 +167,12 @@ const aiLabel = (
);

export const withAILabel = () => (
<div style={{ width: 400 }}>
<div>
<Select
id="select-1"
labelText="Select an option"
helperText="Optional helper text"
slug={aiLabel}>
decorator={aiLabel}>
<SelectItem value="" text="" />
<SelectItem
value="An example option that is really long to show what should be done to handle long text"
Expand Down
52 changes: 40 additions & 12 deletions packages/react/src/components/Select/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ export interface SelectProps
*/
className?: string;

/**
* **Experimental**: Provide a `decorator` component to be rendered inside the `Select` component
*/
decorator?: ReactNode;

/**
* Optionally provide the default value of the `<select>`
*/
Expand Down Expand Up @@ -118,6 +123,7 @@ export interface SelectProps
size?: 'sm' | 'md' | 'lg';

/**
* @deprecated please use decorator instead.
* **Experimental**: Provide a `Slug` component to be rendered inside the `Dropdown` component
*/
slug?: ReactNode;
Expand All @@ -136,6 +142,7 @@ export interface SelectProps
const Select = React.forwardRef(function Select(
{
className,
decorator,
id,
inline = false,
labelText = 'Select',
Expand Down Expand Up @@ -176,6 +183,7 @@ const Select = React.forwardRef(function Select(
[`${prefix}--select--fluid--invalid`]: isFluid && invalid,
[`${prefix}--select--fluid--focus`]: isFluid && isFocused,
[`${prefix}--select--slug`]: slug,
[`${prefix}--select--decorator`]: decorator,
});
const labelClasses = classNames(`${prefix}--label`, {
[`${prefix}--visually-hidden`]: hideLabel,
Expand Down Expand Up @@ -246,12 +254,20 @@ const Select = React.forwardRef(function Select(
},
};

// 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',
}
);
}

const input = (() => {
Expand Down Expand Up @@ -308,7 +324,15 @@ const Select = React.forwardRef(function Select(
onFocus={handleFocus}
onBlur={handleFocus}>
{input}
{normalizedSlug}
{slug ? (
normalizedDecorator
) : decorator ? (
<div className={`${prefix}--select__inner-wrapper--decorator`}>
{normalizedDecorator}
</div>
) : (
''
)}
{isFluid && <hr className={`${prefix}--select__divider`} />}
{isFluid && error ? error : null}
</div>
Expand All @@ -332,6 +356,11 @@ Select.propTypes = {
*/
className: PropTypes.string,

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

/**
* Optionally provide the default value of the `<select>`
*/
Expand Down Expand Up @@ -410,11 +439,10 @@ Select.propTypes = {
*/
size: PropTypes.oneOf(['sm', 'md', 'lg']),

/**
* **Experimental**: Provide a `Slug` component to be rendered inside the `Select` 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 whether the control is currently in warning state
*/
Expand Down
12 changes: 12 additions & 0 deletions packages/react/src/components/Select/__tests__/Select-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -242,13 +242,25 @@ describe('Select', () => {
});

it('should respect slug prop', () => {
const spy = jest.spyOn(console, 'warn').mockImplementation(() => {});
const { container } = render(
<Select id="select" labelText="Select" slug={<AILabel />} />
);

expect(container.firstChild.firstChild).toHaveClass(
`${prefix}--select--slug`
);
spy.mockRestore();
});

it('should respect decorator prop', () => {
const { container } = render(
<Select id="select" labelText="Select" decorator={<AILabel />} />
);

expect(container.firstChild.firstChild).toHaveClass(
`${prefix}--select--decorator`
);
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,10 +201,14 @@
}

// AILabel styles
.#{$prefix}--select--fluid
.#{$prefix}--select--decorator
.#{$prefix}--select__inner-wrapper--decorator
> *,
.#{$prefix}--select--fluid .#{$prefix}--select--slug .#{$prefix}--ai-label,
.#{$prefix}--select--fluid .#{$prefix}--select--slug .#{$prefix}--slug {
inset-block-start: convert.to-rem(42px);
inset-inline-end: $spacing-08;
inset-inline-end: $spacing-09;
}

.#{$prefix}--select--fluid
Expand Down
Loading

0 comments on commit ab1861d

Please sign in to comment.