diff --git a/common/changes/@uifabric/experiments/master_2018-05-24-22-44.json b/common/changes/@uifabric/experiments/master_2018-05-24-22-44.json new file mode 100644 index 0000000000000..1105afbb9792b --- /dev/null +++ b/common/changes/@uifabric/experiments/master_2018-05-24-22-44.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@uifabric/experiments", + "comment": "Keyboard support for the slim version of experiments/Nav component and added aria attributes", + "type": "minor" + } + ], + "packageName": "@uifabric/experiments", + "email": "sikrishn@microsoft.com" +} \ No newline at end of file diff --git a/packages/experiments/src/components/Nav/Nav.styles.ts b/packages/experiments/src/components/Nav/Nav.styles.ts index a4b1a91aa333a..5e7fdd417acd4 100644 --- a/packages/experiments/src/components/Nav/Nav.styles.ts +++ b/packages/experiments/src/components/Nav/Nav.styles.ts @@ -52,16 +52,8 @@ export const getStyles = ( margin: 0, fontSize: navFontSize, selectors: { - li: { - selectors: { - ':hover': { - selectors: { - '>div[class*=ms-Nav-FloatingNav]': { - visibility: 'visible' - } - } - } - } + 'li:hover >div': { + display: 'block' } } }, @@ -111,9 +103,7 @@ export const getStyles = ( }, navFloatingRoot: [ { - displayName: 'ms-Nav-FloatingNav', - display: 'block', - visibility: 'hidden', + display: 'none', position: 'absolute', marginLeft: navCollapsedWidth, marginTop: -navItemHeight - (!!scrollTop && scrollTop > 0 ? scrollTop : 0), diff --git a/packages/experiments/src/components/Nav/Nav.tsx b/packages/experiments/src/components/Nav/Nav.tsx index 6375d29276c1c..67610b4db1274 100644 --- a/packages/experiments/src/components/Nav/Nav.tsx +++ b/packages/experiments/src/components/Nav/Nav.tsx @@ -80,10 +80,16 @@ class NavComponent extends NavBase { return null; } + let ariaProps = {}; + let rightIconName = undefined; if (link.links && link.links.length > 0 && nestingLevel === 0) { // for the first level link, show chevron icon if there is a children - rightIconName = link.isExpanded ? 'ChevronUp' : 'ChevronDown' + rightIconName = link.isExpanded ? 'ChevronUp' : 'ChevronDown'; + + ariaProps = { + ariaExpanded: !!link.isExpanded + } } else if (link.url && link.target && link.target === '_blank') { // for external links, show an icon @@ -116,6 +122,9 @@ class NavComponent extends NavBase { dataHint={ dataHint } dataValue={ link.key } ariaLabel={ linkText } + { + ...ariaProps + } role="menu" rootClassName={ classNames.navItemRoot } leftIconName={ leftIconName } diff --git a/packages/experiments/src/components/Nav/SlimNav.tsx b/packages/experiments/src/components/Nav/SlimNav.tsx index 7554262bc1d99..cfbf1f91f8b94 100644 --- a/packages/experiments/src/components/Nav/SlimNav.tsx +++ b/packages/experiments/src/components/Nav/SlimNav.tsx @@ -22,6 +22,9 @@ import { NavLink } from './NavLink'; const getClassNames = classNamesFunction(); class SlimNavComponent extends NavBase { + // store the previous floating nav shown to close when the current floating nav shows up. + private _prevFloatingNav: any; + constructor(props: INavProps) { super(props); @@ -95,6 +98,53 @@ class SlimNavComponent extends NavBase { ev.stopPropagation(); } + private _getFloatingNav(parentElement: HTMLElement | null): HTMLDivElement | undefined { + if (!parentElement) { + return; + } + + return parentElement.querySelector('[data-floating-nav]') as HTMLDivElement; + } + + private _onKeyDown(link: INavLink, ev: React.SyntheticEvent): void { + const nativeEvent = (ev as any); + if (nativeEvent.keyCode !== 13) { + // accept only enter key to open the floating nav from slim nav + return; + } + + const a = nativeEvent.target as HTMLElement; + const li = a.parentElement; + const currentFloatingNav = this._getFloatingNav(li); + + if (!currentFloatingNav) { + return; + } + + if (this._prevFloatingNav === currentFloatingNav) { + // toggle the floating nav + if (currentFloatingNav.style && currentFloatingNav.style.display && currentFloatingNav.style.display === 'block') { + currentFloatingNav.removeAttribute('style'); + } + else { + currentFloatingNav.setAttribute('style', 'display: block'); + } + } + else { + // prev and current floating navs are different + // close the previous if there is one + if (this._prevFloatingNav) { + this._prevFloatingNav.removeAttribute('style'); + } + + // open the current one + currentFloatingNav.setAttribute('style', 'display: block'); + + // store the current as prev + this._prevFloatingNav = currentFloatingNav; + } + } + private _renderCompositeLink(link: INavLink, linkIndex: number, nestingLevel: number): React.ReactElement<{}> | null { if (!link) { return null; @@ -190,7 +240,7 @@ class SlimNavComponent extends NavBase { const classNames = getClassNames(getStyles!, { hasChildren, scrollTop: link.scrollTop }); return ( -
+
{ this._renderFloatingLinks([link], 0 /* nestingLevel */) } @@ -221,6 +271,7 @@ class SlimNavComponent extends NavBase { key={ link.key || linkIndex } onMouseEnter={ this._onLinkMouseEnterOrLeave.bind(this, link) } onMouseLeave={ this._onLinkMouseEnterOrLeave.bind(this, link) } + onKeyDown={ this._onKeyDown.bind(this, link) } title={ linkText } className={ classNames.navSlimItemRoot }>