Skip to content
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

feat(localized-multiline-text-input): add support for isCondensed pro… #2793

Merged
merged 9 commits into from
Apr 29, 2024

Conversation

Sarah4VT
Copy link
Contributor

@Sarah4VT Sarah4VT commented Apr 23, 2024

…perty on LocalizedMultilineTextInput

Summary

Adds support for isCondensed prop on LocalizedMultilineTextInput component to display a condensed layout.

Description

Figma Design
image

Results with isCondensed toggled on
image

@Sarah4VT Sarah4VT self-assigned this Apr 23, 2024
Copy link

changeset-bot bot commented Apr 23, 2024

🦋 Changeset detected

Latest commit: a7a114f

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 96 packages
Name Type
@commercetools-uikit/localized-multiline-text-input Minor
@commercetools-uikit/localized-text-input Minor
@commercetools-uikit/input-utils Minor
@commercetools-uikit/localized-multiline-text-field Minor
@commercetools-uikit/inputs Minor
@commercetools-uikit/localized-text-field Minor
@commercetools-uikit/calendar-utils Minor
@commercetools-uikit/checkbox-input Minor
@commercetools-uikit/localized-money-input Minor
@commercetools-uikit/localized-rich-text-input Minor
@commercetools-uikit/money-input Minor
@commercetools-uikit/multiline-text-input Minor
@commercetools-uikit/number-input Minor
@commercetools-uikit/password-input Minor
@commercetools-uikit/radio-input Minor
@commercetools-uikit/rich-text-input Minor
@commercetools-uikit/rich-text-utils Minor
@commercetools-uikit/search-text-input Minor
@commercetools-uikit/selectable-search-input Minor
@commercetools-uikit/text-input Minor
@commercetools-uikit/time-input Minor
@commercetools-uikit/toggle-input Minor
@commercetools-uikit/fields Minor
@commercetools-frontend/ui-kit Minor
@commercetools-uikit/date-input Minor
@commercetools-uikit/date-range-input Minor
@commercetools-uikit/date-time-input Minor
@commercetools-uikit/money-field Minor
@commercetools-uikit/multiline-text-field Minor
@commercetools-uikit/pagination Minor
@commercetools-uikit/number-field Minor
@commercetools-uikit/password-field Minor
@commercetools-uikit/data-table-manager Minor
@commercetools-uikit/radio-field Minor
@commercetools-uikit/text-field Minor
@commercetools-uikit/time-field Minor
@commercetools-uikit/date-field Minor
@commercetools-uikit/date-range-field Minor
@commercetools-uikit/date-time-field Minor
@commercetools-uikit/design-system Minor
@commercetools-uikit/calendar-time-utils Minor
@commercetools-uikit/hooks Minor
@commercetools-uikit/i18n Minor
@commercetools-uikit/localized-utils Minor
@commercetools-uikit/utils Minor
@commercetools-uikit/accessible-hidden Minor
@commercetools-uikit/avatar Minor
@commercetools-uikit/card Minor
@commercetools-uikit/collapsible-motion Minor
@commercetools-uikit/collapsible-panel Minor
@commercetools-uikit/collapsible Minor
@commercetools-uikit/constraints Minor
@commercetools-uikit/data-table Minor
@commercetools-uikit/field-errors Minor
@commercetools-uikit/field-label Minor
@commercetools-uikit/field-warnings Minor
@commercetools-uikit/grid Minor
@commercetools-uikit/icons Minor
@commercetools-uikit/label Minor
@commercetools-uikit/link Minor
@commercetools-uikit/loading-spinner Minor
@commercetools-uikit/messages Minor
@commercetools-uikit/notifications Minor
@commercetools-uikit/primary-action-dropdown Minor
@commercetools-uikit/progress-bar Minor
@commercetools-uikit/stamp Minor
@commercetools-uikit/tag Minor
@commercetools-uikit/text Minor
@commercetools-uikit/tooltip Minor
@commercetools-uikit/view-switcher Minor
@commercetools-uikit/accessible-button Minor
@commercetools-uikit/flat-button Minor
@commercetools-uikit/icon-button Minor
@commercetools-uikit/link-button Minor
@commercetools-uikit/primary-button Minor
@commercetools-uikit/secondary-button Minor
@commercetools-uikit/secondary-icon-button Minor
@commercetools-uikit/dropdown-menu Minor
@commercetools-uikit/async-creatable-select-field Minor
@commercetools-uikit/async-select-field Minor
@commercetools-uikit/creatable-select-field Minor
@commercetools-uikit/search-select-field Minor
@commercetools-uikit/select-field Minor
@commercetools-uikit/async-creatable-select-input Minor
@commercetools-uikit/async-select-input Minor
@commercetools-uikit/creatable-select-input Minor
@commercetools-uikit/search-select-input Minor
@commercetools-uikit/select-input Minor
@commercetools-uikit/select-utils Minor
@commercetools-uikit/spacings-inline Minor
@commercetools-uikit/spacings-inset-squish Minor
@commercetools-uikit/spacings-inset Minor
@commercetools-uikit/spacings-stack Minor
@commercetools-uikit/buttons Minor
@commercetools-uikit/spacings Minor
visual-testing-app Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copy link

