-
Notifications
You must be signed in to change notification settings - Fork 2.2k
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
[Labs] new TagInput component! #1232
Conversation
move code to subdirectory, use "fake" namesPreview: documentation |
/** | ||
* Click handler for remove button. | ||
* Button will only be rendered if this prop is defined. | ||
*/ | ||
onRemove?: (e: React.MouseEvent<HTMLSpanElement>) => void; | ||
onRemove?: (e: React.MouseEvent<HTMLButtonElement>) => void; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
previous type was incorrect, as the onClick
is applied to a <button>
@@ -14,12 +14,12 @@ import { isFunction } from "../../common/utils"; | |||
|
|||
import * as Classes from "../../common/classes"; | |||
|
|||
export interface ITagProps extends IProps, IIntentProps, React.HTMLProps<HTMLSpanElement> { | |||
export interface ITagProps extends IProps, IIntentProps, React.HTMLAttributes<Tag> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💥 HTMLAttributes
does not include ref
so it avoids the nasty { ref, ...htmlInputProps }
spread we've had to do! i'll go refactor other usages separately.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks and feels great overall. Just have a couple questions.
input: (ref: HTMLInputElement) => { | ||
this.inputElement = ref; | ||
const refHandler = this.props.inputProps.ref; | ||
if (Utils.isFunction(refHandler)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we use Utils.safeInvoke
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
great Q!
typings problems with safeInvoke
: refHandler
is typed string | (ref) => void
so i must use isFunction
for the type guard. (note that this is exactly what safeInvoke
does inside, but it expects functions. I think we can relax those types and maybe also make it a type guard?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
seems there's not much to be done to improve safeInvoke
usage. if i make it accept non-functions then it loses all type safety :(. adding code comment instead.
|
||
private handleContainerClick = () => { | ||
if (this.inputElement != null) { | ||
this.inputElement.focus(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I'm blurred and then click a tag's remove button, is it still expected that the input focus?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes cuz you've clicked in the .pt-input
(the thing that looks like a text input)
private handleInputKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => { | ||
const { value } = event.currentTarget; | ||
if (event.which === Keys.ENTER && value.length > 0) { | ||
Utils.safeInvoke(this.props.onAdd, value); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Elsewhere, we setState
before invoking the callback. Is the switch here intentional?
} | ||
|
||
private handleRemoveTag = (event: React.MouseEvent<HTMLSpanElement>) => { | ||
// using data attribute to simplify callback logic -- one handler for all children |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🥇
Note: aware that |
fix lintPreview: documentation | table |
input: (ref: HTMLInputElement) => { | ||
this.inputElement = ref; | ||
const refHandler = this.props.inputProps.ref; | ||
if (Utils.isFunction(refHandler)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
great Q!
typings problems with safeInvoke
: refHandler
is typed string | (ref) => void
so i must use isFunction
for the type guard. (note that this is exactly what safeInvoke
does inside, but it expects functions. I think we can relax those types and maybe also make it a type guard?)
public render() { | ||
const { inputProps, tagProps, values } = this.props; | ||
|
||
const classes = classNames(Classes.INPUT, "pt-tag-input", { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note: no constant
// .pt-dark & { | ||
// background-color: rgba($gray5, 0.2); | ||
// } | ||
// } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oops
* or a function to customize props per tag. | ||
* | ||
* If you define `onRemove` here then you will have to implement your own tag removal | ||
* handling as the `TagInput` `onRemove` prop will never be invoked. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
...TagInput's own handler will never...
}; | ||
|
||
public state: ITagInputState = { | ||
activeIndex: -1, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const NONE
|
||
private handleContainerClick = () => { | ||
if (this.inputElement != null) { | ||
this.inputElement.focus(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes cuz you've clicked in the .pt-input
(the thing that looks like a text input)
🌳 remove logsPreview: documentation | table |
add docs about keyboard interactionsPreview: documentation |
Addresses #118, #1064
Changes proposed in this pull request:
TagInput
rendersTag
s inside an input, followed by an actual text input. The container is merely styled to look like a Blueprint input; the actual editable element appears after the last tag. Clicking anywhere on the container will focus the text input for seamless interaction.Reviewers should focus on:
onAdd
andonRemove
seem sufficient in my testing--i was able to implement a basic multi-select with little trouble.Screenshot