palantir/blueprint

Multiline EditableText doesn't update height properly if changing controlled value on confirm

Open

#2,153 opened on Feb 19, 2018

View on GitHub
 (3 comments) (2 reactions) (0 assignees)TypeScript (20,263 stars) (2,167 forks)batch import
P3Package: coreType: bughelp wanted

Description

Bug report

  • Package version(s): 1.35.4
  • Browser and OS versions: Chrome 64.0.3282.167, OS X 10.12.6

Steps to reproduce

  1. Create an EditableText with a controlled value and multiline={true}
  2. Add some validation logic in a parent component that, e.g., resets an empty value to the last saved value onConfirm.

Actual behavior

(Going off the scenario above): The EditableText doesn't update its height to fit the newly updated value. It remains at one line tall, and the text overflows visually as in https://github.com/palantir/blueprint/issues/1610.

Likewise, e.g. if validation logic exists that reverts a 3-line value to a previously saved 1-line value onConfirm, then the EditableText erroneously remains 3 lines tall.

Expected behavior

The EditableText updates its height properly within componentWillReceiveProps.

--

Current workaround is to put something like the following in the parent component (note: this workaround is imperfect; it doesn't handle the case when the EditableText needs to shrink):

public componentDidUpdate() {
    // Blueprint's EditableText doesn't adjust its height if the number of lines changes upon changing the
    // controlled value. So we have to manage that ourselves for now. The two setTimeouts work as follows:
    //   1. Delay to the end of *this* frame.
    //   2. Delay to the end of the *next* frame.
    // Both are necessary to ensure that EditableText's children finish repainting and resizing on blur before
    // we measure here.
    setTimeout(() => {
        setTimeout(() => {
            this.updateHeight();
        });
    });
}

private updateHeight() {
    if (this.editableTextRef == null) {
        return;
    }
    const parentElement = ReactDOM.findDOMNode(this.editableTextRef) as HTMLElement;

    // Blueprint's EditableText doesn't expose a ref prop for this child, so we have to grab it via DOM APIs. Note
    // that we need to query for the -content and not the -input, because this function will be executed only after
    // leaving edit mode.
    const childElement = parentElement.querySelector(".pt-editable-content") as HTMLElement;
    if (childElement == null) {
        return;
    }

    const inputHeight = childElement.style.height;
    if (parentElement.style.height !== inputHeight) {
        parentElement.style.height = inputHeight;
    }
}

Contributor guide