Skip to content

Commit ffdeb72

Browse files
authored
feat: fluid_select_web_component (#20537)
* feat: fluid_select_web_component * feat: fluid select invalid states * fix: stories
1 parent 5a6decd commit ffdeb72

File tree

8 files changed

+417
-1
lines changed

8 files changed

+417
-1
lines changed

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,4 +230,29 @@
230230
.#{$prefix}--slug::before {
231231
display: none;
232232
}
233+
234+
// Skeleton
235+
.#{$prefix}--select--fluid__skeleton {
236+
position: relative;
237+
background: $skeleton-background;
238+
block-size: convert.to-rem(64px);
239+
border-block-end: 1px solid $skeleton-element;
240+
}
241+
242+
.#{$prefix}--select--fluid__skeleton .#{$prefix}--skeleton {
243+
position: absolute;
244+
block-size: convert.to-rem(8px);
245+
inline-size: 25%;
246+
inset-block-start: $spacing-05;
247+
inset-inline-start: $spacing-05;
248+
}
249+
250+
.#{$prefix}--select--fluid__skeleton .#{$prefix}--label {
251+
position: absolute;
252+
padding: 0;
253+
block-size: convert.to-rem(8px);
254+
inline-size: 50%;
255+
inset-block-start: convert.to-rem(36px);
256+
inset-inline-start: $spacing-05;
257+
}
233258
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/**
2+
* Copyright IBM Corp.2025
3+
*
4+
* This source code is licensed under the Apache-2.0 license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
import { prefix } from '../../globals/settings';
9+
import { html } from 'lit';
10+
import { carbonElement as customElement } from '../../globals/decorators/carbon-element';
11+
import styles from './fluid-select.scss?lit';
12+
import CDSSelectSkeleton from '../select/select-skeleton';
13+
/**
14+
* Fluid text area input.
15+
*
16+
* @element cds-fluid-select-skeleton
17+
*/
18+
@customElement(`${prefix}-fluid-select-skeleton`)
19+
class CDSFluidSelectSkeleton extends CDSSelectSkeleton {
20+
render() {
21+
return html`
22+
<div class="${prefix}--select--fluid__skeleton">${super.render()}</div>
23+
`;
24+
}
25+
26+
static styles = [CDSSelectSkeleton.styles, styles];
27+
}
28+
29+
export default CDSFluidSelectSkeleton;
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/**
2+
* Copyright IBM Corp.2025
3+
*
4+
* This source code is licensed under the Apache-2.0 license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
$css--plex: true !default;
9+
@use '@carbon/styles/scss/components/fluid-select/index';
10+
@use '@carbon/styles/scss/layout' as *;
11+
@use '@carbon/styles/scss/type' as *;
12+
@use '@carbon/styles/scss/config' as *;
13+
@use '@carbon/styles/scss/spacing' as *;
14+
@use '@carbon/styles/scss/theme' as *;
15+
@use '@carbon/styles/scss/components/select';
16+
@use '@carbon/styles/scss/colors';
17+
@use '@carbon/styles/scss/utilities/convert' as *;
18+
19+
$divider-width: 1px;
20+
21+
:host(#{$prefix}-fluid-select) {
22+
@extend .#{$prefix}--select--fluid;
23+
@extend .#{$prefix}--select--invalid;
24+
25+
@include emit-layout-tokens();
26+
27+
// Position the AI label and slug
28+
::slotted(#{$prefix}-ai-label),
29+
::slotted(#{$prefix}-slug) {
30+
position: absolute;
31+
inset-block-start: 70%;
32+
inset-inline-end: $spacing-09;
33+
}
34+
35+
// Transform for non-revert-active elements
36+
::slotted(#{$prefix}-ai-label:not([revert-active])),
37+
::slotted(#{$prefix}-slug:not([revert-active])) {
38+
transform: translateY(-70%);
39+
}
40+
41+
// AI label pseudo-elements for dividers
42+
::slotted(#{$prefix}-ai-label) {
43+
&::before,
44+
&::after {
45+
position: absolute;
46+
background-color: $border-subtle-01;
47+
block-size: to-rem(16px);
48+
content: '';
49+
inline-size: to-rem(1px);
50+
inset-inline-start: calc(#{$spacing-06} - #{$divider-width});
51+
}
52+
53+
&::before {
54+
display: var(--#{$prefix}-show-before);
55+
inset-inline-start: calc(-#{$spacing-03} - #{$divider-width});
56+
}
57+
}
58+
}
Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
/**
2+
* Copyright IBM Corp. 2025
3+
*
4+
* This source code is licensed under the Apache-2.0 license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
import { html } from 'lit';
9+
import { ifDefined } from 'lit/directives/if-defined.js';
10+
import './index';
11+
import View16 from '@carbon/icons/es/view/16.js';
12+
import FolderOpen16 from '@carbon/icons/es/folder--open/16.js';
13+
import Folders16 from '@carbon/icons/es/folders/16.js';
14+
import '../form/form-item';
15+
import '../ai-label';
16+
import '../icon-button';
17+
import { iconLoader } from '../../globals/internal/icon-loader';
18+
import '../select/select-item';
19+
import '../toggle-tip/toggletip';
20+
21+
const content = html`
22+
<div slot="body-text">
23+
<p class="secondary">AI Explained</p>
24+
<h2 class="ai-label-heading">84%</h2>
25+
<p class="secondary bold">Confidence score</p>
26+
<p class="secondary">
27+
Lorem ipsum dolor sit amet, di os consectetur adipiscing elit, sed do
28+
eiusmod tempor incididunt ut fsil labore et dolore magna aliqua.
29+
</p>
30+
<hr />
31+
<p class="secondary">Model type</p>
32+
<p class="bold">Foundation model</p>
33+
</div>
34+
`;
35+
36+
const actions = html`
37+
<cds-icon-button kind="ghost" slot="actions" size="lg">
38+
${iconLoader(View16, { slot: 'icon' })}
39+
<span slot="tooltip-content"> View </span>
40+
</cds-icon-button>
41+
<cds-icon-button kind="ghost" slot="actions" size="lg">
42+
${iconLoader(FolderOpen16, { slot: 'icon' })}
43+
<span slot="tooltip-content"> Open folder</span>
44+
</cds-icon-button>
45+
<cds-icon-button kind="ghost" slot="actions" size="lg">
46+
${iconLoader(Folders16, { slot: 'icon' })}
47+
<span slot="tooltip-content"> Folders </span>
48+
</cds-icon-button>
49+
<cds-ai-label-action-button>View details</cds-ai-label-action-button>
50+
`;
51+
52+
const args = {
53+
defaultWidth: 400,
54+
disabled: false,
55+
invalid: false,
56+
invalidText:
57+
'Error message that is really long can wrap to more lines but should not be excessively long',
58+
labelText: 'Select an option',
59+
readOnly: false,
60+
warn: false,
61+
warnText:
62+
'Warning message that is really long can wrap to more lines but should not be excessively long.',
63+
};
64+
65+
const argTypes = {
66+
defaultWidth: {
67+
control: { type: 'range', min: 300, max: 800, step: 50 },
68+
},
69+
disabled: {
70+
control: 'boolean',
71+
description: 'Specify whether the control is disabled.',
72+
},
73+
invalid: {
74+
control: 'boolean',
75+
description: 'Specify if the currently value is invalid.',
76+
},
77+
invalidText: {
78+
control: 'text',
79+
description: 'Message which is displayed if the value is invalid.',
80+
},
81+
labelText: {
82+
control: 'text',
83+
description:
84+
'Provide label text to be read by screen readers when interacting with the control.',
85+
},
86+
readOnly: {
87+
control: 'boolean',
88+
description: 'Whether the select should be read-only.',
89+
},
90+
warn: {
91+
control: 'boolean',
92+
description: 'Specify whether the control is currently in warning state.',
93+
},
94+
warnText: {
95+
control: 'text',
96+
description:
97+
'Provide the text that is displayed when the control is in warning state.',
98+
},
99+
};
100+
101+
export const Default = {
102+
args,
103+
argTypes,
104+
render: (args) => {
105+
const {
106+
defaultWidth,
107+
disabled,
108+
invalid,
109+
invalidText,
110+
labelText,
111+
name,
112+
readOnly,
113+
warn,
114+
warnText,
115+
} = args ?? {};
116+
return html`
117+
<div style="width:${defaultWidth}px;">
118+
<cds-fluid-select
119+
?disabled="${disabled}"
120+
?invalid="${invalid}"
121+
invalid-text="${ifDefined(invalidText)}"
122+
label-text="${ifDefined(labelText)}"
123+
name="${ifDefined(name)}"
124+
?readonly="${readOnly}"
125+
?warn="${warn}"
126+
warn-text="${ifDefined(warnText)}">
127+
<cds-select-item value=""></cds-select-item>
128+
<cds-select-item value="option-1">Option 1</cds-select-item>
129+
<cds-select-item value="option-2">Option 2</cds-select-item>
130+
<cds-select-item value="option-3">Option 3</cds-select-item>
131+
<cds-select-item value="option-4">Option 4</cds-select-item>
132+
</cds-fluid-select>
133+
<div></div>
134+
</div>
135+
`;
136+
},
137+
};
138+
139+
export const WithToggletip = {
140+
render: () => {
141+
return html`
142+
<div style="width:400px;">
143+
<cds-fluid-select>
144+
<cds-toggletip autoAlign="true" slot="label-text">
145+
Label
146+
<p slot="body-text">Additional field information here.</p>
147+
</cds-toggletip>
148+
<cds-select-item
149+
value="An example option that is really long to show what should be done to handle long text"
150+
>An example option that is really long to show what should be done
151+
to handle long text</cds-select-item
152+
>
153+
<cds-select-item value="option-1">Option 1</cds-select-item>
154+
<cds-select-item value="option-2">Option 2</cds-select-item>
155+
<cds-select-item value="option-3">Option 3</cds-select-item>
156+
<cds-select-item value="option-4">Option 4</cds-select-item>
157+
</cds-fluid-select>
158+
<div></div>
159+
</div>
160+
`;
161+
},
162+
};
163+
164+
export const Skeleton = {
165+
parameters: {
166+
percy: {
167+
skip: true,
168+
},
169+
},
170+
render: () =>
171+
html` <div style="width:400px;">
172+
<cds-fluid-select-skeleton></cds-fluid-select-skeleton>
173+
<div style="width:400px;"></div>
174+
</div>`,
175+
};
176+
177+
export const WithAILabel = {
178+
args,
179+
argTypes: {
180+
...argTypes,
181+
},
182+
render: (args) => {
183+
const {
184+
disabled,
185+
invalid,
186+
invalidText,
187+
labelText,
188+
name,
189+
readOnly,
190+
warn,
191+
warnText,
192+
defaultWidth,
193+
} = args ?? {};
194+
195+
return html` <div style="width:${defaultWidth}px;">
196+
<cds-fluid-select
197+
?disabled="${disabled}"
198+
?invalid="${invalid}"
199+
invalid-text="${ifDefined(invalidText)}"
200+
label-text="${ifDefined(labelText)}"
201+
name="${ifDefined(name)}"
202+
?readonly="${readOnly}"
203+
?warn="${warn}"
204+
warn-text="${ifDefined(warnText)}">
205+
<cds-ai-label alignment="bottom-left">
206+
${content}${actions}</cds-ai-label
207+
>
208+
<cds-select-item value=""></cds-select-item>
209+
<cds-select-item value="all"
210+
>An example option that is really long to show what should be done to
211+
handle long text</cds-select-item
212+
>
213+
<cds-select-item value="cloudFoundry">Option 2</cds-select-item>
214+
<cds-select-item value="staging">Option 3</cds-select-item>
215+
<cds-select-item value="dea">Option 4</cds-select-item>
216+
</cds-fluid-select>
217+
</div>`;
218+
},
219+
};
220+
221+
const meta = {
222+
decorators: [
223+
(story) => {
224+
return html`<div style="width: 400px">${story()}</div>`;
225+
},
226+
],
227+
title: 'Components/Fluid Components/FluidSelect',
228+
};
229+
230+
export default meta;

0 commit comments

Comments
 (0)