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

feat: add option to always show all chips for selected items #6515

Merged
merged 2 commits into from
Oct 25, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ registerStyles(
display: flex;
width: 100%;
}

:host([all-chips-visible]) #wrapper {
flex-wrap: wrap;
}
`,
{
moduleId: 'vaadin-multi-select-combo-box-container-styles',
Expand Down Expand Up @@ -49,6 +53,20 @@ class MultiSelectComboBoxContainer extends InputContainer {
}
return memoizedTemplate;
}

static get properties() {
return {
/**
* Set true to not collapse selected items chips into the overflow
* chip and instead always show them, causing input field to grow.
* @attr {boolean} all-chips-visible
*/
allChipsVisible: {
type: Boolean,
reflectToAttribute: true,
},
};
}
}

defineCustomElement(MultiSelectComboBoxContainer);
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,14 @@ export interface MultiSelectComboBoxEventMap<TItem> extends HTMLElementEventMap
* @fires {CustomEvent} validated - Fired whenever the field is validated.
*/
declare class MultiSelectComboBox<TItem = ComboBoxDefaultItem> extends HTMLElement {
/**
* Set to true to not collapse selected items chips into the overflow
* chip and instead always show them all, causing input field to grow
* and wrap into multiple lines when width is limited.
* @attr {boolean} all-chips-visible
*/
allChipsVisible: boolean;

/**
* When true, the user can input a value that is not present in the items list.
* @attr {boolean} allow-custom-value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ const multiSelectComboBox = css`
flex-basis: 0;
padding: 0;
}

:host([all-chips-visible]) #chips {
display: contents;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is needed to get <input> element wrapping together with chip elements. In case of Goran's addon, it worked out of the box because there wasn't any additional container around chip elements.

}

