-
Notifications
You must be signed in to change notification settings - Fork 77
fix: Refactor SideNavigation to remove React-Router dependency #372
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
Conversation
src/SideNavigation/SideNavigation.js
Outdated
| onItemSelect: 'Callback function when user selects an item .' | ||
| }; | ||
|
|
||
| export const SideNavItem = ({children, expandedIds = [], glyph, id, isSubItem, name, onClick, onItemSelect, selected, selectedId, url, ...props}) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's rename to SideNavListItem
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
src/SideNavigation/SideNavigation.js
Outdated
| selectedId: 'The id of the selected `SideNavItem`.' | ||
| }; | ||
|
|
||
| export const SideNavGroup = ({children, className, expandedIds, onItemSelect, selectedId, title, titleProps, ...props}) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lets remove SideNavGroup, and add title prop to SideNavList
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
greg-a-smith
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have some other minor verbiage edits, but I'll save those pending the outcome of my other comments.
| return React.cloneElement(child, { | ||
| expandedIds: this.state.expandedIds, | ||
| onItemSelect: this.handleSelect, | ||
| selectedId: this.state.selectedId |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know this is the story to remove react-router, but since it has turned into basically a complete refactor, I'll comment on all aspects.
I'm having a hard time quantifying it, but sending state through all the child components as props seems wrong. It just seems like too much information is known by too many components. If "selected" and "expanded" state is to be kept in the parent component (SideNav), then there should be better ways to have children report back changes to it. It looks like a lot of the uses of it in child components is simply checking it to determine a boolean outcome. Shouldn't we just pass the needed boolean to the child component?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Chatted with @greg-a-smith about this one. We will have to continue tracking selectedId state however we can track expanded collapsed state of the Sub-SideNavList's at the SubNavListItem level.
| render() { | ||
| const { items, className, ...rest } = this.props; | ||
| SideNav.propTypes = { | ||
| children: PropTypes.node, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the structure seems to depend on the use of the other SideNav* components, should we alter the propType definitions accordingly? What about...
children: PropTypes.oneOfType([
PropTypes.instanceOf(SideNavList),
PropTypes.arrayOf(PropTypes.instanceOf(SideNavList))
])
src/SideNavigation/SideNavigation.js
Outdated
| expandedIds: expandedIds, | ||
| onItemSelect: onItemSelect, | ||
| selected: selectedId === child.props.id, | ||
| selectedId: selectedId |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment as above ... could we just pass along booleans to the child instead of the whole list?
src/SideNavigation/SideNavigation.js
Outdated
| {title} | ||
| </h1> | ||
| {sideNavList} | ||
| </div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need this <div> wrapper? It seems odd that it's included when a title exists, but not otherwise. It appears both styles tied to fd-side-nav__group already exist on fd-side-nav__list and fd-side-nav__title does not require a specific parent class. Maybe just <React.Fragment> here instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this case I think we should try to match the Fundamental HTML which does include this <div>.
| SideNavList.propTypes = { | ||
| items: PropTypes.array.isRequired, | ||
| className: PropTypes.string | ||
| children: PropTypes.node, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment as above regarding specific instances of children.
| }; | ||
|
|
||
| SideNavListItem.propTypes = { | ||
| children: PropTypes.node, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment as above regarding specific instances of children.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After trying to tackle this, there is not a great way in React to enforce what components are passed as children. For now we are ignoring this issue per a conversation with @greg-a-smith.
src/SideNavigation/SideNavigation.js
Outdated
| className: PropTypes.string, | ||
| titleProps: PropTypes.object | ||
| SideNavSubList.propTypes = { | ||
| children: PropTypes.node, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment as above regarding specific instances of children.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After trying to tackle this, there is not a great way in React to enforce what components are passed as children. For now we are ignoring this issue per a conversation with @greg-a-smith.
src/SideNavigation/SideNavigation.js
Outdated
| onClick: () => {} | ||
| }; | ||
|
|
||
| export const SideNavSubList = ({children, id, onItemSelect, open, selectedId, ...props}) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need this extra component? Semantically, isn't a sub list just another list? Maybe have an additional prop like hasParent (that could be passed in the cloneElement calls) that allows you to add different classes or do other conditional logic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This component has been refactored out.
src/index.js
Outdated
| export { ProductTileMedia } from './Tile/Tile'; | ||
| export { SearchInput } from './SearchInput/SearchInput'; | ||
| export { SideNav, SideNavList, SideNavGroup } from './SideNavigation/SideNavigation'; | ||
| export { SideNav, SideNavList, SideNavListItem, SideNavSubList } from './SideNavigation/SideNavigation'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should SideNavSubList still be exported?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
src/SideNavigation/SideNavigation.js
Outdated
|
|
||
| SideNavList.propDescriptions = { | ||
| items: 'An array of objects with keys \'id\', \'url\', \'name\', \'hasChild\', \'child\', and \'glyph\' setting the attributes of the items.' | ||
| hasParent: 'Automatically set to **true** when item has a parent `SideNavListItem`', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if this is set automatically, should it be exposed as a prop type?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have an issue where internal props have to be documented or they are left with a blank description. @greg-a-smith and I chatted about this and decided to write that these props are used for internal use only. I've opened #393 which will aim to correct this issue and would remove these prop descriptions.
| className | ||
| ); | ||
| this.state = { | ||
| expanded: this.props.expanded ? this.props.expanded : false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we will need a getDerivedStateFromProps (previously known as componentWillReceiveProps) to keep the internal state in sync since expanded is a prop.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected in ca228a2
| this.state = { | ||
| selectedItem: 'item_2', | ||
| itemStates: initialState | ||
| selectedId: props.selectedId |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we will need a getDerivedStateFromProps (previously known as componentWillReceiveProps) to keep the internal state in sync since selectedId is a prop.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected in ca228a2
| onItemSelect(e, id, hasChild); | ||
| if (hasChild) { | ||
| this.handleExpand(); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found what I think is a bug, but wanted to get thoughts on it first before creating an issue. It would be a follow-up task if it was a bug since the behavior existed previously. When click on a nav item that is the expand/collapse item, it changes the selectedId, but these items themselves shouldn't actually be "selectable". They are simply used to expand and collapse the children within it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
…SAP/fundamental-react into fix/remove-react-router-sidenav
greg-a-smith
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assuming the builds pass, I think this looks good. I am now partial author so I'll let others review my contributions, but I'll give a partial ⛵️ .
|
I can toss on a 🚢 for @greg-a-smith's contributions! Just have to wait for Coveralls 😄 |
* Save state * saving state * Adding interactivity and active item * Updating Unit Tests * Downgrading React-Router-* to devDependencies * Add Prop Descriptons * Normalized SideNavGroup into SideNavList & renamed SIdeNavSubItems to SideNavSubList * Removing SubList and Refactoring in SideNavList * Remove expanded tracking from SideNav and pushing into SideNavListItem * Linting fixes * Adding focus-trap after removing it * Package-lock was not in sync * PR feedback * Updating children prop type * Fixing issue where parent items were being selectd. Also updated documentation * Correcting testing issue * Updates to prop descriptions
Description
This PR aims to resolve #115. In order to remove the dependency on React Router from Fundamentals-React (#115), we must refactor the the following components...
Breadcrumb (feat: Remove React Router from Breadcrumbs #294)Menu (feat: Remove React Router from Menu #293)Tabs (fix: Refactor tabs to remove React-Router dependency #336)This PR is focused on SideNav and does introduce breaking changes such as...
SideNavSideNavGroup,SideNavItem, andSideNavSubItemscomponents<SideNavList>now takes<SideNavItem>instead of accepting an object through theitemsprop.<SideNavItem>no longer creates its own<Link>but instead allows it to be added via an HOC pattern.react-routerandreact-router-domtodevDependencies.See the documentation for examples of how to use
<SideNav>.