Esri / calcite-design-system

A monorepo containing the packages for Esri's Calcite Design System
https://developers.arcgis.com/calcite-design-system/
Other
279 stars 76 forks source link

Closable tabs issues #7155

Closed RSantosGIS closed 1 month ago

RSantosGIS commented 1 year ago

Actual Behavior

edit: please see conversation for description of additional issue with navigation between tabs also breaking, related to this same root cause.

I started using the closable tabs recently and overall I think it's great work. I observed a few issues during my time getting things setup (this is in React):

MicrosoftTeams-image (9)

Expected Behavior

tabIds = tabDomIndexes.reduce((ids, indexInDOM, registryIndex) => {
        if (this.tabs[registryIndex]) {
          ids[indexInDOM] = this.tabs[registryIndex].id;
        }
        return ids;
      }, []);

Reproduction Sample

ArcGIS Knowledge Studio https://codesandbox.io/s/summer-mountain-y3jcf3?file=/src/App.js

Reproduction Steps

To reproduce in the sandbox updated to 1.5.1:

  1. Select tab labeled "Select Me"
  2. Close the tab labeled "... then close me"
  3. After closing a tab, you'll get the error message referenced in the original issue message
  4. After closing the error message, you will see that you cannot navigate to the tab to the right (123) until your first re-click on "Select Me"

To reproduce in Studio:

For the events:

For the null access error:

Reproduction Version

1.4.2

Relevant Info

No response

Regression?

No response

Priority impact

p3 - want for upcoming milestone

Impact

I can work around the lack of events firing using other handlers, and we are currently just suppressing the error from the null access. Because of the way the code is written where the error is occurring inside a series of async functions that are called for each tab index, the fact that the call fails with an uncontrolled exception for the tab that disappeared from the DOM doesn't actually break anything for us... I think. Obviously it makes me nervous 😄

Esri team

ArcGIS Knowledge

geospatialem commented 1 year ago

@RSantosGIS Could you put together a code sample for us to troubleshoot using a CodeSandbox Calcite and React template?

RSantosGIS commented 1 year ago

@geospatialem Unfortunately, I cannot right now. We are in the home stretch to code freeze for Studio and the best I can do is write up Calcite issues when I find them in the course of our work. I'd be happy to hop on a call to walk someone through it if it's helpful.

RSantosGIS commented 1 year ago

@geospatialem This is become a bigger issue for us because there appears to be other bugged behavior associated with closing a tab when it is part of a dynamically generated tab list from state. I made a sandbox that shows a simple case of things breaking here, and will add it to the issue: https://codesandbox.io/s/suspicious-elion-hk8tgf?file=/src/App.js https://codesandbox.io/s/funny-sound-6vn45x?file=/src/App.js

RSantosGIS commented 1 year ago

I'm also now observing behavior where if I close a tab to the left of the active tab, the user cannot navigate to the tab immediately to the right of the active tab without first clicking on some other tab. There appears to be some kind of hidden index tracking inside tabs that is getting messed up. So for example, if your active tab is in slot 4 and you close a tab in slot 2, the active tab moves into slot 3 and you cannot now open the tab in slot 4.

Unfortunately, I cannot produce a sandbox for this beyond the one I already made because the first error I reported already crashes the sample. But I believe it is almost certainly also the same root cause - tab closing doesn't know how to properly handle the tab also being removed from the DOM

mpriour commented 1 year ago

I found a couple of issues in the sandbox that where making the behavior strange. Here are my corrections that work https://codesandbox.io/s/upbeat-fast-yfwn23

  1. .splice does NOT return the modified array but the deleted items, so even if everything had re-rendered as expected, the onTabClose function was setting the new tab config state to the deleted item and not the updated array.
  2. You should never directly modify non-primitive state objects even if it is to immediately set the state to the newly modified object / array / etc.... There are some new array methods that are now supported or will be fully supported very soon that will make this easier. However, for now you need to at least make a shallow copy of the array before modifying it and then set the state to the modified copy.
  3. The REAL workaround / solution. Add a key parameter to the <CalciteTabs> component that is unique to the configured tabs. In this example, I simply joined the tab title strings together. The reason you need to do this is that you are modifying the internal parts of a web component that the web component cares about and tracks. Therefore you need to instruct react to fully re-render the web component itself. Without the key on the parent web component element, you are getting the web component internal state and the dom state out of sync and then things don't work anymore. I've had this issue with other web components that keep track of their children via slot attributes. Changing a child does not reset the parent's state of trigger a re-render of the parent directly.
RSantosGIS commented 1 year ago

Hey @mpriour , thanks for taking a look at this. Summarizing our conversation on teams and adding a little more:

I still feel like this is something you should take a look at. I would expect when closing the tab that no part of the CalciteTabs component would still rely on the tab existing internally. The fact that if you close a tab and then it disappears from the DOM the breaks the navigation for the tab immediately to the right of the active tab still feels like a bug, because a full re-render of the component to resync everything causes you to lose capabilities.

RSantosGIS commented 1 year ago

Updated my sample with Matt's suggested change, and it still demonstrates both problems. Repro steps:

jcfranco commented 1 year ago

@RSantosGIS Could you update the repro case or the steps above to make sure they're in sync? The latest sample looks as follows:

Screenshot 2023-06-28 at 3 49 49 PM

Just making sure we're reproducing accurately.

RSantosGIS commented 1 year ago

@jcfranco ugh, no it's not. I will do it again and update

RSantosGIS commented 1 year ago

@jcfranco finally got around to updating this, sorry I've been slammed with Studio's end of iteration. https://codesandbox.io/s/funny-sound-6vn45x?file=/src/App.js

Please let me know if the example or anything about what we are trying to achieve is unclear

RSantosGIS commented 1 year ago

@mpriour suggested that changing CalciteTab before CalciteTabTitle resolves the null access error, and I confirmed that indeed it does. However, it does not resolve the navigation problem: navigate to tab 3 of 4 close tab 2 attempting to navigate to the tab that now occupies slot 3 (since they all moved one to the left) does not work

Still, it might be worth changing the docs in the interim to at least recommend the order change.

geospatialem commented 1 year ago

Updated the repro case to remove the extraneous components in the sandbox and updated to 1.5.1. Triaging will occur in the next few weeks.

eriklharper commented 3 months ago

This issue isn't specific to React: https://codepen.io/eriklharper/pen/JjqXKxO

github-actions[bot] commented 1 month ago

Installed and assigned for verification.

geospatialem commented 1 month ago

Verified with https://codepen.io/eriklharper/pen/JjqXKxO in 2.11.0-next.21. 🎉