Make Material UI Autocomplete get focus programatically - javascript

I am working on a project that combines react-datasheet and Material UI Autocomplete to mimic a dropdown inside an Excel cell. The base requirement consists of allowing the user to type in or choose one of the options when the autocomplete gets focus. That is where my problem resides. Because the autocomplete is nested inside a cell, it does not get focus automatically when the parent cell is selected (for example, using the keyboard arrows).
So far, I tried using refs so that I would be able to call .current.focus() using something like the following:
const inputRef = useRef();
useEffect = (() => {
if (selected) {
inputRef.current.focus();
}
}, [selected]);
where selected is a boolean prop that I'm passing from the parent cell to the autocomplete. In other words, I'm trying to somehow make it get focus programatically when selected is true. If you have any tips or ideas to get this to work, or another potential approach I could investigate, I would appreciate it.
EDIT: Here's the component tree as shown in the React Dev Tools. Upon inspecting the autocomplete, it does not expose a focus() method. I see there are inputs down the tree but I am not clear on how I can call focus on them and if that would cause the autocomplete to get focus.
The parent (actually, ancestor) cell component. Here's where I have the selected prop.
The Material UI Autocomplete.
Inputs

#newdev I ran into the same issue and #Dekel's answer here helped solve the mystery: react material ui autocomplete element focus onclick.
TLDR: You need to add the ref to the TextField element in Autocomplete's renderInput prop.

Related

React Table - useRowSelect change select all default behavior

I am using the selection hook in my react table and I can't find a way to override the select all checkbox behavior.
When the select all checkbox is checked if you uncheck a specific row in the table it turns the isAllRowsSelected to false.
I want it to change only if the header checkbox is clicked, because I want to be able to have a state that says "all results, except the unmarked ones".
I am using server side pagination so I can't rely only on the selectedRowIds array.
This is the implementation of useRowSelect I used : here
You can add this line, it trigers on component mount.
React.useEffect(()=> {toggleAllRowsSelected()},[])
All code like this: codesandbox

Setting the boolean value of aria-expanded using React Hooks

I'm currently working on accessibility in a web application using React & Ant Design's Select component. aria-expanded is set to false string.
I want to use React useState to toggle aria-expanded="false" so that the aria label in the screen reader can read back wether the dropdown is open or closed.
The idea is to take the default state and use it in the useState hook like this:
const [selectAria, setSelectAria] = useState('false');
<Select
name="someName"
id="someID"
onSelect={(value) => handleChangeSelect(value)}
aria-label={
selectAria ? 'i am closed' : 'i am open '
}
aria-expanded={selectAria}
>
The screen reader is only able to recognize when the initial state. After I select an option, and the dropdown is closed, it does not read back the new state.
Is it possible to do this in React hooks? Is it possible to access the aria-expanded attribute in useState along with the boolean value? I haven't worked on advanced accessibility and have to stick with the antD library for this. Thanks in advance.
In Ant Design, they have provided a bunch of props with very simple names to get our things done in a very friendly manner.
You can decide the initial openness/closeness of your Select element using defaultOpen prop and, always one-to-one bound openness/closeness using open prop.
Here you want to control the open/close action in always one-to-one bound manner I guess. So your code should be like this!
const [selectAria, setSelectAria] = useState(false);
<Select
name="someName"
id="someID"
onSelect={(value) => handleChangeSelect(value)}
open={selectArea}
>
*Note: Removed your aria-label attribute as well. Refer official AntD documentation for a suitable prop API - Select Component!
**Note: If you use this open prop I mentioned above, it may never allow you to close the panel until your selectAria variable is true. So you have to carefully handle that situation as well!
***Note: This answer is referred to Ant Design version 4.7.3. Select the correct version of documentation before you refer!

Weird re-render behavior with react material-ui dialog

