Skip to content

Commit

Permalink
Merge pull request #529 from gselderslaghs/chips-input-option
Browse files Browse the repository at this point in the history
fix/improvement(Chips) - Input option (proof-of-concept)
  • Loading branch information
wuda-io authored Dec 16, 2024
2 parents 472988b + 2dc93b2 commit d0d4efe
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 36 deletions.
9 changes: 6 additions & 3 deletions sass/components/_chips.scss
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@
flex-wrap: wrap;

border: none;
border-bottom: 1px solid var(--md-sys-color-on-surface-variant);
box-shadow: none;
margin: 0 0 8px 0;

Expand All @@ -90,8 +89,12 @@
box-shadow: 0 1px 0 0 var(--md-sys-color-primary);
}

&:hover {
cursor: text;
&.input-field {
border-bottom: 1px solid var(--md-sys-color-on-surface-variant);

&:hover {
cursor: text;
}
}

input:not([type]):not(.browser-default).input {
Expand Down
51 changes: 37 additions & 14 deletions spec/tests/chips/chipsSpec.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
/* eslint-disable no-undef */

describe('Chips', () => {
const fixture = `<div class="chips"><input></div>
<div class="chips chips-initial"><input></div>
<div class="chips chips-placeholder"><input></div>
<div class="chips chips-autocomplete"><input></div>`;
const fixture = `<div class="chips"></div>
<div class="chips chips-initial"></div>
<div class="chips input-field"><input></div>
<div class="chips chips-initial input-field"><input></div>
<div class="chips chips-placeholder input-field"><input></div>
<div class="chips chips-autocomplete input-field"><input></div>`;

beforeEach(() => {
XloadHtml(fixture);
Expand All @@ -21,11 +23,29 @@ describe('Chips', () => {
}
]
});
M.Chips.init(document.querySelector('.chips-placeholder'), {
M.Chips.init(document.querySelector('.chips.input-field'), {
allowUserInput: true
});
M.Chips.init(document.querySelector('.chips-initial.input-field'), {
allowUserInput: true,
data: [
{ id: 12, text: 'Apple' },
{ id: 13, text: 'Microsoft' },
{
id: 42,
text: 'Google',
image:
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='
}
]
});
M.Chips.init(document.querySelector('.chips-placeholder.input-field'), {
allowUserInput: true,
placeholder: 'Enter a tag',
secondaryPlaceholder: '+Tag'
});
M.Chips.init(document.querySelector('.chips-autocomplete'), {
M.Chips.init(document.querySelector('.chips-autocomplete.input-field'), {
allowUserInput: true,
autocompleteOptions: {
data: [
{ id: 12, text: 'Apple' },
Expand All @@ -38,20 +58,21 @@ describe('Chips', () => {
afterEach(() => XunloadFixtures());

describe('chips plugin', () => {
let chips, input;
let chips, chipsUserInput, input;

it('should work with multiple initializations', () => {
chips = document.querySelector('.chips');
M.Chips.init(chips);
M.Chips.init(chips);
M.Chips.init(chips);
M.Chips.init(chips);
input = chips.querySelectorAll('input');
chipsUserInput = document.querySelector('.chips.input-field');
M.Chips.init(chips, {allowUserInput: true});
input = chipsUserInput.querySelectorAll('input');
expect(input.length).toEqual(1, 'Should dynamically generate chips structure.');
});

it('should be able to add chip', (done) => {
chips = document.querySelector('.chips');
chips = document.querySelector('.chips.input-field');
input = chips.querySelector('input');
input.value = 'one';
keydown(input, 13);
Expand All @@ -68,11 +89,12 @@ describe('Chips', () => {
});

it('should be able to delete chip', (done) => {
chips = document.querySelector('.chips.chips-initial');
input = chips.querySelector('input');
chips = document.querySelector('.chips.chips-initial.input-field');
let numChips = chips.querySelectorAll('.chip').length;
expect(numChips).toEqual(3, '3 initial chips should have been added');
click(chips.querySelector('.chip .close'));
let chipCloseButton = chips.querySelectorAll('.chip .close');
expect(chipCloseButton.length).toEqual(3, 'expected all chips to have close button');
click(chipCloseButton[0]);
setTimeout(() => {
numChips = chips.querySelectorAll('.chip').length;
expect(numChips).toEqual(2, 'one chip should have been deleted');
Expand All @@ -81,7 +103,7 @@ describe('Chips', () => {
});

it('should have working callbacks', (done) => {
chips = document.querySelector('.chips');
chips = document.querySelector('.chips.input-field');
let chipWasAdded = false;
let chipAddedElem = null;
let chipSelect = false;
Expand All @@ -90,6 +112,7 @@ describe('Chips', () => {
let chipDeleted = null;

M.Chips.init(chips, {
allowUserInput: true,
data: [{ id: 'One' }, { id: 'Two' }, { id: 'Three' }],
onChipAdd: (chipsEl, chipEl) => {
chipAddedElem = chipEl;
Expand Down
52 changes: 33 additions & 19 deletions src/chips.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ export interface ChipsOptions extends BaseOptions{
* @default 'material-icons'
*/
closeIconClass: string;
/**
* Specifies option to render user input field
* @default false;
*/
allowUserInput: boolean;
/**
* Callback for chip add.
* @default null
Expand All @@ -78,6 +83,7 @@ let _defaults: ChipsOptions = {
autocompleteOptions: {},
autocompleteOnly: false,
limit: Infinity,
allowUserInput: false,
onChipAdd: null,
onChipSelect: null,
onChipDelete: null
Expand Down Expand Up @@ -109,26 +115,25 @@ export class Chips extends Component<ChipsOptions> {
...options
};

this.el.classList.add('chips', 'input-field');
this.el.classList.add('chips');
this.chipsData = [];
this._chips = [];
this._setupInput();
this.hasAutocomplete = Object.keys(this.options.autocompleteOptions).length > 0;

// Set input id
if (!this._input.getAttribute('id'))
this._input.setAttribute('id', Utils.guid());

// Render initial chips
if (this.options.data.length) {
this.chipsData = this.options.data;
this._renderChips();
}
// Setup autocomplete if needed
if (this.hasAutocomplete) this._setupAutocomplete();
this._setPlaceholder();
this._setupLabel();
this._setupEventHandlers();

// Render input element, setup event handlers
if(this.options.allowUserInput) {
this.el.classList.add('input-field');
this._setupInput();
this._setupEventHandlers();
// move input to end
this.el.append(this._input);
}
}

static get defaults() {
Expand Down Expand Up @@ -165,7 +170,9 @@ export class Chips extends Component<ChipsOptions> {
}

destroy() {
this._removeEventHandlers();
if(this.options.allowUserInput) {
this._removeEventHandlers();
}
this._chips.forEach(c => c.remove());
this._chips = [];
(this.el as any).M_Chips = undefined;
Expand Down Expand Up @@ -302,17 +309,19 @@ export class Chips extends Component<ChipsOptions> {
const renderedChip = document.createElement('div');
renderedChip.classList.add('chip');
renderedChip.innerText = chip.text || <string>chip.id;
renderedChip.setAttribute('tabindex', "0");
const closeIcon = document.createElement('i');
closeIcon.classList.add(this.options.closeIconClass, 'close');
closeIcon.innerText = 'close';
// attach image if needed
if (chip.image) {
const img = document.createElement('img');
img.setAttribute('src', chip.image);
renderedChip.insertBefore(img, renderedChip.firstChild);
}
renderedChip.appendChild(closeIcon);
if(this.options.allowUserInput) {
renderedChip.setAttribute('tabindex', '0');
const closeIcon = document.createElement('i');
closeIcon.classList.add(this.options.closeIconClass, 'close');
closeIcon.innerText = 'close';
renderedChip.appendChild(closeIcon);
}
return renderedChip;
}

Expand All @@ -323,8 +332,6 @@ export class Chips extends Component<ChipsOptions> {
this.el.appendChild(chipElem);
this._chips.push(chipElem);
}
// move input to end
this.el.append(this._input);
}

_setupAutocomplete() {
Expand All @@ -347,6 +354,13 @@ export class Chips extends Component<ChipsOptions> {
this.el.append(this._input);
}
this._input.classList.add('input');
this.hasAutocomplete = Object.keys(this.options.autocompleteOptions).length > 0;
// Setup autocomplete if needed
if (this.hasAutocomplete) this._setupAutocomplete();
this._setPlaceholder();
// Set input id
if (!this._input.getAttribute('id'))
this._input.setAttribute('id', Utils.guid());
}

_setupLabel() {
Expand Down

0 comments on commit d0d4efe

Please sign in to comment.