Skip to content

Commit ad1ed58

Browse files
authored
[core] fix(TagInput): disable new autoResize behavior by default (#6004)
1 parent d92e646 commit ad1ed58

File tree

4 files changed

+65
-20
lines changed

4 files changed

+65
-20
lines changed

packages/core/src/components/tag-input/resizableInput.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export const ResizableInput = forwardRef<Ref, HTMLInputProps>(function Resizable
1919
}
2020
}, [content]);
2121

22-
const { onChange, ...otherProps } = props;
22+
const { onChange, style, ...otherProps } = props;
2323

2424
const handleInputChange: React.ChangeEventHandler<HTMLInputElement> = evt => {
2525
onChange?.(evt);
@@ -28,11 +28,11 @@ export const ResizableInput = forwardRef<Ref, HTMLInputProps>(function Resizable
2828

2929
return (
3030
<>
31-
<span ref={span} className={Classes.RESIZABLE_INPUT_SPAN}>
31+
<span ref={span} className={Classes.RESIZABLE_INPUT_SPAN} aria-hidden={true}>
3232
{/* Need to replace spaces with the html character for them to be preserved */}
3333
{content.replace(/ /g, "\u00a0")}
3434
</span>
35-
<input {...otherProps} type="text" style={{ width }} onChange={handleInputChange} ref={ref} />
35+
<input {...otherProps} type="text" style={{ ...style, width }} onChange={handleInputChange} ref={ref} />
3636
</>
3737
);
3838
});

