Skip to content

Conversation

@jeffredodd
Copy link
Contributor

@jeffredodd jeffredodd commented Feb 6, 2019

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...

This PR is focused on SideNav and does introduce breaking changes such as...

  • Complete Refactor of JSX components for SideNav
  • Introduction of SideNavGroup, SideNavItem, and SideNavSubItems components
  • <SideNavList> now takes <SideNavItem> instead of accepting an object through the items prop.
  • <SideNavItem> no longer creates its own <Link> but instead allows it to be added via an HOC pattern.
  • Demotion of react-router and react-router-dom to devDependencies.

See the documentation for examples of how to use <SideNav>.

@jeffredodd jeffredodd requested a review from a team February 6, 2019 06:42
onItemSelect: 'Callback function when user selects an item .'
};

export const SideNavItem = ({children, expandedIds = [], glyph, id, isSubItem, name, onClick, onItemSelect, selected, selectedId, url, ...props}) => {
Copy link
Contributor

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

selectedId: 'The id of the selected `SideNavItem`.'
};

export const SideNavGroup = ({children, className, expandedIds, onItemSelect, selectedId, title, titleProps, ...props}) => {
Copy link
Contributor

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

Copy link
Contributor

@greg-a-smith greg-a-smith left a 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
Copy link
Contributor

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?

Copy link
Contributor Author

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,
Copy link
Contributor

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))
])

expandedIds: expandedIds,
onItemSelect: onItemSelect,
selected: selectedId === child.props.id,
selectedId: selectedId
Copy link
Contributor

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?

{title}
</h1>
{sideNavList}
</div>
Copy link
Contributor

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?

Copy link
Contributor Author

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,
Copy link
Contributor

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,
Copy link
Contributor

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.

Copy link
Contributor Author

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.

className: PropTypes.string,
titleProps: PropTypes.object
SideNavSubList.propTypes = {
children: PropTypes.node,
Copy link
Contributor

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.

Copy link
Contributor Author

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.

onClick: () => {}
};

export const SideNavSubList = ({children, id, onItemSelect, open, selectedId, ...props}) => {
Copy link
Contributor

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.

Copy link
Contributor Author

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';
Copy link
Contributor

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed


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`',
Copy link
Contributor

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?

Copy link
Contributor Author

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
Copy link
Contributor

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.

Copy link
Contributor Author

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
Copy link
Contributor

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.

Copy link
Contributor Author

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();
}
Copy link
Contributor

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.

Copy link
Contributor Author

@jeffredodd jeffredodd Feb 11, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree this was an issue. Pushed 33e7b51 to fix this issue and 0835d11 to fix the corresponding test.

Copy link
Contributor

@greg-a-smith greg-a-smith left a 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 ⛵️ .

@jeffredodd
Copy link
Contributor Author

I can toss on a 🚢 for @greg-a-smith's contributions! Just have to wait for Coveralls 😄

@jeffredodd jeffredodd merged commit e6d3287 into master Feb 11, 2019
@jeffredodd jeffredodd deleted the fix/remove-react-router-sidenav branch February 11, 2019 21:43
greg-a-smith pushed a commit that referenced this pull request Mar 5, 2019
* 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Remove react router as a dependency

4 participants