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

[InputUnstyled] Fix externally provided inputRef is ignored #35807

Merged
merged 17 commits into from
Jan 20, 2023

Conversation

sai6855
Copy link
Contributor

@sai6855 sai6855 commented Jan 12, 2023

closes: #35783

Sorry, something went wrong.

Verified

This commit was signed with the committer’s verified signature.
LukasTy Lukas Tyla
@sai6855 sai6855 changed the title [InputUnstyled] Make ref passes through accessable [InputUnstyled] Attach value to ref passed through props Jan 12, 2023
@mui-bot
Copy link

mui-bot commented Jan 12, 2023

Messages
📖 Netlify deploy preview: https://deploy-preview-35807--material-ui.netlify.app/

Details of bundle changes

Generated by 🚫 dangerJS against 17457f5

@ZeeshanTamboli ZeeshanTamboli added package: base-ui Specific to @mui/base component: text field This is the name of the generic UI component, not the React module! bug 🐛 Something doesn't work labels Jan 12, 2023
Signed-off-by: Zeeshan Tamboli <zeeshan.tamboli@gmail.com>
Copy link
Member

@ZeeshanTamboli ZeeshanTamboli left a comment

Choose a reason for hiding this comment

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

Thanks @sai6855 ! Looks good!

@sai6855
Copy link
Contributor Author

sai6855 commented Jan 14, 2023

Thanks for the review!!

@ZeeshanTamboli ZeeshanTamboli changed the title [InputUnstyled] Attach value to ref passed through props [InputUnstyled] Fix externally provided inputRef is ignored Jan 14, 2023
Copy link
Member

@mnajdova mnajdova left a comment

Choose a reason for hiding this comment

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

Shouldn't this prop comes from the slotProps.input.ref instead? cc @michaldudak

@michaldudak
Copy link
Member

Yes, this was a general idea. In the case of the Unstyled Input, I suspect that using a ref to the underlying input may be pretty common, so we may additionally expose it in the form of inputRef in the future, but let's use slotProps.input.ref for now.

Comment on lines 77 to 79
typeof slotProps.input === 'object' && typeof slotProps.input.ref !== 'undefined'
? slotProps.input.ref
: undefined,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

this is the best i could do to make typescript happy

Copy link
Contributor

@ivp-dev ivp-dev Jan 17, 2023

Choose a reason for hiding this comment

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

why do you need to pass ref from slotPros.input.ref? It will be forked in useSlotProps for Input.

Copy link
Member

Choose a reason for hiding this comment

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

why do you need to pass ref from slotPros.input.ref?

If someone wants to provide their own ref to the InputUnstyled, they'd do it with the slotPros.input.ref.

this is the best i could do to make typescript happy

Ah, I see, we've got a bit of a chicken and egg problem here - slotProps possibly need ownerState, which needs useInput result, which depend on slotProps. We have to do this differently, then. Do not pass inputRef to useInput, but add it to input slot's useSlotProps' additionalProps object. The useSlotProps hook should take care of merging it correctly with the result of useInput.

Copy link
Contributor Author

@sai6855 sai6855 Jan 19, 2023

Choose a reason for hiding this comment

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

So it turns out we don't need to pass slotProps.input.ref in additional props. externalProps (i guess) in the same hook is taking care of passing ref to useInput. All we have to do is destructure ref and fork :)

Added test to make sure input element is attached to ref.

https://github.com/sai6855/material-ui/blob/79b43f90054c17382e596e4eee55034872d07611/packages/mui-base/src/InputUnstyled/InputUnstyled.test.tsx#L32-L39

Copy link
Member

Choose a reason for hiding this comment

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

Indeed! I forgot what functionality I added to useSlotProps ;)

@sai6855
Copy link
Contributor Author

sai6855 commented Jan 16, 2023

@ZeeshanTamboli @michaldudak updated code as suggested.

@ZeeshanTamboli
Copy link
Member

ZeeshanTamboli commented Jan 16, 2023

Yes, this was a general idea. In the case of the Unstyled Input, I suspect that using a ref to the underlying input may be pretty common, so we may additionally expose it in the form of inputRef in the future, but let's use slotProps.input.ref for now.

@mnajdova @michaldudak So then, implementation-wise, should we remove the inputRef type from UseInputParameters? That would be a breaking change. Or should we rather consider both, with the slot taking precedence?

inputRef: slotProps.input?.ref ?? inputRefProp

@mnajdova
Copy link
Member

@mnajdova @michaldudak So then, implementation-wise, should we remove the inputRef type from UseInputParameters? That would be a breaking change. Or should we rather consider both, with the slot taking precedence?

Remove it from the interface, it was anyway not handled, so I would say it was broken even before. We can anyway list is a breaking change, because of the type change.

@ivp-dev
Copy link
Contributor

ivp-dev commented Jan 19, 2023

@mnajdova @michaldudak So then, implementation-wise, should we remove the inputRef type from UseInputParameters? That would be a breaking change. Or should we rather consider both, with the slot taking precedence?

Remove it from the interface, it was anyway not handled, so I would say it was broken even before. We can anyway list is a breaking change, because of the type change.

It is extremly useful feature for developers who create custom components based on useInput hook because in other case you have no any possibility to get ref for input element but to call getInputProps. Maybe it is better continue support it, just handle ref inside the hook?

@michaldudak
Copy link
Member

michaldudak commented Jan 19, 2023

We can leave the inputRef as a hook parameter, but not include it in the component props.
Whatever is set in InputUnstyled's slotProps.input.ref, should be passed to useInput's inputRef.
Then, useInput will merge the incoming ref with the internal one.

@sai6855
Copy link
Contributor Author

sai6855 commented Jan 19, 2023

We can leave the inputRef as a hook parameter, but not include it in the component props. Whatever is set in InputUnstyled's slotProps.input.ref, should be passed to useInput's inputRef. Then, useInput will merge the incoming ref with the internal one.

Then I guess no changes is required for PR, current state of PR does exactly as you described

@michaldudak
Copy link
Member

Actually, as I commented in #35807 (comment), slotProps.input.ref can't be passed to useInput as it may not be known at the time (useSlotProps can be a function).

@ivp-dev
Copy link
Contributor

ivp-dev commented Jan 19, 2023

Please, check PR

it('should be able to attach input ref passed through props', () => {
const inputRef = React.createRef<HTMLInputElement>();
const { getByRole } = render(<InputUnstyled slotProps={{ input: { ref: inputRef } }} />);
expect(getByRole('textbox')).to.deep.equal(inputRef.current);
Copy link
Contributor Author

@sai6855 sai6855 Jan 19, 2023

Choose a reason for hiding this comment

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

Just to make sure input element is attached to ref.

previously test is written as
expect(inputRef.current).not.to.equal(null)

Copy link
Member

Choose a reason for hiding this comment

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

We could use one more test to verify the behavior of useInput alone. See https://github.com/mui/material-ui/pull/35880/files#r1082379077

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

}
const { getByRole } = render(<Input />);

expect(getByRole('textbox')).to.deep.equal(inputRef.current);
Copy link
Member

Choose a reason for hiding this comment

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

This should be the other way around:

Suggested change
expect(getByRole('textbox')).to.deep.equal(inputRef.current);
expect(inputRef.current).to.deep.equal(getByRole('textbox'));

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated

@sai6855 sai6855 requested a review from michaldudak January 20, 2023 12:37
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.

Thanks!

@michaldudak michaldudak merged commit 4aeda27 into mui:master Jan 20, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug 🐛 Something doesn't work component: text 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.

[useInput] Externally provided ref is ignored
6 participants