packages/core/src/components/tag-input/tagInput.tsx

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,20 @@ export interface ITagInputProps extends IntentProps, Props {
5959
*/
6060
addOnPaste?: boolean;
6161

62+
/**
63+
* Whether the component should automatically resize as a user types in the text input.
64+
* This will have no effect when `fill={true}`.
65+
*
66+
* @default false
67+
*/
68+
autoResize?: boolean;
69+
70+
/**
71+
* Optional child elements which will be rendered between the selected tags and
72+
* the text input. Rendering children is usually unnecessary.
73+
*
74+
* @default undefined
75+
*/
6276
children?: React.ReactNode;
6377

6478
/**
@@ -76,6 +90,7 @@ export interface ITagInputProps extends IntentProps, Props {
7690
/**
7791
* React props to pass to the `<input>` element.
7892
* Note that `ref` and `key` are not supported here; use `inputRef` below.
93+
* Also note that `inputProps.style.width` will be overriden if `autoResize={true}`.
7994
*/
8095
inputProps?: HTMLInputProps;
8196

@@ -204,6 +219,7 @@ export class TagInput extends AbstractPureComponent2<TagInputProps, ITagInputSta
204219
public static defaultProps: Partial<TagInputProps> = {
205220
addOnBlur: false,
206221
addOnPaste: true,
222+
autoResize: false,
207223
inputProps: {},
208224
separator: /[,\n\r]/,
209225
tagProps: {},
@@ -233,7 +249,8 @@ export class TagInput extends AbstractPureComponent2<TagInputProps, ITagInputSta
233249
private handleRef: React.Ref<HTMLInputElement> = refHandler(this, "inputElement", this.props.inputRef);
234250

235251
public render() {
236-
const { className, disabled, fill, inputProps, intent, large, leftIcon, placeholder, values } = this.props;
252+
const { autoResize, className, disabled, fill, inputProps, intent, large, leftIcon, placeholder, values } =
253+
this.props;
237254

238255
const classes = classNames(
239256
Classes.INPUT,
@@ -253,6 +270,21 @@ export class TagInput extends AbstractPureComponent2<TagInputProps, ITagInputSta
253270
const isSomeValueDefined = values.some(val => !!val);
254271
const resolvedPlaceholder = placeholder == null || isSomeValueDefined ? inputProps?.placeholder : placeholder;
255272

273+
// final props that may be sent to <input> or <ResizableInput>
274+
const resolvedInputProps = {
275+
value: this.state.inputValue,
276+
...inputProps,
277+
className: classNames(Classes.INPUT_GHOST, inputProps?.className),
278+
disabled,
279+
onChange: this.handleInputChange,
280+
onFocus: this.handleInputFocus,
281+
onKeyDown: this.handleInputKeyDown,
282+
onKeyUp: this.handleInputKeyUp,
283+
onPaste: this.handleInputPaste,
284+
placeholder: resolvedPlaceholder,
285+
ref: this.handleRef,
286+
};
287+
256288
return (
257289
<div className={classes} onBlur={this.handleContainerBlur} onClick={this.handleContainerClick}>
258290
<Icon
@@ -263,19 +295,7 @@ export class TagInput extends AbstractPureComponent2<TagInputProps, ITagInputSta
263295
<div className={Classes.TAG_INPUT_VALUES}>
264296
{values.map(this.maybeRenderTag)}
265297
{this.props.children}
266-
<ResizableInput
267-
value={this.state.inputValue}
268-
{...inputProps}
269-
onFocus={this.handleInputFocus}
270-
onChange={this.handleInputChange}
271-
onKeyDown={this.handleInputKeyDown}
272-
onKeyUp={this.handleInputKeyUp}
273-
onPaste={this.handleInputPaste}
274-
placeholder={resolvedPlaceholder}
275-
ref={this.handleRef}
276-
className={classNames(Classes.INPUT_GHOST, inputProps?.className)}
277-
disabled={disabled}
278-
/>
298+
{autoResize ? <ResizableInput {...resolvedInputProps} /> : <input {...resolvedInputProps} />}
279299
</div>
280300
{this.props.rightElement}
281301
</div>

packages/core/test/tag-input/tagInputTests.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,25 @@ describe("<TagInput>", () => {
565565
});
566566
});
567567

568+
describe("when autoResize={true}", () => {
569+
it("passes inputProps to input element", () => {
570+
const onBlur = sinon.spy();
571+
const input = mount(
572+
<TagInput autoResize={true} values={VALUES} inputProps={{ autoFocus: true, onBlur }} />,
573+
).find("input");
574+
assert.isTrue(input.prop("autoFocus"));
575+
// check that event handler is proxied
576+
const fakeEvent = { flag: "yes" };
577+
input.prop("onBlur")?.(fakeEvent as any);
578+
assert.strictEqual(onBlur.args[0][0], fakeEvent);
579+
});
580+
581+
it("renders a Tag for each value", () => {
582+
const wrapper = mount(<TagInput autoResize={true} values={VALUES} />);
583+
assert.lengthOf(wrapper.find(Tag), VALUES.length);
584+
});
585+
});
586+
568587
function pressEnterInInput(wrapper: ReactWrapper<any, any>, value: string) {
569588
wrapper.find("input").prop("onKeyDown")?.(createInputKeydownEventMetadata(value, Keys.ENTER) as any);
570589
}

packages/docs-app/src/examples/core-examples/tagInputExample.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ const VALUES = [
3737
export interface ITagInputExampleState {
3838
addOnBlur: boolean;
3939
addOnPaste: boolean;
40+
autoResize: boolean;
4041
disabled: boolean;
4142
fill: boolean;
4243
intent: Intent;
@@ -51,6 +52,7 @@ export class TagInputExample extends React.PureComponent<ExampleProps, ITagInput
5152
public state: ITagInputExampleState = {
5253
addOnBlur: false,
5354
addOnPaste: true,
55+
autoResize: false,
5456
disabled: false,
5557
fill: false,
5658
intent: "none",
@@ -65,6 +67,8 @@ export class TagInputExample extends React.PureComponent<ExampleProps, ITagInput
6567

6668
private handleAddOnPasteChange = handleBooleanChange(addOnPaste => this.setState({ addOnPaste }));
6769

70+
private handleAutoResizeChange = handleBooleanChange(autoResize => this.setState({ autoResize }));
71+
6872
private handleDisabledChange = handleBooleanChange(disabled => this.setState({ disabled }));
6973

7074
private handleFillChange = handleBooleanChange(fill => this.setState({ fill }));
@@ -118,14 +122,16 @@ export class TagInputExample extends React.PureComponent<ExampleProps, ITagInput
118122
private renderOptions() {
119123
return (
120124
<>
121-
<H5>Props</H5>
125+
<H5>Appearance props</H5>
122126
<Switch label="Large" checked={this.state.large} onChange={this.handleLargeChange} />
123127
<Switch label="Disabled" checked={this.state.disabled} onChange={this.handleDisabledChange} />
124128
<Switch label="Left icon" checked={this.state.leftIcon} onChange={this.handleLeftIconChange} />
125-
<Switch label="Add on blur" checked={this.state.addOnBlur} onChange={this.handleAddOnBlurChange} />
126-
<Switch label="Add on paste" checked={this.state.addOnPaste} onChange={this.handleAddOnPasteChange} />
127129
<Switch label="Fill container width" checked={this.state.fill} onChange={this.handleFillChange} />
128130
<IntentSelect intent={this.state.intent} onChange={this.handleIntentChange} />
131+
<H5>Behavior props</H5>
132+
<Switch label="Add on blur" checked={this.state.addOnBlur} onChange={this.handleAddOnBlurChange} />
133+
<Switch label="Add on paste" checked={this.state.addOnPaste} onChange={this.handleAddOnPasteChange} />
134+
<Switch label="Auto resize" checked={this.state.autoResize} onChange={this.handleAutoResizeChange} />
129135
<H5>Tag props</H5>
130136
<Switch
131137
label="Use minimal tags"

0 commit comments

Comments
 (0)