vercel bot commented Apr 23, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
ui-kit ✅ Ready (Inspect) Visit Preview 💬 Add feedback Apr 26, 2024 0:58am

@Sarah4VT
Copy link
Contributor Author

Sarah4VT commented Apr 23, 2024

@CarlosCortizasCT There is some weirdness with this one that I can't figure out. I'm not sure if it has to do with the 3rd party library that we use for this resizable text area. But this !important style for height is getting set somewhere that I think may be calculated based on the initial rendering. This means if you toggle isCondensed in storybook, it doesn't recalculate. Not sure if this is something I need to handle? I did confirm if I have isCondensed turned on, and refresh storybook, it does render the inputs correctly. 🤷‍♀️

Components Inputs - LocalizedMultilineTextInput ⋅ Storybook 2024-04-23 at 4 33 42 PM jpg

@Sarah4VT Sarah4VT marked this pull request as ready for review April 23, 2024 20:38
@Sarah4VT Sarah4VT requested a review from a team April 23, 2024 20:38
@Sarah4VT Sarah4VT requested review from a team, jmcreasman, kterry1 and valoriecarli and removed request for a team April 23, 2024 20:57
@kark
Copy link
Contributor

kark commented Apr 24, 2024

@CarlosCortizasCT There is some weirdness with this one that I can't figure out. I'm not sure if it has to do with the 3rd party library that we use for this resizable text area. But this !important style for height is getting set somewhere that I think may be calculated based on the initial rendering. This means if you toggle isCondensed in storybook, it doesn't recalculate. Not sure if this is something I need to handle? I did confirm if I have isCondensed turned on, and refresh storybook, it does render the inputs correctly. 🤷‍♀️

Components Inputs - LocalizedMultilineTextInput ⋅ Storybook 2024-04-23 at 4 33 42 PM jpg

@Sarah4VT After investigating this case, it appears that the observed behavior results from the underlying <TextareaAutosize> component from the external library, as you mentioned. More precisely, we've set cacheMeasurements={true}


which prevents re-renders. In my view, it's very unlikely for the isCondensed prop to change once this component is rendered, so I think this behavior is negligible.
Another option is switching to cacheMeasurements={false} but I'm not sure if this might cause any issues.

@commercetools/shield-team-design-system what is your take on that?

@chloe0592
Copy link
Contributor

I'm wondering if we actually need the cacheMeasurements={true} prop. I removed it and tested out a bit, and it seems to me it doesn't have any side effects and it solves the highlighted UI issue. 🤔

@ddouglasz
Copy link
Contributor

@commercetools/shield-team-design-system what is your take on that?

Thank you Kacper, I noticed it too and was playing around with it a little bit to be sure if switching it to false actually causes any issues. At least, so far, I have not noticed any issues.
The documentation defines the prop to Reuse previously computed measurements when computing height of textarea which seems to stop the height from updating.

@Sarah4VT
Copy link
Contributor Author

@commercetools/shield-team-design-system I went ahead and removed the cacheMeasurements value and tested it in storybook and verified that it does allow the size to update when toggling the isCondensed property now in storybook. Hopefully this does the trick. Thanks for the suggestion!

@Sarah4VT Sarah4VT force-pushed the PANGOLIN-3739-localized-multiline-text-input branch from e59adec to 0183476 Compare April 24, 2024 13:40
Copy link
Contributor

@ddouglasz ddouglasz left a comment

Choose a reason for hiding this comment

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

Looks good now 💯
Thank you @Sarah4VT

@CarlosCortizasCT
Copy link
Contributor

CarlosCortizasCT commented Apr 24, 2024

Thank you all for looking into this.

As you already spotted, the issue is because of the usage of the cacheMeasurements props which is used to enhance the TextareaAutosize performance by avoiding going to the DOM to perform the measurements calculations needed as it's an "expensive" operation that will happen in every render otherwise (eg: typing anything in the input).
I'm not sure how "expensive" this operation can be; maybe it could be seen in low-end devices.

I can think of different options for use to consider:

  • Do nothing: In this case we assume (as @kark mentioned) is very unlikely the value of the isCondensed prop will change after the component is rendered, so it would be ok to accept the new behaviour.
    • In this case I would say consumers can be confused when coming to our Storybook and seeing that prop does nothing on the component and this is something we want to avoid.
  • Expose a prop to control that behaviour: Let's expose the cacheMeasurements as part of the component's API and set it to true by default. We can explain in the docs of that prop where it can be useful to use it (We would be using it in our Storybook story so the component reacts to changing the isCondensed knob).
  • Keep track of the isCondensed prop history in the MultilineInput component: if we have a ref in that component with the last isConsensed valued, we can compare it with the new one and, only if it has changed, we set the cacheMeasurements to false.