Background
So I build a front-end project for a delivery service, based on React and Material UI.
I was asked to use a Dialog window that will be opened when clicking on item, and there the user will have the opportunity to customize the item.
The dialog can be seen here(very simple: item photo, name, desc, and customization options):
https://ibb.co/GFkpy8Q
(Sorry for the pixelization)
The problem
I use react hooks in this project, and that why manage the Dialog's state.
Although simple, I stumbled upon few problems with how elements got re-rendered/not re-rendered(when expected):
The "checked" prop of the checkboxes uses Array.some, to see if the unique ID of the checkbox is in the state Array. The checkboxes are not being set to checked when clicking on them. The onChange prop simply pushes the checkbox's value to the state array and sets the state:
const [array, setArray] = React.useState([]);
...
<Checkbox
checked={
array.some(
item => item._id === subOption._id
)
}
onChange={() => setArray(array.push(subOption))}
/>
The onChange action works properly, so why the "checked" prop doesn't work properly?
When a checkbox is checked, I want to add a small quantity field next to it, so the user will be able to choose the quantity of the subOption he shall receive.
So I use the next well-practiced pattern:
{array.some(item => item._id === subOption._id) &&
(
<QuantityField />
)
}
The problem is that the QuantityField is not shown after the checkbox get checked.
If I exit the Dialog and enter it again, I suddenly see the checked checkbox is checked, and the QuantityField is shown next to it 😕
If I change the item's quantity with the general QuantityField you can see at the bottom left of the Dialog image, suddenly all the checked checkboxes gets unchecked 😕
The general QuantityField uses a state of it's own, and is not connected to any of the checkboxes.
From what I could see after I tried to debug the weird behavior, I can say that the render action of the Dialog component isn't working as expected. The states are updated, but doesn't trigger a re-render of the Dialog tree. Actually, it is wrong to say that, as the Dialog tree gets re-rendered, but the "checked" prop is not being re-checked during the re-render; but a complete un-mount and re-mount of the Dialog shakes the tree right.
Hope for an interesting answer. Thank you.
I would change how you are using setArray. See from Array.prototype.push() documentation:
The push() method adds one or more elements to the end of an array and returns the new length of the array.
Also using .push() on the state is not allowed because never should mutate the state directly.
Suggested solution instead:
onChange={() => setArray(prevState => [...prevState, subOption])}

How to focus on component

I am using 'keen-ui' library in my project. Here is special component for select item. I want to handle event from another component and set focus on this, but I don't know how to.
Another components, that have inside <input/> tag can be focused like this.$refs[component name].$el.children[0].children[1].focus, where children[0].children[1] is <input/> element. This is ugly, but if the component doesn't contain input tag, we can not do even this.
Examining the widgets, I see that they contain a div with tabindex="0", which means they can receive focus.
If you have a ref on the component, you should be able to do something like
const focusableEl = this.$refs.uiselect.querySelector('[tabindex="0"]');
focusableEl.dispatchEvent(new Event('focus'));
and the widget will light up. I actually did that from the console to test it out. Interestingly, blur did not work for me.

Adding React components after text selection

I have a set of React components that represent a text document. When someone selects text, I want to display a toolbar. Buttons on the toolbar will eventually change styling on the selected text of the React components.
Each phrase in the document gets it's own component that looks like (simplified):
render: function() {
return (
<span handleSelection={this.handleSelection}>
<span className="pre"></span>
<span className="phrase">text</span>
<span className="post"></span>
{this.renderSelectionToolbar}
</span>
)
},
renderSelectionToolbar: function()
return this.state.selected ? <ToolbarHTML> : '';
},
handleSelection: function() {
this.setState({selected: true});
}
I've tried getting the selection with var selection = window.getSelection(). I can get the wrapping span (start of the selection) with selection.anchorNode. I tried calling selection.anchorNode.handleSelection(), but that function doesn't exist on the html element.
How can I call a method of my React component upon text selection? Is there a better way to display a toolbar after selected text? Thanks!
Thanks to Felipe T.'s comment, I've had an idea that solves the problem.
I was already using the onMouseUp event of a parent element to pull the list of phrases that have been changed. I've been rendering a phraseId attribute to each phrase so that I can update its entry in mongodb. Now, I'm updating the mongodb entry of the first phrase in the selection with an attribute {contextToolbar: true}. Then, in my render code I build a display off of the same attribute. If the user dismisses the toolbar or un-selects the text then the attribute gets set back to false. Though, I have to remember to remove the flag if the user goes straight into selecting a new set of text.
This is working for now, but I'd love to hear a more elegant solution if you have one. Especially one that doesn't force me to save temporary state information in my document database.

Categories

Resources