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.
Related
New to Stack Overflow and Lit.
I'm building something using Lit and wanted to know how do I render element/s only if a parent element is present. I am trying to add a login element dependency which will let user to use these elements only if the login element is present.
Example of what should be allowed.
<login-element>
<child-element-button> </child-element-button>
</login-element>
Example of what should not be allowed.
<child-element-button> </child-element-button>
Another problem that I have is a way for the child elements to share data, it can be to and from sibling elements, children elements, grandchildren element and so on. I do not have a solution for data sharing currently and would like to know the possible ways to achieve this.
Is there a way to achieve all of this without making <login-element> the parent element? something like this?
<login-element> </login-element>
<div> ... </div>
<my-custom-button> </my-custom-button>
<p> ... </p>
<my-colors> </my-colors>
<my-custom-footer> </my-custom-footer>
In short, I need users to be able to use custom elements only if <login-element> if present anywhere in the document and the custom elements that are present should be able to communicate between each other. For instance, a <my-colors> element should be able to send active color data to <display-image> element which will render an image with a specific background color received from `.
Currently, I read the child elements of <login-element>, copy the nodes, run loop, delete original nodes and append with those copied nodes. Additionally, in every child elements, I check if <login-element> is present in DOM or not, if present, render, else render a error element. As for passing and receiving data to and from other components, I have not tried anything.
Than you for your time.
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 nested component, what is the Polymer way to travel up the DOM or component tree in this case.
<parent-component>
<some-component>
<component-i-am-starting-from></<component-i-am-starting-from>
</some-component>
<some-other-component>
</some-other-component>
</parent-component>
I'd like to be at a deep nested component and reference any of the parent components and their models or trigger an event inside one of them. Bonus if I can access sibling components, etc.
Traveling down was easy enough with
this.$.idOfChildComponent.event()
I have tried dispatchEvent, domHost, shadowRoot, can't seem to get any further up the component tree then the direct parent or get an error back that something is undefined, is not a function, etc.
Is there a way like React to pass a reference down as a property. The docs do not seem to be helpful nor scouring the internet.
Thanks!
update
So I am not sure if this is the correct way but it works ok calling a parent function from a child function.
<parent-component id="parentComponent">
<some-component>
<component-i-am-starting-from></<component-i-am-starting-from>
</some-component>
<some-other-component>
</some-other-component>
</parent-component>
componentIAmStartingFromFunc(){
document.getElementById('parentElement').parentElementFunc()
}
However does not seem to work for siblings?
** Update **
I essentially did the same the to call the sibling event by calling the parent from one of its children, and then sent out a trigger to the sibling which is also a child component.
Calling a parent function from child ;
child-component.html (polymer 2.x)
this.dispatchEvent(new CustomEvent('upway-func', {detail: {op:"Optionally I can send some data"}}));
child-component.html (polymer 1.x)
this.fire('upway-func', {detail: {op:"Optionally I can send some data"}});
parent-component.html
...
<child-component on-upway-func='_someFunction'></child-component>
...
_someFunction(d){
console.log(d.detail.op); // "Optionally I can send some data"
}
Here this link for more detail
Have you tried using this.parentElement?
Ask yourself: if I were a span in a div, how would I get to the div?
If you want to find a particular element in the ancestor chain by selector, you can use .closest() in good browsers or https://github.com/jonathantneal/closest
The message system mentioned in another answer can work but doesn't help you with positional relationship traversing which I assume your question was requiring. Passing ids and/or objects down can be used with messages in some cases.
I’ve hit an interesting issue that doesn’t fit with Ember’s data down, actions up principle.
I have a code editor component (code-editor) sat inside a parent component (request-editor). There's a method on the editor component to insert a string at the current cursor position. The parent component includes some buttons to insert things into the editor (e.g. the current date).
I think I'm right in separating the buttons from the editor because the editor is used elsewhere without these buttons.
It obviously doesn't make sense to use a bound variable for this use-case because it's not really data, it's wanting to perform an action. I.e. {{code-editor insertText=insertText}} makes no sense.
How is it possible to effectively call codeEditorChildComponent.insert() from the parent component? I appreciate it will probably involve coupling them together but they have to be coupled for it to work anyway. The parent component is composed of the child components already.
All communications should be done using actions.
I think below is a good way. And you have code_editor property in request-editor component then could send action to code-editor component.
request-editor.hbs
{{code-editor owner=this}}
request-editor.js
actions:{
setChild(child){
this.set('code_editor', child);
}
}
code-editor.js
didInsertElement(){
this._super(...arguments);
this.get('owner').send('setChild', this);
}
I built a simplistic tab container in ReactJS using the idea that the container component keeps in its state an integer index denoting the tab pane to display and then renders only the child (from the this.props.children array) that is found at that index position.
The gist of this approach was:
const TabContainer = React.createClass({
props: {
tabNames: React.PropTypes.arrayOf(React.PropTypes.string).isRequired
},
getInitialState: function() {
return {
activeIndex: 0
};
},
setTab: function(n) {
this.setState({activeIndex: n});
},
render: function render() {
const childToRender = this.props.children[this.state.activeIndex];
return (
<div className='tab-container'>
<Tabs
tabNames= {this.props.tabNames}
active = {this.state.active}
setTab = {this.setTab}
/>
<div className='tab-pane'>
{childToRender}
</div>
</div>
);
}
});
I.e. only the indexed child is selected and rendered (I've omitted for the sake of simplicity the code handling the edge case where this.props.children is not an Array).
I found out that this approach was unsatisfactory as when the user selected different tabs, the component corresponding to the pane to render was mounted and unmounted repeatedly and any state that the panes had could not be preserved.
Ultimately, I used an approach in which all children are rendered and all panes, except the selected one, are assigned a class hidden that is then used to style those panes as: display:none. When this later solution was used my panes remained mounted even after the user clicked through the various tabs and any state they had wasn't lost as the user cycled through the tabs.
My questions are:
was the initial approach (where only a specific child is rendered) an anti-pattern or was there some other mechanism I could have used to preserve the state of the individual panes or prevent them from being unmounted?
if the initial approach was indeed an anti-pattern how can that anti-pattern be expressed more generally?
I don't think the initial approach was an antipattern at all. Choosing whether or not to mount/unmount in your logic is just dependent on your circumstances. If you want state preserved, then don't unmount. If you want a fresh element, complete with a call to getInitialState, then unmounting is the right way to go.
As an easy counterexample, consider React-Router. The Router completely unmounts/remounts components on route change. And route changing is effectively just a higher order of tabbing.
But given the situation where you want state preserved, I think your solution is the proper one. You might want to take a look at material-ui, which does something very similar in their tabbing: https://github.com/callemall/material-ui/blob/master/src/Tabs/TabTemplate.js