I want to break the text the user inputs into the content editable container and replace the content of the container with the same text wrapped in <span> elements.
Here's my render method:
render: function() {
var children = [],
index = 0;
this.state.tokens.forEach(function(token) {
children.push(<span key={index++}>{token}</span>, <span key={index++}> </span>);
});
return <div
ref="input"
className="input"
contentEditable="true"
onKeyPress={this.keyPress}
>{children}</div>;
}
(entire example in JSFiddle)
What happens is that after the interval (600ms) from user input, when the state changes and the component is rendered, the children are added but React for some reason adds the original text that was in the container, so it kind of duplicates the text.
Another things is that if then the user selects text and deletes it, in the next update React will throw all kind of errors such as:
Uncaught TypeError: Cannot read property 'parentNode' of undefined
and
Uncaught Error: Invariant Violation: findComponentRoot(..., .0.$2):
Unable to find element. This probably means the DOM was unexpectedly
mutated (e.g., by the browser), usually due to forgetting a
when using tables or nesting or tag......
Any ideas of why this happens?
Thanks
Unfortunately contenteditable doesn't work properly with React-generated children right now: Uncaught Error when using ContentEditable="true" within Chrome.
One current workaround is to build the HTML yourself or use React.renderToStaticMarkup and use React's dangerouslySetInnerHTML, though you lose some of React's benefits by doing so.
Related
I am migrating some of my unit test cases which were previously written using Jest and Enzyme to React Testing Library. I am using Material UI's Select component and I know that, in order to open the dropdown, we have to trigger the mouseDown event on the corresponding div. Here is how I did it in Enzyme (working):
wrapper.find('[role="button"]').simulate('mousedown', { button: 0 });
I am trying to achieve the same using React Testing Library in the following manner, which is not working:
const { container, getAllByRole, getByRole } = renderComponent(mockProps);
fireEvent.mouseDown(getByRole('button')); // trigger the mouseDown on div having role=button
After this I am trying to access the listbox element which is ul element:
getByRole('listbox')
which throws an error and says:
TestingLibraryElementError: Unable to find an accessible element with the role "listbox"
There are no accessible roles. But there might be some inaccessible roles. If you wish to access them, then set the `hidden` option to `true`. Learn more about this here: https://testing-library.com/docs/dom-testing-library/api-queries#byrole
I have verified and the ul element is visible (neither it nor its parent have display none or visibility hidden)
UPDATE - 1
I have tried all the following approach to wait for element to appear in DOM:
waitFor
findByRole instead of getByRole
in both the case it throws error that
Unable to find role="listbox"
What is wrong?
If the element is not accessible at the first render this error could appear, you can try passing the option hidden to the options key in the second argument of the getByRole, if this not works you can try using findByRole, that method wait for the element, and if you want to be sure of the visibility of the element you can try adding a waitFor inside of the test.
getByRole('listbox', { options: { hidden: true } });
Make sure animations are not compromising your results. If the animations change opacity or display and they did not finish before assertion from jest... it is likely to throw an error.
I found the root cause of this issue. I am using the Select component from MUI in disablePortal mode, which make the menu list to be rendered inside the parent component instead of document body. But while MUI does that, it doesn't remove the aria-hidden attribute from the parent component's div, and because of that testing library is not able to locate the listbox (ul) element inside.
There is an issue reported here:
https://github.com/mui/material-ui/issues/19450
So as a work around, I passed the data-testid to the menu component (MenuListProps) and using it to get the listbox.
I have a <span> element that is inside of a contentEditable <div>. In certain situations, when I try to delete everything in the <div> at once with Command-Delete, or when I try to delete individual characters from the <span> element when it is the only thing inside of the <div>, I get the error in the title.
How can I go about fixing this?
I put together a sandbox example of the issue:
https://codesandbox.io/s/nostalgic-wildflower-52eul?file=/src/App.js
It throws the error under two circumstances (the keyword in the example is test, it should get highlighted in blue when you type it):
Type any string with test inside of it or just test and highlight everything and delete.
Type any string with test inside of it, delete everything else except for test, then delete any character in test.
Thanks for any help!
You most probably read the following error when you set contentEditable of your div to true:
Warning: A component is `contentEditable` and contains `children` managed by React. It is now your responsibility to guarantee that none of those nodes are unexpectedly modified or duplicated. This is probably not intentional.
The problem here is that you let React add a child node to the content editable div and remove it through the actual DOM when interacting with it and removing all of its content.
React tries to re-render, and remove the span that should not be existent on next render however, you already removed it through your keyboard input.
You should only suppress the warning if you're absolutely sure about what you're doing, as it is warning you about the exact error you're getting.
The solution you're going for will not work as you would like it to.
You could be better off trying to implement an edit mode and highlighting the words after the user saves the content. This is only an idea as I don't know exactly what you're trying to achieve.
More info on this: https://stackoverflow.com/a/49639256/7381466
Just give the key to the parent element i.e div className='app ' according to your code or add a new parent element and just give the key to that element.
and if anything changes and you get this error, just update the key of the parent elementÂ
Because updating that parent element key causes react to detach the parent element from its dom, you can avoid this error because the problem is with the child element.
In onInput just check if the elemeent with id="addItemInput" is present or not and if it is not there just update the parent element key
I am trying to implement a contenteditable div inside a stateless react component.
I keep getting the below warning:
warning.js:36 Warning: A component is `contentEditable` and contains `children` managed by React. It is now your responsibility to guarantee that none of those nodes are unexpectedly modified or duplicated. This is probably not intentional.
How do I fix this?
Also how do I read contents of div on change?
Add suppressContentEditableWarning="true" to contenteditable div.
Reference: https://github.com/facebook/draft-js/issues/81
As with any React application, browser plugins and extensions that modify
the DOM can cause Draft editors to break.
Grammar checkers, for instance, may modify the DOM within
contentEditable elements, adding styles like underlines and
backgrounds. Since React cannot reconcile the DOM if the browser does
not match its expectations, the editor state may fail to remain in
sync with the DOM.
https://github.com/facebook/draft-js/issues/53
A known error. As for reading whats in a div, assign the element an id and..
oDoc = document.getElementById("divelement");
sDefTxt = oDoc.innerHTML;
Warning: A component is `contentEditable` and contains `children` managed by React
Resolved by adding...
//...
<div
suppressContentEditableWarning={true} // <-- Add this
className="MyClass"
onClick={ ()=> { onEidtHandler() } }
onBlur={ ()=> { onSaveHandler() }
>
Editable content
</div>
//...
I have issue keeping ref of nested original element while cloned parent gets unmounted. Not sure what I am doing wrong.
Have page with structure like this
<Sortable>
<Collapsible ref='Collapsible-1'>...</Collapsible>
<Collapsible ref='Collapsible-2'>...</Collapsible>
</Sortable>
Sortable component wraps each children to Sortable__item component
When user starts sorting(dragging) one of Sortable__item components I React.cloneElement() original Sortable__item to display it as dragable shadow
It gets cloned with all children, in this scenario with Collapsible component on which Page has ref['Collapsible-1'] saved. That ref on page gets changed to this shadows ref.
Once touchEnd kicks in I update state of Sortable to not show shadow(it gets unmounted).
When it gets unmounted it also removes ref inside Page(changes to null)
ISSUE: page doesn't have ref to original Collapsible as first its got changed to shadow, and then shadow got unmounted so now its null
Quick hack/fix to solve this annoying issue. This way refs never get updated if they already exist. This is pretty bad, but I don't know how else prevent this. Anyone can point me to right direction?
let registerRef = function(name, item){
if(this.items[name]) return;
this.items[name] = item;
}
<Sortable>
<Collapsible ref={registerRef.bind(this,'Collapsible-1')}>...</Collapsible>
<Collapsible ref={registerRef.bind(this,'Collapsible-2')}>...</Collapsible>
</Sortable>
I have re-thought my solution, and changed how it works.
Idea to duplicate entire child with all components inside, only to show it as draggable shadow, is bad idea! It is performance heavy task to do and also it causes unexpected results for reactjs refs.
I have chosen other solution. When I start sorting(dragging) child I allow user to take it out from list and move it(I mean I apply display: absolute; and some css transforms to follow finger). Then I create div inside list like a dropbox to indicate where draggable item will be droped on onTouchEnd. This way is much better for performance because I am not duplicating entire item DOM tree and it doesn't cause issue with duplication of children refs.
Solved.
I have a polymer element which has a costum element inside it. I want to remove everything (parent and child) when I click on it.
The problem is that every time I close an instance of the parent element I get an error. I think it's related to the fact I'm removing an element that has an element inside.
How can I remove it correctly?
Here's the method code:
closeWindow: function (event, detail, sender) {
this.firstChild.remove();
this.remove();
}
Here's the error I'm having:
Uncaught TypeError: Cannot read property 'length' of undefined
Here's an image of the error:
This issue was fixed a few releases ago and should also work fine in Polymer 0.3.x onwards. If you run into any further issues with this.remove() within the scope of an element, please file an issue.
On polymer 1.5.0+ you do event.target.remove()