Personally I would try to avoid removing that property altogether from the MultilineInput components because it will introduce a performance degradation from our baseline.
I believe exposing the cacheMeasurements property and having it to true by default is the most convenient solution.

@Sarah4VT
Copy link
Contributor Author

Thank you all for looking into this.

As you already spotted, the issue is because of the usage of the cacheMeasurements props which is used to enhance the TextareaAutosize performance by avoiding going to the DOM to perform the measurements calculations needed as it's an "expensive" operation that will happen in every render otherwise (eg: typing anything in the input). I'm not sure how "expensive" this operation can be; maybe it could be seen in low-end devices.

I can think of different options for use to consider:

* **Do nothing**: In this case we assume (as @kark mentioned) is very unlikely the value of the `isCondensed` prop will change after the component is rendered, so it would be ok to accept the new behaviour.
  
  * In this case I would say consumers can be confused when coming to our Storybook and seeing that prop does nothing on the component and this is something we want to avoid.

* **Expose a prop to control that behaviour**: Let's expose the `cacheMeasurements` as part of the component's API and set it to `true` by default. We can explain in the docs of that prop where it can be useful to use it (We would be using it in our Storybook story so the component reacts to changing the `isCondensed` knob).

* **Keep track of the `isCondensed` prop history in the `MultilineInput` component**: if we have a ref in that component with the last `isConsensed` valued, we can compare it with the new one and, only if it has changed, we set the `cacheMeasurements` to `false`.

Personally I would try to avoid removing that property altogether from the MultilineInput components because it will introduce a performance degradation from our baseline. I believe exposing the cacheMeasurements property and having it to true by default is the most convenient solution.

Thanks for the feedback Carlos! I agree, I was nervous to take it out, because it was obviously added for a reason and I didn't want to lose any benefits it was giving, or break some functionality we depended on.

I agree that the 2nd proposed solution could work fine, but I actually thought your 3rd solution was a little more encapsulated and took mental load off of consuming components to understand when and how to set that. Right now, we know what our one use case is for needing this value turned off, and that's in Storybook if we are toggling this value. In theory if someone else wanted to toggle this value (which I agree is very unlikely), why not handle it for them since we have to handle it for ourselves anyways. I went ahead and implemented a fix based on these thoughts. Take a look, see if you like it. If not, I can undo it and add a different solution that will match your second proposal.

@Sarah4VT Sarah4VT force-pushed the PANGOLIN-3739-localized-multiline-text-input branch from 308fec2 to 13593a2 Compare April 24, 2024 19:25
Copy link
Contributor

@ddouglasz ddouglasz left a comment

Choose a reason for hiding this comment

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

Some minor feedback it we would be going with this approach.

