Skip to content
Draft
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
26 changes: 25 additions & 1 deletion src/components/input-chips/input-chips.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -326,8 +326,32 @@ export class InputChips {
this.bdsChangeChips.emit({ data: this.internalChips, value: this.getLastChip() });
break;
case 'Backspace':
// Use nativeInput.value to check current input state instead of this.value
// This ensures we're checking the actual input value at the time of keydown
const currentValue = this.nativeInput?.value || '';
if (currentValue.length === 0 && this.internalChips.length) {
// Move the last chip content to the input for editing
const lastChip = this.getLastChip();
this.removeLastChip();
this.value = lastChip;
// Update the native input value to reflect the change
if (this.nativeInput) {
this.nativeInput.value = lastChip;
// Set cursor to the end of the input
setTimeout(() => {
this.nativeInput.setSelectionRange(lastChip.length, lastChip.length);
}, 0);
}
this.bdsChange.emit({ data: this.internalChips, value: this.getLastChip() });
this.bdsChangeChips.emit({ data: this.internalChips, value: this.getLastChip() });
// Prevent default backspace behavior since we handled it
event.preventDefault();
}
break;
case 'Delete':
if ((this.value === null || this.value.length <= 0) && this.internalChips.length) {
// Delete key still removes the chip without editing
const currentDeleteValue = this.nativeInput?.value || '';
if (currentDeleteValue.length === 0 && this.internalChips.length) {
this.removeLastChip();
this.bdsChange.emit({ data: this.internalChips, value: this.getLastChip() });
this.bdsChangeChips.emit({ data: this.internalChips, value: this.getLastChip() });
Expand Down
91 changes: 91 additions & 0 deletions src/components/input-chips/test/input-chips.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,4 +146,95 @@ describe('bds-input-chips e2e tests', () => {
expect(focusedElement).toBe('BDS-INPUT-CHIPS');
});
});

describe('Character deletion while typing', () => {
it('should allow deleting characters while typing a new chip', async () => {
page = await newE2EPage({
html: `<bds-input-chips label="Tags" placeholder="Type here"></bds-input-chips>`,
});

const inputElement = await page.find('bds-input-chips >>> input');

// Type some text
await inputElement.type('test123');
await page.waitForChanges();

let value = await inputElement.getProperty('value');
expect(value).toBe('test123');

// Press backspace to delete '3'
await page.keyboard.press('Backspace');
await page.waitForChanges();

value = await inputElement.getProperty('value');
expect(value).toBe('test12');

// Press backspace again to delete '2'
await page.keyboard.press('Backspace');
await page.waitForChanges();

value = await inputElement.getProperty('value');
expect(value).toBe('test1');
});

it('should move last chip to input for editing when backspace is pressed on empty input', async () => {
page = await newE2EPage({
html: `<bds-input-chips chips='["chip1", "chip2"]'></bds-input-chips>`,
});

const inputChips = await page.find('bds-input-chips');
const inputElement = await page.find('bds-input-chips >>> input');

// Input should be empty initially
let value = await inputElement.getProperty('value');
expect(value).toBe('');

// Verify we have 2 chips
let chips = await page.findAll('bds-input-chips >>> bds-chip-clickable');
expect(chips.length).toBe(2);

// Press backspace - should move last chip to input
await inputElement.focus();
await page.keyboard.press('Backspace');
await page.waitForChanges();

// Should have 1 chip now
chips = await page.findAll('bds-input-chips >>> bds-chip-clickable');
expect(chips.length).toBe(1);

// Input should now contain the removed chip's content
value = await inputElement.getProperty('value');
expect(value).toBe('chip2');
});

it('should NOT remove chip when backspace is pressed while typing', async () => {
page = await newE2EPage({
html: `<bds-input-chips chips='["chip1"]'></bds-input-chips>`,
});

const inputElement = await page.find('bds-input-chips >>> input');

// Type some text
await inputElement.type('newchip');
await page.waitForChanges();

let value = await inputElement.getProperty('value');
expect(value).toBe('newchip');

// Verify we still have 1 chip
let chips = await page.findAll('bds-input-chips >>> bds-chip-clickable');
expect(chips.length).toBe(1);

// Press backspace - should delete 'p', NOT remove chip
await page.keyboard.press('Backspace');
await page.waitForChanges();

value = await inputElement.getProperty('value');
expect(value).toBe('newchi');

// Should still have 1 chip
chips = await page.findAll('bds-input-chips >>> bds-chip-clickable');
expect(chips.length).toBe(1);
});
});
});
104 changes: 101 additions & 3 deletions src/components/input-chips/test/input-chips.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -357,26 +357,41 @@ describe('bds-input-chips', () => {
expect(changeEmitted).toBe(true);
});

