diff --git a/package-lock.json b/package-lock.json index 25d9b496e..da783ccf4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1880,7 +1880,7 @@ }, "chalk": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { @@ -2898,7 +2898,7 @@ }, "callsites": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "resolved": "http://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", "dev": true }, @@ -3038,7 +3038,7 @@ "dependencies": { "css-select": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", "dev": true, "requires": { @@ -4353,7 +4353,7 @@ }, "regjsgen": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", + "resolved": "http://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", "dev": true }, @@ -8168,6 +8168,7 @@ "version": "4.7.2", "resolved": "https://registry.npmjs.org/history/-/history-4.7.2.tgz", "integrity": "sha512-1zkBRWW6XweO0NBcjiphtVJVsIQ+SXF29z9DVkceeaSLVMFXHool+fdCZD4spDCfZJCILPILc3bm7Bc+HRi0nA==", + "dev": true, "requires": { "invariant": "^2.2.1", "loose-envify": "^1.2.0", @@ -8180,6 +8181,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", + "dev": true, "requires": { "loose-envify": "^1.0.0" } @@ -8206,7 +8208,8 @@ "hoist-non-react-statics": { "version": "2.5.5", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz", - "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==" + "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==", + "dev": true }, "home-or-tmp": { "version": "2.0.0", @@ -8399,7 +8402,7 @@ }, "http-proxy-middleware": { "version": "0.18.0", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz", + "resolved": "http://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz", "integrity": "sha512-Fs25KVMPAIIcgjMZkVHJoKg9VcXcC1C8yb9JUgeDvVXY0S/zgVIhMb+qVswDIgtJe2DfckMSY2d6TuTEutlk6Q==", "dev": true, "requires": { @@ -8649,6 +8652,7 @@ "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dev": true, "requires": { "loose-envify": "^1.0.0" } @@ -9095,7 +9099,8 @@ "isarray": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true }, "isemail": { "version": "3.2.0", @@ -9838,7 +9843,7 @@ }, "jest-get-type": { "version": "22.4.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-22.4.3.tgz", + "resolved": "http://registry.npmjs.org/jest-get-type/-/jest-get-type-22.4.3.tgz", "integrity": "sha512-/jsz0Y+V29w1chdXVygEKSz2nBoHoYqNShPe+QgxSNjAuP1i8+k4LbQNrfoliKej0P45sivkSCh7yiD6ubHS3w==", "dev": true }, @@ -10626,7 +10631,8 @@ "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true }, "js-yaml": { "version": "3.12.1", @@ -11080,6 +11086,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, "requires": { "js-tokens": "^3.0.0 || ^4.0.0" } @@ -11282,7 +11289,7 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true } @@ -11435,7 +11442,7 @@ }, "minimist": { "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true }, @@ -11788,7 +11795,7 @@ }, "chalk": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { @@ -12024,7 +12031,8 @@ "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true }, "object-copy": { "version": "0.1.0", @@ -12551,6 +12559,7 @@ "version": "1.7.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", + "dev": true, "requires": { "isarray": "0.0.1" } @@ -16596,6 +16605,7 @@ "version": "15.6.2", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz", "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==", + "dev": true, "requires": { "loose-envify": "^1.3.1", "object-assign": "^4.1.1" @@ -17042,6 +17052,7 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/react-router/-/react-router-4.3.1.tgz", "integrity": "sha512-yrvL8AogDh2X42Dt9iknk4wF4V8bWREPirFfS9gLU1huk6qK41sg7Z/1S81jjTrGHxa3B8R3J6xIkDAA6CVarg==", + "dev": true, "requires": { "history": "^4.7.2", "hoist-non-react-statics": "^2.5.0", @@ -17056,6 +17067,7 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-4.3.1.tgz", "integrity": "sha512-c/MlywfxDdCp7EnB7YfPMOfMD3tOtIjrQlj/CKfNMBxdmpJP8xcz5P/UAFn3JbnQCNUxsHyVVqllF9LhgVyFCA==", + "dev": true, "requires": { "history": "^4.7.2", "invariant": "^2.2.4", @@ -17364,7 +17376,7 @@ }, "css-select": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", "dev": true, "requires": { @@ -17577,7 +17589,7 @@ }, "callsites": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", + "resolved": "http://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", "dev": true }, @@ -17638,7 +17650,8 @@ "resolve-pathname": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-2.2.0.tgz", - "integrity": "sha512-bAFz9ld18RzJfddgrO2e/0S2O81710++chRMUxHjXOYKF6jTAMrUNZrEZ1PvV0zlhfjidm08iRPdTLPno1FuRg==" + "integrity": "sha512-bAFz9ld18RzJfddgrO2e/0S2O81710++chRMUxHjXOYKF6jTAMrUNZrEZ1PvV0zlhfjidm08iRPdTLPno1FuRg==", + "dev": true }, "resolve-url": { "version": "0.2.1", @@ -17776,7 +17789,7 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true } @@ -18180,7 +18193,7 @@ "dependencies": { "kind-of": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-2.0.1.tgz", + "resolved": "http://registry.npmjs.org/kind-of/-/kind-of-2.0.1.tgz", "integrity": "sha1-AY7HpM5+OobLkUG+UZ0kyPqpgbU=", "dev": true, "requires": { @@ -19231,7 +19244,7 @@ }, "supports-color": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "resolved": "http://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", "dev": true }, @@ -20641,7 +20654,8 @@ "value-equal": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-0.4.0.tgz", - "integrity": "sha512-x+cYdNnaA3CxvMaTX0INdTCN8m8aF2uY9BvEqmxuYp8bL09cs/kWVQPVGcA35fMktdOsP69IgU7wFj/61dJHEw==" + "integrity": "sha512-x+cYdNnaA3CxvMaTX0INdTCN8m8aF2uY9BvEqmxuYp8bL09cs/kWVQPVGcA35fMktdOsP69IgU7wFj/61dJHEw==", + "dev": true }, "vary": { "version": "1.1.2", @@ -20735,6 +20749,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.2.tgz", "integrity": "sha512-wbTp09q/9C+jJn4KKJfJfoS6VleK/Dti0yqWSm6KMvJ4MRCXFQNapHuJXutJIrWV0Cf4AhTdeIe4qdKHR1+Hug==", + "dev": true, "requires": { "loose-envify": "^1.0.0" } @@ -20751,7 +20766,7 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true } diff --git a/package.json b/package.json index 8c7a20274..30ae04584 100644 --- a/package.json +++ b/package.json @@ -43,9 +43,7 @@ "dependencies": { "classnames": "^2.2.6", "fiori-fundamentals": "^1.4.0", - "focus-trap-react": "^6.0.0", - "react-router": "^4.3.1", - "react-router-dom": "^4.3.1" + "focus-trap-react": "^6.0.0" }, "devDependencies": { "@babel/cli": "^7.1.5", @@ -112,6 +110,8 @@ "react-dev-utils": "^6.1.1", "react-dom": "^16.6.3", "react-markdown": "^4.0.6", + "react-router": "^4.3.1", + "react-router-dom": "^4.3.1", "react-syntax-highlighter": "^9.0.1", "react-test-renderer": "^16.6.3", "resolve": "1.8.1", diff --git a/src/SideNavigation/SideNavigation.Component.js b/src/SideNavigation/SideNavigation.Component.js index bc816c6dc..31824e0cd 100644 --- a/src/SideNavigation/SideNavigation.Component.js +++ b/src/SideNavigation/SideNavigation.Component.js @@ -1,90 +1,226 @@ import React from 'react'; import { Description, DocsText, DocsTile, Header, Import, Properties, Separator } from '../_playground'; -import { SideNav, SideNavGroup, SideNavList } from '../'; +import { Link, MemoryRouter } from 'react-router-dom'; +import { SideNav, SideNavList, SideNavListItem } from '../'; export const SideNavigationComponent = () => { - const sideNavOneLevelCode = ` - + const sideNavOneLevelCode = ` + + + + + + `; - const sideNavWithTitlesCode = ` - - - - - - - - + const sideNavWithTitlesCode = ` + + + + + Link Item + + + + + Link Item + + + + + Link Item + + + + + Link Item + + + + + + + Link Item + + + + + Link Item + + + + + Link Item + + + + + Link Item + + + + + Link Item + + + `; - const sideNavMultiLevelCode = ` - + const sideNavMultiLevelCode = ` + + + + + + + + + + + + + + + + + + + + `; - const sideNavWithIconsCode = ` - + const sideNavWithIconsCode = ` + + + + Link Item + + + + + Link Item + + + + + Link Item + + + + + `; - const sideNavCollapsedCode = ` - + const sideNavCollapsedCode = ` + + + + + + `; @@ -105,15 +241,30 @@ export const SideNavigationComponent = () => {

Side Navigation with one level

- - + + + + + + + + {sideNavOneLevelCode} @@ -123,28 +274,73 @@ export const SideNavigationComponent = () => {

Side navigation with titles

Use this to group navigation. Titles are not clickable. - - - - - - - - + + + + + + + Link Item + + + + + Link Item + + + + + Link Item + + + + + Link Item + + + + + + + Link Item + + + + + Link Item + + + + + Link Item + + + + + Link Item + + + + + Link Item + + + + + {sideNavWithTitlesCode} @@ -156,37 +352,68 @@ export const SideNavigationComponent = () => { expanded and collapsed. - - + + + + + + + + + + + + + + + + + + + + + + {sideNavMultiLevelCode} @@ -195,15 +422,41 @@ export const SideNavigationComponent = () => {

Side navigation with icons

- - + + + + + Link Item + + + + + Link Item + + + + + Link Item + + + + + {sideNavWithIconsCode} @@ -217,15 +470,31 @@ export const SideNavigationComponent = () => { the user can only see one level of navigation when collapsed. - - + + + + + + + + {sideNavCollapsedCode} diff --git a/src/SideNavigation/SideNavigation.js b/src/SideNavigation/SideNavigation.js index 6846c4fa3..b38d012e8 100644 --- a/src/SideNavigation/SideNavigation.js +++ b/src/SideNavigation/SideNavigation.js @@ -1,209 +1,277 @@ import classnames from 'classnames'; import PropTypes from 'prop-types'; -import { BrowserRouter, Link } from 'react-router-dom'; import React, { Component } from 'react'; -export const SideNav = props => { - const { icons, children, ...rest } = props; +export class SideNav extends Component { + constructor(props) { + super(props); + + this.state = { + selectedId: props.selectedId + }; + } - const sideNavClasses = classnames( - 'fd-side-nav', - { - 'fd-side-nav--icons': icons + getDerrivedStateFromProps(updatedProps, previousState) { + if (updatedProps.selectedId !== previousState.selectedId) { + return { selectedId: updatedProps.selectedId }; } - ); + } + + handleSelect = (e, id) => { + this.setState({ + selectedId: id + }); + } + + render() { + const { children, className, icons, selectedId, ...rest } = this.props; + + const sideNavClasses = classnames( + className, + 'fd-side-nav', + { + 'fd-side-nav--icons': icons + } + ); + + return ( + + ); + } +} - return ( - - ); -}; -SideNav.displayName = 'SideNav'; SideNav.propTypes = { children: PropTypes.node, - icons: PropTypes.bool + className: PropTypes.string, + icons: PropTypes.bool, + selectedId: PropTypes.string }; SideNav.propDescriptions = { - icons: 'Set to **true** enables side navigation collapsed with icons.' + icons: 'Set to **true** to only render icons for each `SideNavListItem`.', + selectedId: 'The `id` of the selected `SideNavListItem`.' }; -export class SideNavList extends Component { +SideNav.displayName = 'SideNav'; + +export class SideNavList extends React.Component { constructor(props) { super(props); + } + + render() { + const { children, className, hasParent, onItemSelect, open, selectedId, title, titleProps, ...rest } = this.props; + const sideNavListClasses = classnames({ + 'fd-side-nav__list': !hasParent, + 'fd-side-nav__sublist': hasParent + }, + className + ); + + const sideNavHeaderlasses = classnames( + 'fd-side-nav__group', + className + ); - let initialState = []; + const sideNavList = ( +
    + {React.Children.map(children, (child) => { + if (React.isValidElement(child)) { + return React.cloneElement(child, { + isSubItem: hasParent, + onItemSelect: onItemSelect, + selected: selectedId === child.props.id, + selectedId: selectedId + }); + } else { + return child; + } + })} +
+ ); - props.items.forEach(item => { - if (item.hasChild) { - let id = item.id; - let obj = {}; + if (title && !hasParent) { + return ( +
+

+ {title} +

+ {sideNavList} +
+ ); + } - obj[id] = false; - initialState.push(obj); - } - }); + return sideNavList; + } +} + +SideNavList.propTypes = { + children: PropTypes.node, + className: PropTypes.string, + hasParent: PropTypes.bool, + open: PropTypes.bool, + selectedId: PropTypes.string, + title: PropTypes.string, + titleProps: PropTypes.object, + onItemSelect: PropTypes.func +}; + +SideNavList.propDescriptions = { + hasParent: '_INTERNAL USE ONLY._', + open: '_INTERNAL USE ONLY._', + selectedId: '_INTERNAL USE ONLY._', + onItemSelect: '_INTERNAL USE ONLY._' +}; + +SideNavList.displayName = 'SideNavList'; + +export class SideNavListItem extends React.Component { + constructor(props) { + super(props); this.state = { - selectedItem: 'item_2', - itemStates: initialState + expanded: this.props.expanded ? this.props.expanded : false }; } - handleSelectChild = (e, id) => { + getDerrivedStateFromProps(updatedProps, previousState) { + if (updatedProps.expanded !== previousState.expanded) { + return { expanded: updatedProps.expanded }; + } + } + + handleExpand = () => { this.setState({ - selectedItem: id + expanded: !this.state.expanded }); }; - handleSelect = (e, id) => { - let iStates = Object.assign({}, this.state.itemStates); - iStates[id] = !iStates[id]; - this.setState({ itemStates: iStates }); - this.setState({ selectedItem: id }); - }; - - getLinkClasses = ({ id, hasChild }) => { - return classnames( - 'fd-side-nav__link', - { - 'is-selected': this.state.selectedItem === id, - 'has-child': hasChild, - 'is-expanded': this.state.itemStates[id] && hasChild - } - ); - } + render() { + const { children, glyph, id, isSubItem, name, onClick, onItemSelect, selected, selectedId, url, ...props } = this.props; + const getClasses = () => { + return classnames( + { + 'fd-side-nav__link': !isSubItem, + 'fd-side-nav__sublink': isSubItem, + 'is-selected': selected, + 'has-child': hasChild, + 'is-expanded': this.state.expanded + } + ); + }; - getSubLinkClasses = (id) => { - return classnames( - 'fd-side-nav__sublink', - { - 'is-selected': this.state.selectedItem === id + let hasChild = false; + React.Children.forEach(children, (child) => { + if (React.isValidElement(child) && child.type === SideNavList) { + hasChild = true; } - ); - } - - render() { - const { items, className, ...rest } = this.props; + }); - const sideNavListClasses = classnames( - 'fd-side-nav__list', - className - ); + const renderLink = () => { + return ( + { + onClick(e); + !hasChild && onItemSelect(e, id, hasChild); + if (hasChild) { + this.handleExpand(); + } + }}> + {glyph ? ( + + ) : null} + {name} + + ); + }; return ( - -
    - {items.map(item => { - return ( -
  • - {item.link ? ( - this.handleSelect(e, item.id)} - to={{ pathname: item.link }}> - {item.glyph ? ( - - ) : null} - {item.name} - - ) : null} - - {item.url ? ( - this.handleSelect(e, item.id)}> - {item.glyph ? ( - - ) : null} - {item.name} - +
  • + {url && renderLink()} + {React.Children.map(children, (child) => { + if (React.isValidElement(child) && child.type !== SideNavList) { + return React.cloneElement(child, { + children: ( + {glyph ? ( + ) : null} - - {item.hasChild ? ( - - ) : null} -
  • - ); - })} -
-
+ {child.props.children} + ), + className: getClasses(), + onClick: (e) => { + onClick(e); + onItemSelect(e, id, hasChild); + if (hasChild) { + this.handleExpand(); + } + } + }); + } else if (React.isValidElement(child) && child.type === SideNavList) { + return React.cloneElement(child, { + hasParent: true, + onItemSelect: onItemSelect, + open: this.state.expanded, + selectedId: selectedId + }); + } else { + return child; + } + })} + ); } } -SideNavList.displayName = 'SideNavList'; - -SideNavList.propTypes = { - items: PropTypes.array.isRequired, - className: PropTypes.string -}; -SideNavList.propDescriptions = { - items: 'An array of objects with keys \'id\', \'url\', \'name\', \'hasChild\', \'child\', and \'glyph\' setting the attributes of the items.' +SideNavListItem.propTypes = { + children: PropTypes.node, + expanded: PropTypes.bool, + glyph: PropTypes.string, + id: PropTypes.string, + isSubItem: PropTypes.bool, + name: PropTypes.string, + selected: PropTypes.bool, + selectedId: PropTypes.string, + url: PropTypes.string, + onClick: PropTypes.func, + onItemSelect: PropTypes.func }; -export const SideNavGroup = props => { - const { title, children, className, titleProps, ...rest } = props; - - const sideNavGroupClasses = classnames( - 'fd-side-nav__group', - className - ); - - return ( -
-

{title}

- {children} -
- ); +SideNavListItem.propDescriptions = { + expanded: 'Set to **true** to have this item initially render as expanded and its children items shown.', + isSubItem: '_INTERNAL USE ONLY._', + name: 'Localized text for the item (when `url` is provided).', + onItemSelect: '_INTERNAL USE ONLY._', + selected: '_INTERNAL USE ONLY._', + selectedId: '_INTERNAL USE ONLY._', + url: 'Enables use of `` element. Value to be applied to the anchor\'s `href` attribute.' }; -SideNavGroup.displayName = 'SideNavGroup'; +SideNavListItem.displayName = 'SideNavListItem'; -SideNavGroup.propTypes = { - title: PropTypes.string.isRequired, - className: PropTypes.string, - titleProps: PropTypes.object +SideNavListItem.defaultProps = { + onClick: () => {} }; diff --git a/src/SideNavigation/SideNavigation.test.js b/src/SideNavigation/SideNavigation.test.js index ed632eb4e..f0903f329 100644 --- a/src/SideNavigation/SideNavigation.test.js +++ b/src/SideNavigation/SideNavigation.test.js @@ -1,107 +1,253 @@ import { mount } from 'enzyme'; import React from 'react'; import renderer from 'react-test-renderer'; -import { SideNav, SideNavGroup, SideNavList } from './SideNavigation'; +import { BrowserRouter, Link } from 'react-router-dom'; +import { SideNav, SideNavList, SideNavListItem } from '../'; describe('', () => { const subSideNavList = ( - + + + + + + + + + + + + + + + + + + + + + + ); const oneLevelSideNav = ( - - - + + + + + + Link Item + + + + + Link Item + + + + + Link Item + + + + + Link Item + + + + + Link Item + + + + + ); const sideNavWithTitle = ( - - - - - - - - + + + + + + Link Item + + + + + Link Item + + + + + Link Item + + + + + Link Item + + + + + Link Item + + + + + + + Link Item + + + + + Link Item + + + + + Link Item + + + + + Link Item + + + + + Link Item + + + + + ); const sideNavMultiLevel = {subSideNavList}; const sideNavWithIcons = ( - - - + + + + + + Link Item + + + + + Link Item + + + + + Link Item + + + + + + + ); const sideNavCollapsed = ( - + + + + + + + ); @@ -138,61 +284,46 @@ describe('', () => { }); test('handle side nav list link click', () => { - const wrapper = mount(subSideNavList); + const wrapper = mount(sideNavMultiLevel); + const Item2 = wrapper.find({ 'id': 'item_2' }); + const Item4 = wrapper.find({ 'id': 'item_4' }); - expect(wrapper.state('itemStates')).toEqual([ - { 'item-2': false }, - { 'item-4': false } - ]); - expect(wrapper.state('selectedItem')).toEqual('item_2'); + expect(Item2.state('expanded')).toBeFalsy(); + expect(Item4.state('expanded')).toBeFalsy(); + expect(wrapper.state('selectedId')).toBeFalsy(); wrapper .find('.fd-side-nav__link') .at(1) .simulate('click'); - expect(wrapper.state('itemStates')).toEqual({ - '0': { 'item-2': false }, - '1': { 'item-4': false }, - 'item-2': true - }); - expect(wrapper.state('selectedItem')).toEqual('item-2'); + expect(Item2.state('expanded')).toBeTruthy(); + expect(Item4.state('expanded')).toBeFalsy(); wrapper .find('.fd-side-nav__link') - .at(4) + .at(3) .simulate('click'); - expect(wrapper.state('itemStates')).toEqual({ - '0': { 'item-2': false }, - '1': { 'item-4': false }, - 'item-2': true, - 'item-4': true - }); - expect(wrapper.state('selectedItem')).toEqual('item-4'); + expect(Item2.state('expanded')).toBeTruthy(); + expect(Item4.state('expanded')).toBeTruthy(); }); test('handle side nav sub link click', () => { - const wrapper = mount(subSideNavList); + const wrapper = mount(sideNavMultiLevel); wrapper .find('.fd-side-nav__sublink') .at(0) .simulate('click'); - expect(wrapper.state('itemStates')).toEqual([ - { 'item-2': false }, - { 'item-4': false } - ]); + expect(wrapper.state('selectedId')).toEqual('subitem_21'); wrapper .find('.fd-side-nav__sublink') .at(4) .simulate('click'); - expect(wrapper.state('itemStates')).toEqual([ - { 'item-2': false }, - { 'item-4': false } - ]); + expect(wrapper.state('selectedId')).toEqual('subitem_25'); }); describe('Prop spreading', () => { @@ -205,28 +336,28 @@ describe('', () => { }); test('should allow props to be spread to the SideNavList component', () => { - const items = [ - { id: 'item-1', url: '#', name: 'Link Item 1' }, - { id: 'item-2', url: '#', name: 'Link Item 2' }, - { id: 'item-3', url: '#', name: 'Link Item 3' } - ]; - const element = mount(); - - expect( - element.getDOMNode().attributes['data-sample'].value - ).toBe('Sample'); - }); - - test('should allow props to be spread to the SideNavGroup component', () => { - const element = mount(); + const element = mount( + + + + ); expect( element.getDOMNode().attributes['data-sample'].value ).toBe('Sample'); }); - test('should allow props to be spread to the SideNavGroup component\'s h1 element', () => { - const element = mount(); + test('should allow props to be spread to the SideNavList\'s h1 element', () => { + const element = mount(); expect( element.find('h1').getDOMNode().attributes['data-sample'].value diff --git a/src/SideNavigation/__snapshots__/SideNavigation.test.js.snap b/src/SideNavigation/__snapshots__/SideNavigation.test.js.snap index 6c5f186bc..342f2b20f 100644 --- a/src/SideNavigation/__snapshots__/SideNavigation.test.js.snap +++ b/src/SideNavigation/__snapshots__/SideNavigation.test.js.snap @@ -12,7 +12,7 @@ exports[` create side navigation 1`] = ` > Link Item @@ -23,7 +23,7 @@ exports[` create side navigation 1`] = ` > Link Item @@ -34,7 +34,7 @@ exports[` create side navigation 1`] = ` > Link Item @@ -45,7 +45,7 @@ exports[` create side navigation 1`] = ` > Link Item @@ -56,7 +56,7 @@ exports[` create side navigation 1`] = ` > Link Item @@ -238,45 +238,65 @@ exports[` create side navigation 3`] = ` Link Item 2
  • create side navigation 3`] = ` > Link Item 4
  • create side navigation 3`] = ` > Link Item @@ -407,7 +443,7 @@ exports[` create side navigation 4`] = ` > create side navigation 4`] = ` > create side navigation 6`] = ` Link Item 2
  • create side navigation 6`] = ` > Link Item 4
  • create side navigation 6`] = ` > Link Item diff --git a/src/index.js b/src/index.js index 3a5ea4494..c42f6f8f7 100644 --- a/src/index.js +++ b/src/index.js @@ -50,7 +50,7 @@ export { ProductTile } from './Tile/Tile'; export { ProductTileContent } from './Tile/Tile'; export { ProductTileMedia } from './Tile/Tile'; export { SearchInput } from './SearchInput/SearchInput'; -export { SideNav, SideNavList, SideNavGroup } from './SideNavigation/SideNavigation'; +export { SideNav, SideNavList, SideNavListItem } from './SideNavigation/SideNavigation'; export { Table } from './Table/Table'; export { Tab, TabGroup } from './Tabs/Tabs'; export { Token } from './Token/Token';