Skip to content

Commit

Permalink
Merge branch 'v2-dev' of https://github.com/materializecss/materialize
Browse files Browse the repository at this point in the history
…into modal-with-dialog
  • Loading branch information
wuda-io committed Dec 17, 2024
2 parents 48845ee + 8e59a9d commit e912223
Show file tree
Hide file tree
Showing 5 changed files with 359 additions and 136 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
70 changes: 55 additions & 15 deletions spec/tests/datepicker/datepickerSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@ describe('Datepicker Plugin', () => {
</div>
</div>`;

beforeEach(() => {
XloadHtml(fixture);
M.Datepicker.init(document.querySelectorAll('.datepicker'));
});
beforeEach(() => XloadHtml(fixture));
afterEach(() => XunloadFixtures());

describe('Datepicker', () => {
afterEach(() => {
M.Datepicker.getInstance(document.querySelector('.datepicker')).destroy();
});

it('should open and close programmatically', (done) => {
M.Datepicker.init(document.querySelectorAll('.datepicker'));
const input = document.querySelector('#datepickerInput');
const modal = document.querySelector('.datepicker-modal');
expect(modal).toBeHidden('Should be hidden before datepicker input is focused.');
Expand All @@ -39,17 +41,18 @@ describe('Datepicker Plugin', () => {
it('can have a string format', (done) => {
const input = document.querySelector('#datepickerInput');
const today = new Date();
M.Datepicker.init(input, { format: 'mm/dd/yyyy' }).open();
M.Datepicker.getInstance(input).open();
M.Datepicker.init(input, { format: 'mm/dd/yyyy' });
const datepicker = M.Datepicker.getInstance(input);
datepicker.open();
setTimeout(() => {
const day1 = document.querySelector('.datepicker-modal button[data-day="1"]');
day1.click();
document.querySelector('.datepicker-done').click();
setTimeout(() => {
const year = today.getFullYear();
let month = today.getMonth() + 1;
month = month < 10 ? `0${month}` : month;
const value = M.Datepicker.getInstance(input).toString();
expect(value).toEqual(`${month}/01/${year}`);
const month = today.getMonth() + 1;
const value = datepicker.toString();
expect(value).toEqual(`${month < 10 ? `0${month}` : month}/01/${year}`);
done();
}, 400);
}, 400);
Expand All @@ -58,20 +61,57 @@ describe('Datepicker Plugin', () => {
it('can have a format function', (done) => {
const input = document.querySelector('#datepickerInput');
const today = new Date();
const formatFn = (date) => `${date.getFullYear() - 100}-${date.getMonth() + 1}-99`;
M.Datepicker.init(input, { format: formatFn }).open();
M.Datepicker.getInstance(input).open();
const formatFn = `${today.getFullYear() - 100}-${today.getMonth() + 1}-99`;
M.Datepicker.init(input, { format: formatFn });
const datepicker = M.Datepicker.getInstance(input);
datepicker.open();
setTimeout(() => {
const day1 = document.querySelector('.datepicker-modal button[data-day="1"]');
day1.click();
document.querySelector('.datepicker-done').click();
setTimeout(() => {
const year = today.getFullYear() - 100;
const month = today.getMonth() + 1;
const value = M.Datepicker.getInstance(input).toString();
expect(value).toEqual(`${year}-${month}-99`);
expect(datepicker.toString()).toEqual(`${year}-${month < 10 ? `0${month}` : month}-99`);
done();
}, 400);
}, 400);
});

it('can change the calendar modal selected date by input', (done) => {
const input = document.querySelector('#datepickerInput');
M.Datepicker.init(input, { format: 'mm/dd/yyyy' });
const today = new Date();
let month = today.getMonth();
const year = today.getFullYear() - 44;
const day = 11;
input.value = `${month < 10 ? `0${month}` : month}/${day}/${year}`;
input.dispatchEvent(
new KeyboardEvent('change', { bubbles: true, cancelable: true })
);
keydown(input, 13);
setTimeout(() => {
expect(document.querySelector('.datepicker-modal')).toHaveClass(
'open',
'modal should be shown after input is submitted.'
);
const selectMonthElem = document.querySelector('.datepicker-select.orig-select-month');
const selectYearElem = document.querySelector('.datepicker-select.orig-select-year');
const selectedDayElem = document.querySelector(`.datepicker-row td[data-day="${day}"]`);
expect(selectMonthElem.querySelector('option[selected="selected"]').value === (month - 1).toString()).toEqual(true, `selected month should be ${month}, given value ${selectMonthElem.querySelector('option[selected="selected"]').value}`);
expect(selectYearElem.querySelector('option[selected="selected"]').value === year.toString()).toEqual(true, `selected year should be ${year}, given value ${selectYearElem.querySelector('option[selected="selected"]').value}`);
expect(selectedDayElem.classList.contains('is-selected')).toEqual(true, `selected day should be ${day}, given value ${selectedDayElem.classList}`);
done();
}, 10);
});

it('should have a date range input field if date range option is enabled', (done) => {
const input = document.querySelector('#datepickerInput');
M.Datepicker.init(input, { isDateRange: true });
setTimeout(() => {
expect(document.querySelector('.datepicker-end-date')).toExist('end date input should exist');
done();
}, 10);
});
});
});
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
Loading

0 comments on commit e912223

Please sign in to comment.