it('should handle keypress Backspace to remove last chip', async () => {
it('should handle keypress Backspace to move last chip to input for editing', async () => {
const page = await newSpecPage({
components: [InputChips],
html: '<bds-input-chips value=""></bds-input-chips>',
});

const component = page.rootInstance;
component.internalChips = ['chip1', 'chip2'];

// Mock native input with empty value and setSelectionRange method
const mockInput = {
value: '',
setSelectionRange: jest.fn()
};
component.nativeInput = mockInput as any;

let changeEmitted = false;
page.root.addEventListener('bdsChange', () => {
changeEmitted = true;
});

const backspaceEvent = {
key: 'Backspace'
} as KeyboardEvent;
key: 'Backspace',
preventDefault: jest.fn()
} as any;

component.keyPressWrapper(backspaceEvent);

// Chip should be removed
expect(component.internalChips).toEqual(['chip1']);
// Value should be set to the removed chip's content
expect(component.value).toBe('chip2');
expect(mockInput.value).toBe('chip2');
// Should prevent default behavior
expect(backspaceEvent.preventDefault).toHaveBeenCalled();
expect(changeEmitted).toBe(true);
});

Expand All @@ -388,6 +403,12 @@ describe('bds-input-chips', () => {

const component = page.rootInstance;
component.internalChips = ['chip1', 'chip2'];

// Mock native input with text
const mockInput = {
value: 'some text'
};
component.nativeInput = mockInput as any;

const backspaceEvent = {
key: 'Backspace'
Expand All @@ -398,6 +419,83 @@ describe('bds-input-chips', () => {
expect(component.internalChips).toEqual(['chip1', 'chip2']);
});

it('should allow typing and deleting characters while writing a new chip', async () => {
const page = await newSpecPage({
components: [InputChips],
html: '<bds-input-chips></bds-input-chips>',
});

const component = page.rootInstance;
component.internalChips = ['chip1'];

// Mock the native input with a value
const mockInput = {
value: 'test123'
};
component.nativeInput = mockInput as any;

// User presses backspace - should NOT remove chip because input has text
const backspaceEvent = {
key: 'Backspace'
} as KeyboardEvent;

component.keyPressWrapper(backspaceEvent);

// Chips should remain unchanged because input has value
expect(component.internalChips).toEqual(['chip1']);
});

it('should handle Delete key to remove last chip without editing', async () => {
const page = await newSpecPage({
components: [InputChips],
html: '<bds-input-chips value=""></bds-input-chips>',
});

const component = page.rootInstance;
component.internalChips = ['chip1', 'chip2'];

// Mock native input with empty value
const mockInput = {
value: ''
};
component.nativeInput = mockInput as any;

const deleteEvent = {
key: 'Delete'
} as KeyboardEvent;

component.keyPressWrapper(deleteEvent);

// Should remove chip without moving to input
expect(component.internalChips).toEqual(['chip1']);
expect(component.value).toBe('');
});

it('should not remove chips with Delete when input has value', async () => {
const page = await newSpecPage({
components: [InputChips],
html: '<bds-input-chips value="typing"></bds-input-chips>',
});

const component = page.rootInstance;
component.internalChips = ['chip1', 'chip2'];

// Mock native input with text
const mockInput = {
value: 'typing'
};
component.nativeInput = mockInput as any;

const deleteEvent = {
key: 'Delete'
} as KeyboardEvent;

component.keyPressWrapper(deleteEvent);

// Should not remove chips when input has value
expect(component.internalChips).toEqual(['chip1', 'chip2']);
});

// Delimiter handling tests
it('should handle comma delimiters', async () => {
const page = await newSpecPage({
Expand Down