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

[base-ui][NumberInput] Implement numberInputReducer #38723

Merged
merged 20 commits into from
Nov 23, 2023

Conversation

mj12albert
Copy link
Member

@mj12albert mj12albert commented Aug 30, 2023

Part of #38514

This PR adds a numberInputReducer function that would be used to determine the internal value and inputValue of the useNumberInput hook. (I plan to integrate this with useNumberInput and useControllableReducer in a separate PR)

Action types:

  • clamp: apply the clamp function to inputValue
  • inputChange: update inputValue
  • increment, decrement: step the value up or down based on step, multiplier
  • incrementToMax, decrementToMin: directly set the value to max or min if they are set

The logic is mostly extracted from useNumberInput with one notable change – the value can be set to an empty string when the input is completely cleared. Previously it was just undefined but it shouldn't be used as it would change the controlled/uncontrolled state (#38513)

@mj12albert mj12albert added package: base-ui Specific to @mui/base component: number field This is the name of the generic UI component, not the React module! labels Aug 30, 2023
@mui-bot
Copy link

mui-bot commented Aug 30, 2023

Netlify deploy preview

https://deploy-preview-38723--material-ui.netlify.app/

Bundle size report

No bundle size changes (Toolpad)
No bundle size changes

Generated by 🚫 dangerJS against 83d7ec4

@mj12albert mj12albert force-pushed the base/number-input-reducer branch 8 times, most recently from ebdfda6 to e155ab0 Compare September 1, 2023 09:24
@mj12albert mj12albert marked this pull request as ready for review September 1, 2023 09:57
@mj12albert
Copy link
Member Author

@michaldudak Regarding managing the controlled/uncontrolled state, if a component is using useControllableReducer it wouldn't need to use the state provided by useControlled right? It just needs to show the error

/**
* The dirty `value` of the `input` element when it is in focus.
*/
inputValue?: string;
Copy link
Member Author

@mj12albert mj12albert Sep 1, 2023

Choose a reason for hiding this comment

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

I'm leaning towards calling this inputValue again, it's easier to associate with onInputChange

Even though this is just internal for now, I think we also need to provide the option to have inputValue as a controlled state

@michaldudak
Copy link
Member

Regarding managing the controlled/uncontrolled state, if a component is using useControllableReducer it wouldn't need to use the state provided by useControlled right?

Right. useControllableReducer is an equivalent of useControlled in a sense that it handles both the controlled and uncontrolled values. You don't need both useControllableReducer and useControlled in the same component.

@mj12albert

This comment was marked as outdated.

@mj12albert mj12albert marked this pull request as ready for review October 13, 2023 11:30
@mj12albert
Copy link
Member Author

@michaldudak I've updated this ~ in the end I decided to not pass any events into this at all to keep it simpler (I was only using very few properties on the events anyway)

It's also much clearer what each action is going to do:

clamp: 'numberInput:clamp',
inputChange: 'numberInput:inputChange',
increment: 'numberInput:increment',
decrement: 'numberInput:decrement',
decrementToMin: 'numberInput:decrementToMin',
incrementToMax: 'numberInput:incrementToMax',

Copy link
Member

@michaldudak michaldudak left a comment

Choose a reason for hiding this comment

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

Looking good in general. I've got a couple of remarks but I like the direction!

) {
const { getInputValueAsString } = context;

const parsedValue = getInputValueAsString(inputValue);
Copy link
Member

Choose a reason for hiding this comment

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

Nit: technically, a "parsed" value would be a number, while here it's just a string representing the value.
BTW, do you intend developers to provide their own implementation of getInputValueAsString? What would be the use case?

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah I haven't fully thought out the terminology yet (including getInputValueAsString), so let me rename parsedValue to numberValueAsString for now to be 100% literal 😄

The use case would be if a user wanted to accept something like $50 in the input, we would expose getInputValueAsString as a prop (with a better name) to accept a custom function to remove the $.

It would also require something like formatNewValueToDisplayValue() to add back the $ when the value is updated.

Copy link
Member

Choose a reason for hiding this comment

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

Do you think we need such a two-step process? Wouldn't it be enough if developers could override just the parsing function (that gets whatever string there is in the input and returns a number)?

Copy link
Member Author

Choose a reason for hiding this comment

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

@michaldudak I will benchmark possible alternatives, but with a currency/money input component in mind as a use-case (has currency symbol, decimals, thousands separator), I know this "parse + format" approach will work from redux-form

antd has a very similar api for their InputNumber component, though their example feels a bit weird when you use it: https://codesandbox.io/s/antd-number-input-formatter-and-parser-n74kgs?file=/src/index.js

state: State,
context: NumberInputActionContext,
applyMultiplier: boolean,
direction: StepDirection,
Copy link
Member

Choose a reason for hiding this comment

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

If we used 1 | -1 as direction, we could get rid of some code without sacrificing readability IMO (using an enum would also work).

Copy link
Member Author

Choose a reason for hiding this comment

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

How about '+' | '-' 😬

Copy link
Member

Choose a reason for hiding this comment

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

This still requires some logic other than simple multiplication, but if you feel 1 | -1 is not expressive enough, let's leave it.

Copy link
Member Author

Choose a reason for hiding this comment

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

OK I've reverted this bit – it's just that having a number as direction just made me stop and think all the time 😅

Copy link
Member

@michaldudak michaldudak left a comment

Choose a reason for hiding this comment

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

I like it! Feels like the domain-specific action names look better than event-specific ones. I'm considering refactoring the existing ones in Select and Menu.

Copy link
Member

@michaldudak michaldudak left a comment

Choose a reason for hiding this comment

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

I like it! Feels like the domain-specific action names look better than event-specific ones. I'm considering refactoring the existing ones in Select and Menu.

@github-actions github-actions bot added the PR: out-of-date The pull request has merge conflicts and can't be merged label Nov 23, 2023
@github-actions github-actions bot removed the PR: out-of-date The pull request has merge conflicts and can't be merged label Nov 23, 2023
@mj12albert mj12albert merged commit e4e4e9a into mui:master Nov 23, 2023
22 checks passed
@mj12albert mj12albert deleted the base/number-input-reducer branch November 23, 2023 09:44
mnajdova pushed a commit to mnajdova/material-ui that referenced this pull request Nov 30, 2023
mnajdova pushed a commit to mnajdova/material-ui that referenced this pull request Dec 1, 2023
mnajdova pushed a commit to mnajdova/material-ui that referenced this pull request Dec 1, 2023
mnajdova pushed a commit to mnajdova/material-ui that referenced this pull request Dec 6, 2023
mnajdova pushed a commit to mnajdova/material-ui that referenced this pull request Dec 6, 2023
mnajdova pushed a commit to mnajdova/material-ui that referenced this pull request Dec 6, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component: number field This is the name of the generic UI component, not the React module! package: base-ui Specific to @mui/base
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants