Skip to content

Commit

Permalink
[core] feat(TagInput): handle delete to remove items (#3993)
Browse files Browse the repository at this point in the history
Allows users to press `DELETE` on their keyboards to delete
an item from `<TagInput />`, which in turn is used by the
`Select` family of components.

Handling `DELETE` to remove item mimics the way `DELETE` works
in a text editor, where an user can delete from the beginning
of the line forward. Reaching the end of the line, in our
case passing the last selected item in `<TagInput />` will stop
deleting. Moving the selection to the first item and deleting
from that point onwards repeatedly will delete all the selected
items.
  • Loading branch information
grancalavera authored Mar 3, 2020
1 parent dd8b43c commit ecebb8f
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 1 deletion.
3 changes: 2 additions & 1 deletion packages/core/src/components/tag-input/tag-input.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ new items. A `separator` prop is supported to allow multiple items to be added
at once; the default splits on commas and newlines.

**Tags can be removed** by clicking their <span class="@ns-icon-standard @ns-icon-cross"></span>
buttons, or by pressing <kbd>backspace</kbd> repeatedly.
buttons, or by pressing either <kbd>backspace</kbd> or <kbd>delete</kbd> repeatedly.
Pressing <kbd>delete</kbd> mimics the behavior of deleting in a text editor, where trying to delete at the end of the line will do nothing.
Arrow keys can also be used to focus on a particular tag before removing it. The
cursor must be at the beginning of the text input for these interactions.

Expand Down
10 changes: 10 additions & 0 deletions packages/core/src/components/tag-input/tagInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,8 @@ export class TagInput extends AbstractPureComponent2<ITagInputProps, ITagInputSt
}
} else if (event.which === Keys.BACKSPACE) {
this.handleBackspaceToRemove(event);
} else if (event.which === Keys.DELETE) {
this.handleDeleteToRemove(event);
}
}

Expand Down Expand Up @@ -450,6 +452,14 @@ export class TagInput extends AbstractPureComponent2<ITagInputProps, ITagInputSt
}
}

private handleDeleteToRemove(event: React.KeyboardEvent<HTMLInputElement>) {
const { activeIndex } = this.state;
if (this.isValidIndex(activeIndex)) {
event.stopPropagation();
this.removeIndexFromValues(activeIndex);
}
}

/** Remove the item at the given index by invoking `onRemove` and `onChange` accordingly. */
private removeIndexFromValues(index: number) {
const { onChange, onRemove, values } = this.props;
Expand Down
27 changes: 27 additions & 0 deletions packages/core/test/tag-input/tagInputTests.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,33 @@ describe("<TagInput>", () => {
assert.sameMembers(onRemove.args[0], [VALUES[1], 1]);
});

it("pressing left arrow key navigates active item and delete removes it", () => {
const onRemove = sinon.spy();
const wrapper = mount(<TagInput onRemove={onRemove} values={VALUES} />);
// select and remove middle item
wrapper
.find("input")
.simulate("keydown", { which: Keys.ARROW_LEFT })
.simulate("keydown", { which: Keys.ARROW_LEFT })
.simulate("keydown", { which: Keys.DELETE });

// in this case we're not moving into the previous item but
// we rather "take the place" of the item we just removed
assert.equal(wrapper.state("activeIndex"), 1);
assert.isTrue(onRemove.calledOnce);
assert.sameMembers(onRemove.args[0], [VALUES[1], 1]);
});

it("pressing delete with no selection does nothing", () => {
const onRemove = sinon.spy();
const wrapper = mount(<TagInput onRemove={onRemove} values={VALUES} />);

wrapper.find("input").simulate("keydown", { which: Keys.DELETE });

assert.equal(wrapper.state("activeIndex"), -1);
assert.isTrue(onRemove.notCalled);
});

it("pressing right arrow key in initial state does nothing", () => {
const wrapper = mount(<TagInput values={VALUES} />);
wrapper.find("input").simulate("keydown", { which: Keys.ARROW_RIGHT });
Expand Down

1 comment on commit ecebb8f

@blueprint-bot
Copy link

Choose a reason for hiding this comment

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

[core] feat(TagInput): handle delete to remove items (#3993)

Previews: documentation | landing | table

Please sign in to comment.