@@ -52,6 +59,20 @@ const MultilineInput = (props: TMultiLineInputProps) => {
const { onHeightChange } = props;
const ref = useRef<HTMLTextAreaElement | null>(null);

const [prevIsCondensed, setPrevIsCondensed] = useState(props.isCondensed);
const [cacheMeasurements, setCacheMeasurements] = useState(true);
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
const [cacheMeasurements, setCacheMeasurements] = useState(true);
const [isCacheMeasurements, setIsCacheMeasurements] = useState(true);

// cacheMeasurements is set to false. So let's set cacheMeasurements to true by default to get the performance
// benefits, unless isCondensed value has changed, in which case we will set it to false for this render.
if (props.isCondensed !== prevIsCondensed) {
setCacheMeasurements(false);
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
setCacheMeasurements(false);
setIsCacheMeasurements(false);

if (props.isCondensed !== prevIsCondensed) {
setCacheMeasurements(false);
} else {
setCacheMeasurements(true);
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
setCacheMeasurements(true);
setIsCacheMeasurements(true);

@@ -103,7 +124,7 @@ const MultilineInput = (props: TMultiLineInputProps) => {
role="textbox"
minRows={MIN_ROW_COUNT}
maxRows={props.isOpen ? undefined : MIN_ROW_COUNT}
cacheMeasurements={true}
cacheMeasurements={cacheMeasurements}
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
cacheMeasurements={cacheMeasurements}
cacheMeasurements={isCacheMeasurements}

@CarlosCortizasCT
Copy link
Contributor

CarlosCortizasCT commented Apr 25, 2024

Thanks @Sarah4VT for your feedback 👍

In theory if someone else wanted to toggle this value (which I agree is very unlikely), why not handle it for them since we have to handle it for ourselves anyways.

My argument to avoid this is that any lines of code we add to a component increases its complexity and, thus, its maintainability.
I believe we should balance that with the features we provide and, in this case, since we (more or less) agree is unlikely consumers would need that, exposing the property would be (in my opinion) a good balance between complexity added to the component in comparison with the requirement.

Copy link
Contributor

@kterry1 kterry1 left a comment

Choose a reason for hiding this comment

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

Nice work! Tested it on the preview branch and it works as expected.

@Sarah4VT Sarah4VT force-pushed the PANGOLIN-3739-localized-multiline-text-input branch from 13593a2 to 16d0263 Compare April 25, 2024 18:12
@Sarah4VT
Copy link
Contributor Author

Thanks @Sarah4VT for your feedback 👍

In theory if someone else wanted to toggle this value (which I agree is very unlikely), why not handle it for them since we have to handle it for ourselves anyways.

My argument to avoid this is that any lines of code we add to a component increases its complexity and, thus, its maintainability. I believe we should balance that with the features we provide and, in this case, since we (more or less) agree is unlikely consumers would need that, exposing the property would be (in my opinion) a good balance between complexity added to the component in comparison with the requirement.

@CarlosCortizasCT I made the changes to move cacheMeasurements out to a prop on the component to allow ultimate control for consumers and set it to true by default. Let me know if I need to change anything else!

…ensed by reducing value padding and label line height
…llow toggling isCondensed property to affect input height immediately
…based on whether isCondensed value has been toggled
@Sarah4VT Sarah4VT force-pushed the PANGOLIN-3739-localized-multiline-text-input branch from 16d0263 to 13dbaf4 Compare April 25, 2024 18:15
@Sarah4VT
Copy link
Contributor Author

Some minor feedback it we would be going with this approach.

I ended up undoing the implementation due to Carlos's feedback, so these changes were not applied as the code was removed.

@CarlosCortizasCT
Copy link
Contributor

I ended up undoing the implementation due to Carlos's feedback, so these changes were not applied as the code was removed.

Thanks @Sarah4VT 🙇

I've just pushed a small commit in order to use the component's default props better.

Also, bear in mind we're currently not respecting the expected left margin of the text input value when the component is in condensed mode.

Design
image

Storybook
image

@@ -69,7 +69,7 @@ storiesOf('Components|Inputs', module)
}
defaultExpandMultilineText={defaultExpandMultilineText}
isAutofocussed={boolean('isAutofocussed', false)}
cacheMeasurements={boolean('cacheMeasurements', true)}
cacheMeasurements={boolean('cacheMeasurements', false)}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@CarlosCortizasCT Do you typically set the default value in storybook to false even if the default value in the component is true?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think it's better to use false here because, otherwise, the isCondensed check won't work and users won't know why.

…put back to original value and only reduce top/bottom padding when isCondensed
@Sarah4VT
Copy link
Contributor Author

I ended up undoing the implementation due to Carlos's feedback, so these changes were not applied as the code was removed.

Thanks @Sarah4VT 🙇

I've just pushed a small commit in order to use the component's default props better.

Also, bear in mind we're currently not respecting the expected left margin of the text input value when the component is in condensed mode.

Design image

Storybook image

@CarlosCortizasCT I fixed the left/right padding to go back to the original value and only reduced the top/bottom padding on the input when isCondensed is true. Good catch!

@CarlosCortizasCT CarlosCortizasCT requested a review from FilPob April 26, 2024 07:16
@FilPob
Copy link

FilPob commented Apr 26, 2024

Thanks for all the work! Looks good from my side

@CarlosCortizasCT
Copy link
Contributor

Something that I wanted to check with @FilPob is the horizontal padding on the text in the input.
In the Figma file, this padding is 16px but we already had 8px in the component. I think this was wrong as the other inputs also use 16px.

I think we should take the opportunity to also adjust that.

Filip, what do you think?

@FilPob
Copy link

FilPob commented Apr 26, 2024

@CarlosCortizasCT true! good catch. should be 16px for all inputs

… 16px to match other inputs, regardless of isCondensed
@Sarah4VT
Copy link
Contributor Author

@CarlosCortizasCT true! good catch. should be 16px for all inputs

@CarlosCortizasCT @FilPob I made the change to update the horizontal padding and updated the screenshot in the description.

@FilPob
Copy link

FilPob commented Apr 26, 2024

@Sarah4VT awesome, looks good. thanks :)

Copy link
Contributor

@CarlosCortizasCT CarlosCortizasCT left a comment

Choose a reason for hiding this comment

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

Looks good to me 👍

Thanks Sarah!

@CarlosCortizasCT CarlosCortizasCT merged commit d72e043 into main Apr 29, 2024
7 checks passed
@CarlosCortizasCT CarlosCortizasCT deleted the PANGOLIN-3739-localized-multiline-text-input branch April 29, 2024 07:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants