Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโ€™ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(core/select): prevent list items to expand beyond screen width and add dropdown width properties #1669

Merged
merged 13 commits into from
Jan 30, 2025
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/orange-geckos-hide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@siemens/ix-angular': minor
'@siemens/ix': minor
'@siemens/ix-vue': minor
---

Prevent `ix-select` list items to expand beyond screen width and add properties: dropdown-width + dropdown-max-width
4 changes: 2 additions & 2 deletions packages/angular/src/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2186,15 +2186,15 @@ export declare interface IxRow extends Components.IxRow {}


@ProxyCmp({
inputs: ['allowClear', 'disabled', 'editable', 'helperText', 'hideListHeader', 'i18nNoMatches', 'i18nPlaceholder', 'i18nPlaceholderEditable', 'i18nSelectListHeader', 'infoText', 'invalidText', 'label', 'mode', 'name', 'readonly', 'required', 'selectedIndices', 'showTextAsTooltip', 'validText', 'value', 'warningText'],
inputs: ['allowClear', 'disabled', 'dropdownMaxWidth', 'dropdownWidth', 'editable', 'helperText', 'hideListHeader', 'i18nNoMatches', 'i18nPlaceholder', 'i18nPlaceholderEditable', 'i18nSelectListHeader', 'infoText', 'invalidText', 'label', 'mode', 'name', 'readonly', 'required', 'selectedIndices', 'showTextAsTooltip', 'validText', 'value', 'warningText'],
methods: ['getNativeInputElement', 'focusInput']
})
@Component({
selector: 'ix-select',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '<ng-content></ng-content>',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
inputs: ['allowClear', 'disabled', 'editable', 'helperText', 'hideListHeader', 'i18nNoMatches', 'i18nPlaceholder', 'i18nPlaceholderEditable', 'i18nSelectListHeader', 'infoText', 'invalidText', 'label', 'mode', 'name', 'readonly', 'required', 'selectedIndices', 'showTextAsTooltip', 'validText', 'value', 'warningText'],
inputs: ['allowClear', 'disabled', 'dropdownMaxWidth', 'dropdownWidth', 'editable', 'helperText', 'hideListHeader', 'i18nNoMatches', 'i18nPlaceholder', 'i18nPlaceholderEditable', 'i18nSelectListHeader', 'infoText', 'invalidText', 'label', 'mode', 'name', 'readonly', 'required', 'selectedIndices', 'showTextAsTooltip', 'validText', 'value', 'warningText'],
})
export class IxSelect {
protected el: HTMLElement;
Expand Down
58 changes: 58 additions & 0 deletions packages/core/component-doc.json
Original file line number Diff line number Diff line change
Expand Up @@ -16946,6 +16946,64 @@
"optional": false,
"required": false
},
{
"name": "dropdownMaxWidth",
"type": "number | undefined",
"complexType": {
"original": "number",
"resolved": "number | undefined",
"references": {}
},
"mutable": false,
"attr": "dropdown-max-width",
"reflectToAttr": false,
"docs": "Optional paramater to set the maximum width of the dropdown element in rem.\nBy default the maximum width of the dropdown element is set to 100%.",
"docsTags": [
{
"name": "since",
"text": "2.7.0"
}
],
"values": [
{
"type": "number"
},
{
"type": "undefined"
}
],
"optional": true,
"required": false
},
{
"name": "dropdownWidth",
"type": "number | undefined",
"complexType": {
"original": "number",
"resolved": "number | undefined",
"references": {}
},
"mutable": false,
"attr": "dropdown-width",
"reflectToAttr": false,
"docs": "Optional paramater to set the width of the dropdown element in rem.",
"docsTags": [
{
"name": "since",
"text": "2.7.0"
}
],
"values": [
{
"type": "number"
},
{
"type": "undefined"
}
],
"optional": true,
"required": false
},
{
"name": "editable",
"type": "boolean",
Expand Down
20 changes: 20 additions & 0 deletions packages/core/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2461,6 +2461,16 @@ export namespace Components {
* If true the select will be in disabled state
*/
"disabled": boolean;
/**
* Optional paramater to set the maximum width of the dropdown element in rem. By default the maximum width of the dropdown element is set to 100%.
* @since 2.7.0
*/
"dropdownMaxWidth"?: number;
/**
* Optional paramater to set the width of the dropdown element in rem.
* @since 2.7.0
*/
"dropdownWidth"?: number;
/**
* Select is extendable
*/
Expand Down Expand Up @@ -7685,6 +7695,16 @@ declare namespace LocalJSX {
* If true the select will be in disabled state
*/
"disabled"?: boolean;
/**
* Optional paramater to set the maximum width of the dropdown element in rem. By default the maximum width of the dropdown element is set to 100%.
* @since 2.7.0
*/
"dropdownMaxWidth"?: number;
/**
* Optional paramater to set the width of the dropdown element in rem.
* @since 2.7.0
*/
"dropdownWidth"?: number;
/**
* Select is extendable
*/
Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/components/select/select.scss
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,10 @@
}
}

ix-dropdown {
max-width: 100%;
danielleroux marked this conversation as resolved.
Show resolved Hide resolved
}

.dropdown-visible {
--ix-icon-button-color: var(--theme-color-dynamic--hover);
}
Expand Down
18 changes: 18 additions & 0 deletions packages/core/src/components/select/select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,22 @@ export class Select implements IxInputFieldComponent<string | string[]> {
*/
@Prop() hideListHeader = false;

/**
* Optional paramater to set the width of the dropdown element in rem.
AndreasBerliner marked this conversation as resolved.
Show resolved Hide resolved
*
* @since 2.7.0
*/
@Prop() dropdownWidth?: number;

/**
* Optional paramater to set the maximum width of the dropdown element in rem.
* By default the maximum width of the dropdown element is set to 100%.
*
* @since 2.7.0
*
*/
@Prop() dropdownMaxWidth?: number;

/**
* Value changed
* @since 2.0.0
Expand Down Expand Up @@ -960,6 +976,8 @@ export class Select implements IxInputFieldComponent<string | string[]> {

return {
minWidth: `${minWidth}px`,
width: `${this.dropdownWidth}rem`,
maxWidth: `${this.dropdownMaxWidth}rem`,
AndreasBerliner marked this conversation as resolved.
Show resolved Hide resolved
};
}}
>
Expand Down
99 changes: 99 additions & 0 deletions packages/core/src/components/select/test/select.ct.ts
Original file line number Diff line number Diff line change
Expand Up @@ -505,3 +505,102 @@ test('async set content and check input value', async ({ mount, page }) => {
const input = page.locator('input');
await expect(input).toHaveValue('Item 1');
});

test.describe('Dropdown width', () => {
test('should be 25rem when dropdown-width is set to 35rem and dropdown-max-width to 25rem', async ({
mount,
page,
}) => {
await mount(`<ix-select value="1" dropdown-width=35 dropdown-max-width=25>
<ix-select-item label="this is an example for a very long selection option. this is an example for a very long selection option." value="1"></ix-select-item>
</ix-select>`);

const select = page.locator('ix-select');

await page.locator('[data-select-dropdown]').click();

const dropdown = select.locator('ix-dropdown');
await expect(dropdown).toBeVisible();

const box = await dropdown.boundingBox();
expect(box?.width).toBe(16 * 25);
});

test('should be 25rem when dropdown-width is set to 25 and dropdown-max-width to 35rem', async ({
mount,
page,
}) => {
await mount(`<ix-select value="1" dropdown-width=25 dropdown-max-width=35>
<ix-select-item label="this is an example for a very long selection option. this is an example for a very long selection option." value="1"></ix-select-item>
</ix-select>`);

const select = page.locator('ix-select');

await page.locator('[data-select-dropdown]').click();

const dropdown = select.locator('ix-dropdown');
await expect(dropdown).toBeVisible();

const box = await dropdown.boundingBox();
expect(box?.width).toBe(16 * 25);
});

test('should be 25rem when dropdown-width is set to 25 and dropdown-max-width is not set', async ({
mount,
page,
}) => {
await mount(`<ix-select value="1" dropdown-width=25>
<ix-select-item label="this is an example for a very long selection option. this is an example for a very long selection option." value="1"></ix-select-item>
</ix-select>`);

const select = page.locator('ix-select');

await page.locator('[data-select-dropdown]').click();

const dropdown = select.locator('ix-dropdown');
await expect(dropdown).toBeVisible();

const box = await dropdown.boundingBox();
expect(box?.width).toBe(16 * 25);
});

test('should be 35rem when dropdown-width is not set and dropdown-max-width is set to 35rem', async ({
mount,
page,
}) => {
await mount(`<ix-select value="1" dropdown-max-width=35>
<ix-select-item label="this is an example for a very long selection option. this is an example for a very long selection option." value="1"></ix-select-item>
</ix-select>`);

const select = page.locator('ix-select');

await page.locator('[data-select-dropdown]').click();

const dropdown = select.locator('ix-dropdown');
await expect(dropdown).toBeVisible();

const box = await dropdown.boundingBox();
expect(box?.width).toBe(16 * 35);
});

test('should be 100% when dropdown-width and dropdown-max-width are not set', async ({
mount,
page,
}) => {
await mount(`<ix-select value="1">
<ix-select-item label="this is an example for a very long selection option. this is an example for a very long selection option. this is an example for a very long selection option. this is an example for a very long selection option. this is an example for a very long selection option." value="1"></ix-select-item>
</ix-select>`);

const select = page.locator('ix-select');

await page.locator('[data-select-dropdown]').click();

const dropdown = select.locator('ix-dropdown');
await expect(dropdown).toBeVisible();

const box = await dropdown.boundingBox();
const pageWidth = page.viewportSize()?.width;

expect(box?.width).toBe(pageWidth);
});
});
2 changes: 1 addition & 1 deletion packages/documentation/docs/controls/_select_styleguide.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ A select component allows users to choose from a list of options. It supports si
- **Overflow:**
- The text in an input field is truncated with the length of the container.
- On the multiselect, the selected items break into a second line and then show a scrollbar if it extends beyond two lines.
- The dropdown list is scrollable when the list exceeds the container height. Its width is defined by the longest item.
- The dropdown list is scrollable when the list exceeds the container height. Its width is defined by the longest item. The maximum width of the dropdown list is set to 100% by default. Use the properties `dropdownWidth` and `dropdownMaxWidth` to customize the dimensions.
- **Alignment:** Selects are always aligned to the left, while right alignment is reserved exclusively for [number inputs](input-number.mdx).

### States
Expand Down
30 changes: 30 additions & 0 deletions packages/storybook-docs/src/stories/select.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,33 @@ export const editableSelect: Story = {
disabled: false,
},
};

export const editable_with_dropdown_width: Story = {
render: ({
editable,
disabled,
dropdownWidth,
dropdownMaxWidth,
}) => {
return html` <ix-select
editable=${editable}
disabled=${disabled}
dropdown-width=${dropdownWidth}
dropdown-max-width=${dropdownMaxWidth}
>
<ix-select-item
label="this is an example for a very long selection option. this is an example for a very long selection option."
value="1"
></ix-select-item>
<ix-select-item label="Item 2" value="2"></ix-select-item>
<ix-select-item label="Item 3" value="3"></ix-select-item>
<ix-select-item label="Item 4" value="4"></ix-select-item>
</ix-select>`;
},
args: {
editable: true,
disabled: false,
dropdownWidth: 35,
dropdownMaxWidth: 25,
},
};
2 changes: 2 additions & 0 deletions packages/vue/src/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -986,6 +986,8 @@ export const IxSelect = /*@__PURE__*/ defineContainer<JSX.IxSelect, JSX.IxSelect
'i18nSelectListHeader',
'i18nNoMatches',
'hideListHeader',
'dropdownWidth',
'dropdownMaxWidth',
'valueChange',
'itemSelectionChange',
'inputChange',
Expand Down
Loading