Skip to content

Commit ab1861d

Browse files
authored
feat(select): add new decorator prop (#18107)
* 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
1 parent 5da347d commit ab1861d

File tree

8 files changed

+184
-32
lines changed

8 files changed

+184
-32
lines changed

packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6831,6 +6831,9 @@ Map {
68316831
"className": Object {
68326832
"type": "string",
68336833
},
6834+
"decorator": Object {
6835+
"type": "node",
6836+
},
68346837
"defaultValue": Object {
68356838
"type": "any",
68366839
},
@@ -6879,9 +6882,7 @@ Map {
68796882
],
68806883
"type": "oneOf",
68816884
},
6882-
"slug": Object {
6883-
"type": "node",
6884-
},
6885+
"slug": [Function],
68856886
"warn": Object {
68866887
"type": "bool",
68876888
},

packages/react/src/components/FluidSelect/FluidSelect.stories.js

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ import {
1414
ToggletipButton,
1515
ToggletipContent,
1616
} from '../Toggletip';
17-
import { Information } from '@carbon/icons-react';
17+
import Button from '../Button';
18+
import { AILabel, AILabelContent, AILabelActions } from '../AILabel';
19+
import { IconButton } from '../IconButton';
20+
import { Information, View, FolderOpen, Folders } from '@carbon/icons-react';
1821
import mdx from './FluidSelect.mdx';
1922

2023
export default {
@@ -113,6 +116,52 @@ export const Default = () => (
113116
</div>
114117
);
115118

119+
const aiLabel = (
120+
<AILabel className="ai-label-container">
121+
<AILabelContent>
122+
<div>
123+
<p className="secondary">AI Explained</p>
124+
<h1>84%</h1>
125+
<p className="secondary bold">Confidence score</p>
126+
<p className="secondary">
127+
Lorem ipsum dolor sit amet, di os consectetur adipiscing elit, sed do
128+
eiusmod tempor incididunt ut fsil labore et dolore magna aliqua.
129+
</p>
130+
<hr />
131+
<p className="secondary">Model type</p>
132+
<p className="bold">Foundation model</p>
133+
</div>
134+
<AILabelActions>
135+
<IconButton kind="ghost" label="View">
136+
<View />
137+
</IconButton>
138+
<IconButton kind="ghost" label="Open Folder">
139+
<FolderOpen />
140+
</IconButton>
141+
<IconButton kind="ghost" label="Folders">
142+
<Folders />
143+
</IconButton>
144+
<Button>View details</Button>
145+
</AILabelActions>
146+
</AILabelContent>
147+
</AILabel>
148+
);
149+
150+
export const withAILabel = () => (
151+
<div style={{ width: 400 }}>
152+
<FluidSelect id="select-1" labelText="Select an option" decorator={aiLabel}>
153+
<SelectItem value="" text="" />
154+
<SelectItem
155+
value="An example option that is really long to show what should be done to handle long text"
156+
text="An example option that is really long to show what should be done to handle long text"
157+
/>
158+
<SelectItem value="Option 2" text="Option 2" />
159+
<SelectItem value="Option 3" text="Option 3" />
160+
<SelectItem value="Option 4" text="Option 4" />
161+
</FluidSelect>
162+
</div>
163+
);
164+
116165
export const Playground = (args) => (
117166
<div style={{ width: args.playgroundWidth }}>
118167
<FluidSelect {...args} id="select-1">

packages/react/src/components/Form/Form.stories.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ export const withAILabel = (args) => {
365365
id="select-1"
366366
labelText="Select an option"
367367
helperText="Optional helper text"
368-
slug={aiLabel}
368+
decorator={aiLabel}
369369
{...rest}>
370370
<SelectItem value="" text="" />
371371
<SelectItem
@@ -476,7 +476,7 @@ export const withAILabel = (args) => {
476476
/>
477477
</div>
478478
<div style={{ display: 'flex' }}>
479-
<FluidSelect slug={aiLabel} {...rest} id="select-2">
479+
<FluidSelect decorator={aiLabel} {...rest} id="select-2">
480480
<SelectItem value="" text="" />
481481
<SelectItem value="option-1" text="Option 1" />
482482
<SelectItem value="option-2" text="Option 2" />

packages/react/src/components/Select/Select.stories.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { AILabel, AILabelContent, AILabelActions } from '../AILabel';
1717
import { IconButton } from '../IconButton';
1818
import { View, FolderOpen, Folders } from '@carbon/icons-react';
1919
import mdx from './Select.mdx';
20+
import { Tooltip } from '../Tooltip';
2021

2122
export default {
2223
title: 'Components/Select',
@@ -166,12 +167,12 @@ const aiLabel = (
166167
);
167168

168169
export const withAILabel = () => (
169-
<div style={{ width: 400 }}>
170+
<div>
170171
<Select
171172
id="select-1"
172173
labelText="Select an option"
173174
helperText="Optional helper text"
174-
slug={aiLabel}>
175+
decorator={aiLabel}>
175176
<SelectItem value="" text="" />
176177
<SelectItem
177178
value="An example option that is really long to show what should be done to handle long text"

packages/react/src/components/Select/Select.tsx

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ export interface SelectProps
4242
*/
4343
className?: string;
4444

45+
/**
46+
* **Experimental**: Provide a `decorator` component to be rendered inside the `Select` component
47+
*/
48+
decorator?: ReactNode;
49+
4550
/**
4651
* Optionally provide the default value of the `<select>`
4752
*/
@@ -118,6 +123,7 @@ export interface SelectProps
118123
size?: 'sm' | 'md' | 'lg';
119124

120125
/**
126+
* @deprecated please use decorator instead.
121127
* **Experimental**: Provide a `Slug` component to be rendered inside the `Dropdown` component
122128
*/
123129
slug?: ReactNode;
@@ -136,6 +142,7 @@ export interface SelectProps
136142
const Select = React.forwardRef(function Select(
137143
{
138144
className,
145+
decorator,
139146
id,
140147
inline = false,
141148
labelText = 'Select',
@@ -176,6 +183,7 @@ const Select = React.forwardRef(function Select(
176183
[`${prefix}--select--fluid--invalid`]: isFluid && invalid,
177184
[`${prefix}--select--fluid--focus`]: isFluid && isFocused,
178185
[`${prefix}--select--slug`]: slug,
186+
[`${prefix}--select--decorator`]: decorator,
179187
});
180188
const labelClasses = classNames(`${prefix}--label`, {
181189
[`${prefix}--visually-hidden`]: hideLabel,
@@ -246,12 +254,20 @@ const Select = React.forwardRef(function Select(
246254
},
247255
};
248256

249-
// Slug is always size `mini`
250-
let normalizedSlug;
251-
if (slug && slug['type']?.displayName === 'AILabel') {
252-
normalizedSlug = React.cloneElement(slug as React.ReactElement<any>, {
253-
size: 'mini',
254-
});
257+
// AILabel always size `mini`
258+
let normalizedDecorator = React.isValidElement(slug ?? decorator)
259+
? (slug ?? decorator)
260+
: null;
261+
if (
262+
normalizedDecorator &&
263+
normalizedDecorator['type']?.displayName === 'AILabel'
264+
) {
265+
normalizedDecorator = React.cloneElement(
266+
normalizedDecorator as React.ReactElement<any>,
267+
{
268+
size: 'mini',
269+
}
270+
);
255271
}
256272

257273
const input = (() => {
@@ -308,7 +324,15 @@ const Select = React.forwardRef(function Select(
308324
onFocus={handleFocus}
309325
onBlur={handleFocus}>
310326
{input}
311-
{normalizedSlug}
327+
{slug ? (
328+
normalizedDecorator
329+
) : decorator ? (
330+
<div className={`${prefix}--select__inner-wrapper--decorator`}>
331+
{normalizedDecorator}
332+
</div>
333+
) : (
334+
''
335+
)}
312336
{isFluid && <hr className={`${prefix}--select__divider`} />}
313337
{isFluid && error ? error : null}
314338
</div>
@@ -332,6 +356,11 @@ Select.propTypes = {
332356
*/
333357
className: PropTypes.string,
334358

359+
/**
360+
* **Experimental**: Provide a decorator component to be rendered inside the `Select` component
361+
*/
362+
decorator: PropTypes.node,
363+
335364
/**
336365
* Optionally provide the default value of the `<select>`
337366
*/
@@ -410,11 +439,10 @@ Select.propTypes = {
410439
*/
411440
size: PropTypes.oneOf(['sm', 'md', 'lg']),
412441

413-
/**
414-
* **Experimental**: Provide a `Slug` component to be rendered inside the `Select` component
415-
*/
416-
slug: PropTypes.node,
417-
442+
slug: deprecate(
443+
PropTypes.node,
444+
'The `slug` prop has been deprecated and will be removed in the next major version. Use the decorator prop instead.'
445+
),
418446
/**
419447
* Specify whether the control is currently in warning state
420448
*/

packages/react/src/components/Select/__tests__/Select-test.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,13 +242,25 @@ describe('Select', () => {
242242
});
243243

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

249250
expect(container.firstChild.firstChild).toHaveClass(
250251
`${prefix}--select--slug`
251252
);
253+
spy.mockRestore();
254+
});
255+
256+
it('should respect decorator prop', () => {
257+
const { container } = render(
258+
<Select id="select" labelText="Select" decorator={<AILabel />} />
259+
);
260+
261+
expect(container.firstChild.firstChild).toHaveClass(
262+
`${prefix}--select--decorator`
263+
);
252264
});
253265
});
254266

packages/styles/scss/components/fluid-select/_fluid-select.scss

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,10 +201,14 @@
201201
}
202202

203203
// AILabel styles
204+
.#{$prefix}--select--fluid
205+
.#{$prefix}--select--decorator
206+
.#{$prefix}--select__inner-wrapper--decorator
207+
> *,
204208
.#{$prefix}--select--fluid .#{$prefix}--select--slug .#{$prefix}--ai-label,
205209
.#{$prefix}--select--fluid .#{$prefix}--select--slug .#{$prefix}--slug {
206210
inset-block-start: convert.to-rem(42px);
207-
inset-inline-end: $spacing-08;
211+
inset-inline-end: $spacing-09;
208212
}
209213

210214
.#{$prefix}--select--fluid

0 commit comments

Comments
 (0)