Skip to content

Commit 0c62e71

Browse files
authored
Add autowidth attribute to select and combobox (#67)
* Add autowidth attribute to select and combobox * Fix linter
1 parent 1039606 commit 0c62e71

File tree

8 files changed

+187
-4
lines changed

8 files changed

+187
-4
lines changed

.prettierrc

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
{
22
"singleQuote": true,
33
"trailingComma": "none",
4-
"arrowParens": "avoid"
4+
"arrowParens": "avoid",
5+
"overrides": [
6+
{
7+
"files": "package.json",
8+
"options": {
9+
"tabWidth": 4
10+
}
11+
}
12+
]
513
}

packages/components/src/combobox/combobox.stories.ts

+9
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export default {
1414
customIndicator: { control: 'boolean' },
1515
numberOfChildren: { control: 'number' },
1616
isMinimal: { control: 'boolean' },
17+
hasAutoWidth: { control: 'boolean' },
1718
autocomplete: {
1819
control: 'select',
1920
options: ['none', 'inline', 'list', 'both']
@@ -55,6 +56,7 @@ const Template: StoryFn = (args, context): HTMLElement => {
5556
`<jp-combobox
5657
${args.isDisabled ? 'disabled' : ''}
5758
${args.isMinimal ? 'minimal' : ''}
59+
${args.hasAutoWidth ? 'autowidth' : ''}
5860
${args.autocomplete !== 'none' ? `autocomplete=${args.autocomplete}` : ''}
5961
>
6062
${args.customIndicator ? getFaIcon('sliders-h', 'indicator') : ''}
@@ -90,6 +92,7 @@ Default.args = {
9092
customIndicator: false,
9193
numberOfChildren: 10,
9294
isMinimal: false,
95+
hasAutoWidth: false,
9396
autocomplete: 'none',
9497
onChange: action('combobox-onchange')
9598
};
@@ -100,6 +103,12 @@ WithOpen.args = {
100103
isOpen: true
101104
};
102105

106+
export const WithAutoWidth: StoryObj = { render: Template.bind({}) };
107+
WithAutoWidth.args = {
108+
...Default.args,
109+
hasAutoWidth: true
110+
};
111+
103112
export const WithDisabled: StoryObj = { render: Template.bind({}) };
104113
WithDisabled.args = {
105114
...Default.args,

packages/components/src/combobox/index.ts

+72
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,16 @@ import { comboboxStyles as styles } from './combobox.styles';
1414
* @public
1515
*/
1616
export class Combobox extends FoundationCombobox {
17+
/**
18+
* Whether the combobox has a compact layout or not.
19+
*
20+
* @public
21+
* @remarks
22+
* HTML Attribute: minimal
23+
*/
24+
@attr({ attribute: 'autowidth', mode: 'boolean' })
25+
public autoWidth: boolean;
26+
1727
/**
1828
* Whether the combobox has a compact layout or not.
1929
*
@@ -23,6 +33,68 @@ export class Combobox extends FoundationCombobox {
2333
*/
2434
@attr({ attribute: 'minimal', mode: 'boolean' })
2535
public minimal: boolean;
36+
37+
/**
38+
* The connected callback for this FASTElement.
39+
*
40+
* @override
41+
*
42+
* @internal
43+
*/
44+
connectedCallback(): void {
45+
super.connectedCallback();
46+
this.setAutoWidth();
47+
}
48+
49+
/**
50+
* Synchronize the form-associated proxy and updates the value property of the element.
51+
*
52+
* @param prev - the previous collection of slotted option elements
53+
* @param next - the next collection of slotted option elements
54+
*
55+
* @internal
56+
*/
57+
slottedOptionsChanged(prev: Element[] | undefined, next: Element[]): void {
58+
super.slottedOptionsChanged(prev, next);
59+
this.setAutoWidth();
60+
}
61+
62+
/**
63+
* (Un-)set the width when the autoWidth property changes.
64+
*
65+
* @param prev - the previous autoWidth value
66+
* @param next - the current autoWidth value
67+
*/
68+
protected autoWidthChanged(prev: boolean | undefined, next: boolean): void {
69+
if (next) {
70+
this.setAutoWidth();
71+
} else {
72+
this.style.removeProperty('width');
73+
}
74+
}
75+
76+
/**
77+
* Compute the listbox width to set the one of the input.
78+
*/
79+
protected setAutoWidth(): void {
80+
if (!this.autoWidth || !this.isConnected) {
81+
return;
82+
}
83+
84+
let listWidth = this.listbox.getBoundingClientRect().width;
85+
// If the list has not been displayed yet trick to get its size
86+
if (listWidth === 0 && this.listbox.hidden) {
87+
Object.assign(this.listbox.style, { visibility: 'hidden' });
88+
this.listbox.removeAttribute('hidden');
89+
listWidth = this.listbox.getBoundingClientRect().width;
90+
this.listbox.setAttribute('hidden', '');
91+
this.listbox.style.removeProperty('visibility');
92+
}
93+
94+
if (listWidth > 0) {
95+
Object.assign(this.style, { width: `${listWidth}px` });
96+
}
97+
}
2698
}
2799

28100
/**

packages/components/src/select/index.ts

+72
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,16 @@ import { selectStyles as styles } from './select.styles';
1414
* @public
1515
*/
1616
export class Select extends FoundationSelect {
17+
/**
18+
* Whether the select has a compact layout or not.
19+
*
20+
* @public
21+
* @remarks
22+
* HTML Attribute: minimal
23+
*/
24+
@attr({ attribute: 'autowidth', mode: 'boolean' })
25+
public autoWidth: boolean;
26+
1727
/**
1828
* Whether the select has a compact layout or not.
1929
*
@@ -23,6 +33,68 @@ export class Select extends FoundationSelect {
2333
*/
2434
@attr({ attribute: 'minimal', mode: 'boolean' })
2535
public minimal: boolean;
36+
37+
/**
38+
* The connected callback for this FASTElement.
39+
*
40+
* @override
41+
*
42+
* @internal
43+
*/
44+
connectedCallback(): void {
45+
super.connectedCallback();
46+
this.setAutoWidth();
47+
}
48+
49+
/**
50+
* Synchronize the form-associated proxy and updates the value property of the element.
51+
*
52+
* @param prev - the previous collection of slotted option elements
53+
* @param next - the next collection of slotted option elements
54+
*
55+
* @internal
56+
*/
57+
slottedOptionsChanged(prev: Element[] | undefined, next: Element[]): void {
58+
super.slottedOptionsChanged(prev, next);
59+
this.setAutoWidth();
60+
}
61+
62+
/**
63+
* (Un-)set the width when the autoWidth property changes.
64+
*
65+
* @param prev - the previous autoWidth value
66+
* @param next - the current autoWidth value
67+
*/
68+
protected autoWidthChanged(prev: boolean | undefined, next: boolean): void {
69+
if (next) {
70+
this.setAutoWidth();
71+
} else {
72+
this.style.removeProperty('width');
73+
}
74+
}
75+
76+
/**
77+
* Compute the listbox width to set the one of the input.
78+
*/
79+
protected setAutoWidth(): void {
80+
if (!this.autoWidth || !this.isConnected) {
81+
return;
82+
}
83+
84+
let listWidth = this.listbox.getBoundingClientRect().width;
85+
// If the list has not been displayed yet trick to get its size
86+
if (listWidth === 0 && this.listbox.hidden) {
87+
Object.assign(this.listbox.style, { visibility: 'hidden' });
88+
this.listbox.removeAttribute('hidden');
89+
listWidth = this.listbox.getBoundingClientRect().width;
90+
this.listbox.setAttribute('hidden', '');
91+
this.listbox.style.removeProperty('visibility');
92+
}
93+
94+
if (listWidth > 0) {
95+
Object.assign(this.style, { width: `${listWidth}px` });
96+
}
97+
}
2698
}
2799

28100
/**

packages/components/src/select/select.stories.ts

+14
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export default {
1313
customIndicator: { control: 'boolean' },
1414
numberOfChildren: { control: 'number' },
1515
isMinimal: { control: 'boolean' },
16+
hasAutoWidth: { control: 'boolean' },
1617
onChange: {
1718
action: 'changed',
1819
table: {
@@ -29,11 +30,14 @@ const Template: StoryFn = (args, context): HTMLElement => {
2930
} = context;
3031
setTheme(accent, parameters.backgrounds, backgrounds);
3132
const container = document.createElement('div');
33+
34+
const index = args.numberOfChildren ?? 3;
3235
container.insertAdjacentHTML(
3336
'afterbegin',
3437
`<jp-select
3538
${args.isDisabled ? 'disabled' : ''}
3639
${args.isMinimal ? 'minimal' : ''}
40+
${args.hasAutoWidth ? 'autowidth' : ''}
3741
>
3842
${args.customIndicator ? getFaIcon('sliders-h', 'indicator') : ''}
3943
${new Array(args.numberOfChildren ?? 3)
@@ -43,6 +47,9 @@ const Template: StoryFn = (args, context): HTMLElement => {
4347
`<jp-option value="${index}">Option ${index + 1}</jp-option>`
4448
)
4549
.join('\n')}
50+
<jp-option value="${index}">This is a very long option ${
51+
index + 1
52+
}</jp-option>
4653
</jp-select>`
4754
);
4855

@@ -66,6 +73,7 @@ Default.args = {
6673
customIndicator: false,
6774
numberOfChildren: 3,
6875
isMinimal: false,
76+
hasAutoWidth: false,
6977
onChange: action('select-onchange')
7078
};
7179

@@ -75,6 +83,12 @@ WithOpen.args = {
7583
isOpen: true
7684
};
7785

86+
export const WithAutoWidth: StoryObj = { render: Template.bind({}) };
87+
WithAutoWidth.args = {
88+
...Default.args,
89+
hasAutoWidth: true
90+
};
91+
7892
export const WithDisabled: StoryObj = { render: Template.bind({}) };
7993
WithDisabled.args = {
8094
...Default.args,

packages/components/src/select/select.styles.ts

+8-3
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,14 @@ export const selectStyles: FoundationElementTemplate<
5858
height: calc(${heightNumber} * 1px);
5959
position: relative;
6060
user-select: none;
61-
min-width: 250px;
6261
outline: none;
6362
vertical-align: top;
6463
}
6564
65+
:host(:not([autowidth])) {
66+
min-width: 250px;
67+
}
68+
6669
.listbox {
6770
${elevation}
6871
background: ${neutralLayerFloating};
@@ -76,10 +79,13 @@ export const selectStyles: FoundationElementTemplate<
7679
padding: calc(${designUnit} * 1px) 0;
7780
overflow-y: auto;
7881
position: absolute;
79-
width: 100%;
8082
z-index: 1;
8183
}
8284
85+
:host(:not([autowidth])) .listbox {
86+
width: 100%;
87+
}
88+
8389
.listbox[hidden] {
8490
display: none;
8591
}
@@ -99,7 +105,6 @@ export const selectStyles: FoundationElementTemplate<
99105
100106
:host([minimal]) {
101107
--density: -4;
102-
min-width: unset;
103108
}
104109
105110
:host(:not([disabled]):hover) {

packages/react-components/src/combobox/index.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ const { wrap } = provideReactWrapper(React, provideJupyterDesignSystem());
1313
export const Combobox: React.DetailedHTMLFactory<
1414
React.HTMLAttributes<HTMLElement> & {
1515
autocomplete?: 'inline' | 'list' | 'both' | 'none';
16+
autowidth?: boolean;
1617
disabled?: boolean;
18+
minimal?: boolean;
1719
name?: string;
1820
position?: 'above' | 'below';
1921
placeholder?: 'string';

packages/react-components/src/select/index.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const { wrap } = provideReactWrapper(React, provideJupyterDesignSystem());
99

1010
export const Select: React.DetailedHTMLFactory<
1111
React.HTMLAttributes<HTMLElement> & {
12+
autowidth?: boolean;
1213
disabled?: boolean;
1314
minimal?: boolean;
1415
open?: boolean;

0 commit comments

Comments
 (0)