:host([all-chips-visible]) [class$='container'] {
width: fit-content;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This ensures the component expands when selecting a new item, until max-width is reached.

}
`;

registerStyles('vaadin-multi-select-combo-box', [inputFieldShared, multiSelectComboBox], {
Expand Down Expand Up @@ -177,6 +185,7 @@ class MultiSelectComboBox extends ResizeMixin(InputControlMixin(ThemableMixin(El
>
<vaadin-multi-select-combo-box-container
part="input-field"
all-chips-visible="[[allChipsVisible]]"
readonly="[[readonly]]"
disabled="[[disabled]]"
invalid="[[invalid]]"
Expand Down Expand Up @@ -213,6 +222,19 @@ class MultiSelectComboBox extends ResizeMixin(InputControlMixin(ThemableMixin(El

static get properties() {
return {
/**
* Set to true to not collapse selected items chips into the overflow
* chip and instead always show them all, causing input field to grow
* and wrap into multiple lines when width is limited.
* @attr {boolean} all-chips-visible
*/
allChipsVisible: {
type: Boolean,
value: false,
reflectToAttribute: true,
observer: '_allChipsVisibleChanged',
},

/**
* Set true to prevent the overlay from opening automatically.
* @attr {boolean} auto-open-disabled
Expand Down Expand Up @@ -674,6 +696,13 @@ class MultiSelectComboBox extends ResizeMixin(InputControlMixin(ThemableMixin(El
super._delegateAttribute(name, value);
}

/** @private */
_allChipsVisibleChanged(visible, oldVisible) {
if (visible || oldVisible) {
this.__updateChips();
}
}

/**
* Setting clear button visible reduces total space available
* for rendering chips, and making it hidden increases it.
Expand Down Expand Up @@ -918,7 +947,8 @@ class MultiSelectComboBox extends ResizeMixin(InputControlMixin(ThemableMixin(El
const chip = this.__createChip(items[i]);
this.insertBefore(chip, refNode);

if (this.$.chips.clientWidth > remainingWidth) {
// If all the chips are visible, no need to measure remaining width
if (!this.allChipsVisible && this.$.chips.clientWidth > remainingWidth) {
chip.remove();
break;
}
Expand Down
50 changes: 50 additions & 0 deletions packages/multi-select-combo-box/test/chips.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -411,4 +411,54 @@ describe('chips', () => {
});
});
});

describe('allChipsVisible', () => {
let overflow;

beforeEach(async () => {
comboBox.style.width = '250px';
await nextResize(comboBox);
overflow = getChips(comboBox)[0];
});

it('should not show overflow chip when allChipsVisible is set to true', async () => {
comboBox.allChipsVisible = true;
comboBox.selectedItems = ['apple', 'banana'];
await nextRender();
expect(getChips(comboBox).length).to.equal(3);
expect(overflow.hasAttribute('hidden')).to.be.true;
});

it('should show overflow chip when allChipsVisible is set to false', async () => {
comboBox.allChipsVisible = true;
comboBox.selectedItems = ['apple', 'banana'];
await nextRender();

comboBox.allChipsVisible = false;
await nextRender();
expect(getChips(comboBox).length).to.equal(2);
expect(overflow.hasAttribute('hidden')).to.be.false;
});

it('should update chips when allChipsVisible is set after selectedItems', async () => {
comboBox.selectedItems = ['apple', 'banana'];
await nextRender();
expect(getChips(comboBox).length).to.equal(2);
expect(overflow.hasAttribute('hidden')).to.be.false;

comboBox.allChipsVisible = true;
await nextRender();
expect(getChips(comboBox).length).to.equal(3);
expect(overflow.hasAttribute('hidden')).to.be.true;
});

it('should wrap chips and increase input field height if chips do not fit', async () => {
const inputField = comboBox.shadowRoot.querySelector('[part="input-field"]');
const height = inputField.clientHeight;
comboBox.allChipsVisible = true;
comboBox.selectedItems = ['apple', 'banana', 'lemon', 'orange'];
await nextRender();
expect(inputField.clientHeight).to.be.greaterThan(height);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ assertType<string | null | undefined>(narrowedComboBox.label);
assertType<boolean>(narrowedComboBox.required);
assertType<string>(narrowedComboBox.overlayClass);
assertType<boolean>(narrowedComboBox.groupSelectedItems);
assertType<boolean>(narrowedComboBox.allChipsVisible);

// Mixins
assertType<ControllerMixinClass>(narrowedComboBox);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,22 @@ describe('multi-select-combo-box', () => {
});
});

describe('all chips visible', () => {
beforeEach(() => {
element.selectedItems = [...element.items];
element.allChipsVisible = true;
});

it('all chips visible', async () => {
await visualDiff(div, 'all-chips-visible');
});

it('all chips visible max width', async () => {
element.style.maxWidth = '250px';
await visualDiff(div, 'all-chips-visible-max-width');
});
});

describe('opened', () => {
beforeEach(() => {
div.style.height = '200px';
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,22 @@ describe('multi-select-combo-box', () => {
});
});

describe('all chips visible', () => {
beforeEach(() => {
element.selectedItems = [...element.items];
element.allChipsVisible = true;
});

it('all chips visible', async () => {
await visualDiff(div, 'all-chips-visible');
});

it('all chips visible max width', async () => {
element.style.maxWidth = '250px';
await visualDiff(div, 'all-chips-visible-max-width');
});
});

describe('opened', () => {
beforeEach(() => {
div.style.height = '200px';
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,16 @@ registerStyles(
{ moduleId: 'lumo-multi-select-combo-box-overlay' },
);

registerStyles(
'vaadin-multi-select-combo-box-container',
css`
:host([all-chips-visible]) {
padding-block: var(--lumo-space-xs);
}
`,
{ moduleId: 'lumo-multi-select-combo-box-container' },
);

const multiSelectComboBox = css`
:host([has-value]) {
padding-inline-start: 0;
Expand All @@ -67,10 +77,18 @@ const multiSelectComboBox = css`
mask-image: none;
}

:host([all-chips-visible]) ::slotted([slot='chip']) {
margin-block: calc(var(--lumo-space-xs) / 2);
}

::slotted([slot='chip']:not([readonly]):not([disabled])) {
padding-inline-end: 0;
}

:host([all-chips-visible]) ::slotted([slot='input']) {
min-height: calc(var(--lumo-text-field-size, var(--lumo-size-m)) - 2 * var(--lumo-space-xs));
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reduced min-height to align input more nicely when chips wrap into multiple lines.

}

::slotted([slot='chip']:not(:last-of-type)),
::slotted([slot='overflow']:not(:last-of-type)) {
margin-inline-end: var(--lumo-space-xs);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ const multiSelectComboBox = css`
padding: 0 0.5rem;
}

:host([all-chips-visible]) ::slotted([slot='chip']) {
margin-top: 0.25rem;
align-self: flex-start;
}

::slotted([slot='chip']:not([readonly]):not([disabled])) {
padding-inline-end: 0;
}
Expand Down