diff --git a/common/changes/office-ui-fabric-react/nav-scss2ms-pt2_2018-02-21-20-05.json b/common/changes/office-ui-fabric-react/nav-scss2ms-pt2_2018-02-21-20-05.json new file mode 100644 index 0000000000000..3434a87a0341a --- /dev/null +++ b/common/changes/office-ui-fabric-react/nav-scss2ms-pt2_2018-02-21-20-05.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "office-ui-fabric-react", + "comment": "Converting SCSS to MergeStyles step 2 - style conversion", + "type": "minor" + } + ], + "packageName": "office-ui-fabric-react", + "email": "v-brgarl@microsoft.com" +} \ No newline at end of file diff --git a/packages/office-ui-fabric-react/src/components/Nav/Nav.base.tsx b/packages/office-ui-fabric-react/src/components/Nav/Nav.base.tsx index da9be9817ab85..56292b2c6f85a 100644 --- a/packages/office-ui-fabric-react/src/components/Nav/Nav.base.tsx +++ b/packages/office-ui-fabric-react/src/components/Nav/Nav.base.tsx @@ -2,22 +2,22 @@ import * as React from 'react'; import { autobind, BaseComponent, - css, + classNamesFunction, + customizable, divProperties, - getNativeProps, - getRTL + getNativeProps } from '../../Utilities'; import { FocusZone, FocusZoneDirection } from '../../FocusZone'; -import { ActionButton, IButtonStyles } from '../../Button'; +import { ActionButton } from '../../Button'; import { Icon } from '../../Icon'; -import * as stylesImport from './Nav.scss'; -const styles: any = stylesImport; -import { AnimationClassNames, mergeStyles } from '../../Styling'; +import { buttonStyles } from './Nav.styles'; import { INav, INavProps, INavLinkGroup, - INavLink + INavLink, + INavStyles, + INavStyleProps } from './Nav.types'; // The number pixels per indentation level for Nav links. @@ -26,9 +26,6 @@ const _indentationSize: number = 14; // The number of pixels of left margin const _baseIndent: number = 3; -// The number of pixels of padding to add to the far side of the button (allows ellipsis to happen) -const _farSidePadding: number = 20; - // global var used in _isLinkSelectedKey let _urlResolver: HTMLAnchorElement | undefined; @@ -37,12 +34,15 @@ export function isRelativeUrl(url: string): boolean { return !!url && !/^[a-z0-9+-.]:\/\//i.test(url); } +const getClassNames = classNamesFunction(); + export interface INavState { isGroupCollapsed?: { [key: string]: boolean }; isLinkExpandStateChanged?: boolean; selectedKey?: string; } +@customizable('Nav', ['theme']) export class NavBase extends BaseComponent implements INav { public static defaultProps: INavProps = { @@ -93,7 +93,7 @@ export class NavBase extends BaseComponent implements INav } public render(): JSX.Element | null { - const { groups, className, isOnTop } = this.props; + const { getStyles, groups, className, isOnTop, theme } = this.props; if (!groups) { return null; @@ -109,16 +109,13 @@ export class NavBase extends BaseComponent implements INav const groupElements: React.ReactElement<{}>[] = groups.map(this._renderGroup); + const classNames = getClassNames(getStyles!, { theme: theme!, className, isOnTop, groups }); + return ( @@ -130,43 +127,35 @@ export class NavBase extends BaseComponent implements INav return this.state.selectedKey; } + @autobind private _onRenderLink(link: INavLink) { - return (
{ link.name }
); + const { getStyles, groups, theme } = this.props; + const classNames = getClassNames(getStyles!, { theme: theme!, groups }); + return (
{ link.name }
); } private _renderNavLink(link: INavLink, linkIndex: number, nestingLevel: number) { - const isRtl: boolean = getRTL(); - const paddingBefore = _indentationSize * nestingLevel + _baseIndent; - const buttonStyles: IButtonStyles = { - root: { - [isRtl ? 'paddingRight' : 'paddingLeft']: paddingBefore, - [isRtl ? 'paddingLeft' : 'paddingRight']: _farSidePadding, - }, - textContainer: { - overflow: 'hidden', - }, - label: { - whiteSpace: 'nowrap', - textOverflow: 'ellipsis', - overflow: 'hidden', - lineHeight: '36px' - } - }; const { + getStyles, + groups, + theme, onRenderLink = this._onRenderLink } = this.props; + const classNames = getClassNames(getStyles!, { + theme: theme!, + isSelected: this._isLinkSelected(link), + isButtonEntry: link.onClick && !link.forceAnchor, + leftPadding: _indentationSize * nestingLevel + _baseIndent, + groups + }); + // Prevent hijacking of the parent window if link.target is defined const rel = link.url && link.target && !isRelativeUrl(link.url) ? 'noopener noreferrer' : undefined; return ( implements INav } private _renderCompositeLink(link: INavLink, linkIndex: number, nestingLevel: number): React.ReactElement<{}> { - const isLinkSelected: boolean = this._isLinkSelected(link); - const isRtl: boolean = getRTL(); - const absolutePositionString = `${_indentationSize * nestingLevel + 1}px`; + const { getStyles, groups, theme } = this.props; + const classNames = getClassNames(getStyles!, { + theme: theme!, + isExpanded: !!link.isExpanded, + isSelected: this._isLinkSelected(link), + isLink: true, + position: _indentationSize * nestingLevel + 1, + groups + }); return (
{ (link.links && link.links.length > 0 ? : null @@ -228,8 +206,11 @@ export class NavBase extends BaseComponent implements INav } private _renderLink(link: INavLink, linkIndex: number, nestingLevel: number): React.ReactElement<{}> { + const { getStyles, groups, theme } = this.props; + const classNames = getClassNames(getStyles!, { theme: theme!, groups }); + return ( -
  • +
  • { this._renderCompositeLink(link, linkIndex, nestingLevel) } { (link.isExpanded ? this._renderLinks(link.links, ++nestingLevel) : null) }
  • @@ -243,8 +224,11 @@ export class NavBase extends BaseComponent implements INav const linkElements: React.ReactElement<{}>[] = links.map( (link: INavLink, linkIndex: number) => this._renderLink(link, linkIndex, nestingLevel)); + const { getStyles, groups, theme } = this.props; + const classNames = getClassNames(getStyles!, { theme: theme!, groups }); + return ( -
      +
        { linkElements }
      ); @@ -252,34 +236,32 @@ export class NavBase extends BaseComponent implements INav @autobind private _renderGroup(group: INavLinkGroup, groupIndex: number): React.ReactElement<{}> { - const isGroupExpanded: boolean = !this.state.isGroupCollapsed![group.name!]; + const { getStyles, groups, theme } = this.props; + const classNames = getClassNames(getStyles!, { + theme: theme!, + isGroup: true, + isExpanded: !this.state.isGroupCollapsed![group.name!], + groups + }); return (
      { (group.name ? : null) } -
      +
      { this._renderLinks(group.links, 0 /* nestingLevel */) }
      diff --git a/packages/office-ui-fabric-react/src/components/Nav/Nav.scss b/packages/office-ui-fabric-react/src/components/Nav/Nav.scss deleted file mode 100644 index 8095e4a0383ee..0000000000000 --- a/packages/office-ui-fabric-react/src/components/Nav/Nav.scss +++ /dev/null @@ -1,196 +0,0 @@ -@import '../../common/common'; - -$navnode-height: 36px; -$hasExpandButtonLinkLeftPadding: 28px; -$noExpandButtonLinkLeftPadding: 20px; -$linkRightPadding: 20px; - -.root { - overflow-y: auto; - -webkit-overflow-scrolling: touch; - user-select: none; -} - -.rootIsOnTop { - position: absolute; -} - -.navItems { - list-style-type: none; -} - -.navItems, -.navItems > .navItem { - padding: 0; -} - -.groupContent { - display: none; - margin-bottom: 40px; -} - -.group.groupIsExpanded .groupContent { - display: block; -} - -.icon { - padding: 0px; - color: $ms-color-neutralPrimary; - background: $ms-color-neutralLighter; - transition: transform .1s linear; -} - -.iconLink { - @include ms-margin-right(4px); -} - -.chevronButton { - display: block; - - font-weight: $ms-font-weight-regular; - font-size: $ms-font-size-s; - - @include text-align(left); - line-height: $navnode-height; - margin: 5px 0; - @include padding(0px, $linkRightPadding, 0px, $hasExpandButtonLinkLeftPadding); - background: none; - border: none; - - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; - - cursor: pointer; - - color: $bodyTextColor; - background: $bodyBackgroundColor; - - &:visited { - color: inherit; - } - - &:hover { - color: $bodyTextColor; - background: $ms-color-neutralLighterAlt; - } - - &.chevronButtonIsGroup { - width: 100%; - height: $navnode-height; - border-bottom: 1px solid $bodyDividerColor; - } -} - -.chevronIcon { - position: absolute; - @include ms-left(8px); - height: $navnode-height; - line-height: $navnode-height; - font-size: 12px; - transition: transform .1s linear; -} - -.chevronIsExpanded { - transform: rotate(-180deg); -} - -.linkText { - margin: 0 4px; - overflow: hidden; - vertical-align: middle; - text-overflow: ellipsis; -} - -.compositeLink { - display: block; - position: relative; - - .chevronButton.chevronButtonLink { - display: block; - width: $hasExpandButtonLinkLeftPadding - 2; - height: $navnode-height - 2; - position: absolute; - top: 1px; - z-index: 1; - padding: 0; - margin: 0; - - .chevronIcon { - top: 0; - } - } - - color: $bodyTextColor; - background: $bodyBackgroundColor; - - &:hover { - - & .link, - & .chevronButton { - background: $ms-color-neutralLighterAlt; - color: $bodyTextColor; - } - } - - &.compositeLinkIsExpanded { - .chevronIcon { - transform: rotate(-180deg); - } - } - - &.compositeLinkIsSelected { - & .link, - & .chevronButton { - - color: $ms-color-themePrimary; - background: $ms-color-neutralLighter; - - &:after { - @include border-left(2px, solid, $ms-color-themePrimary); - - content: ''; - position: absolute; - top: 0; - @include ms-right(0); - bottom: 0; - @include ms-left(0); - } - } - } -} - -.link { - display: block; - position: relative; - height: $navnode-height; - width: 100%; - line-height: $navnode-height; - text-decoration: none; - cursor: pointer; - text-overflow: ellipsis; - text-decoration: none; - white-space: nowrap; - overflow: hidden; -} - -.buttonEntry { - color: $ms-color-themePrimary; -} - -.groupHeaderFontSize { - @include ms-font-l; -} - -.chevronButton, -.chevronButtonGroup, -.chevronButtonLink, -.link { - @include focus-border(); -} - -.root .link { - :global(.ms-Button-label) { - @include ms-font-m; - } -} diff --git a/packages/office-ui-fabric-react/src/components/Nav/Nav.styles.ts b/packages/office-ui-fabric-react/src/components/Nav/Nav.styles.ts index 55c5277ba6920..816a978de43bd 100644 --- a/packages/office-ui-fabric-react/src/components/Nav/Nav.styles.ts +++ b/packages/office-ui-fabric-react/src/components/Nav/Nav.styles.ts @@ -1,15 +1,44 @@ import { INavStyleProps, INavStyles } from './Nav.types'; +import { IButtonStyles } from '../../Button'; import { + AnimationClassNames, + DefaultFontStyles, + getFocusStyle, + FontSizes, + FontWeights, IStyle, ITheme, } from '../../Styling'; +export const buttonStyles: IButtonStyles = { + textContainer: { + overflow: 'hidden', + }, + label: { + whiteSpace: 'nowrap', + textOverflow: 'ellipsis', + overflow: 'hidden', + lineHeight: '36px' + } +}; + export const getStyles = ( props: INavStyleProps ): INavStyles => { const { className, theme, + isOnTop, + isExpanded, + isGroup, + isLink, + isSelected, + isButtonEntry, + navHeight = 36, + position, + leftPadding = 20, + leftPaddingExpanded = 28, + rightPadding = 20 } = props; const { palette, semanticColors } = theme; @@ -17,12 +46,192 @@ export const getStyles = ( return ({ root: [ 'ms-Nav', + className, { - // Insert css properties - + overflowY: 'auto', + userSelect: 'none', + WebkitOverflowScrolling: 'touch' + }, + isOnTop && [ + { + position: 'absolute' + }, + AnimationClassNames.slideRightIn40 + ] + ], + linkText: [ + 'ms-Nav-linkText', + { + margin: '0 4px', + overflow: 'hidden', + verticalAlign: 'middle', + textOverflow: 'ellipsis' } ], - - // Insert className styles + compositeLink: [ + 'ms-Nav-compositeLink', + { + display: 'block', + position: 'relative', + color: semanticColors.bodyText, + backgroundColor: semanticColors.bodyBackground, + } + ], + link: [ + 'ms-Nav-link', + getFocusStyle(theme), + { + display: 'block', + position: 'relative', + height: `${navHeight}px`, + width: '100%', + lineHeight: `${navHeight}px`, + textDecoration: 'none', + cursor: 'pointer', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + overflow: 'hidden', + paddingLeft: leftPadding, + paddingRight: rightPadding, + selectors: { + '.ms-Nav-compositeLink:hover &': { + backgroundColor: palette.neutralLighterAlt, + color: semanticColors.bodyText + }, + }, + }, + isSelected && { + color: palette.themePrimary, + backgroundColor: palette.neutralLighter, + selectors: { + '&:after': { + borderLeft: `2px solid ${palette.themePrimary}`, + content: '""', + position: 'absolute', + top: 0, + right: 0, + bottom: 0, + left: 0 + } + } + }, + isButtonEntry && { + color: palette.themePrimary + } + ], + chevronButton: [ + 'ms-Nav-chevronButton', + getFocusStyle(theme), + { + display: 'block', + fontWeight: FontWeights.regular, + fontSize: FontSizes.small, + textAlign: 'left', + lineHeight: `${navHeight}px`, + margin: '5px 0', + padding: `0px, ${rightPadding}px, 0px, ${leftPaddingExpanded}px`, + border: 'none', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + overflow: 'hidden', + cursor: 'pointer', + color: semanticColors.bodyText, + backgroundColor: 'transparent', + selectors: { + '&:visited': { + color: 'inherit' + }, + '&:hover': { + color: semanticColors.bodyText, + backgroundColor: palette.neutralLighterAlt + }, + '$compositeLink:hover &': { + color: semanticColors.bodyText, + backgroundColor: palette.neutralLighterAlt + }, + } + }, + isGroup && [ + { + width: '100%', + height: `${navHeight}px`, + borderBottom: `1px solid ${semanticColors.bodyDivider}` + }, + DefaultFontStyles.large + ], + isLink && [ + { + display: 'block', + width: `${leftPaddingExpanded - 2}px`, + height: `${navHeight - 2}px`, + position: 'absolute', + top: '1px', + left: `${position}px`, + zIndex: 1, + padding: 0, + margin: 0 + } + ], + isSelected && { + color: palette.themePrimary, + backgroundColor: palette.neutralLighterAlt, + selectors: { + '&:after': { + borderLeft: `2px solid ${palette.themePrimary}`, + content: '""', + position: 'absolute', + top: 0, + right: 0, + bottom: 0, + left: 0 + } + } + } + ], + chevronIcon: [ + 'ms-Nav-chevron', + { + position: 'absolute', + left: '8px', + height: `${navHeight}px`, + lineHeight: `${navHeight}px`, + fontSize: '12px', + transition: 'transform .1s linear', + }, + isExpanded && { + transform: 'rotate(-180deg)' + }, + isLink && { + top: 0 + } + ], + navItem: [ + 'ms-Nav-navItem', + { + padding: 0 + } + ], + navItems: [ + 'ms-Nav-navItems', + { + listStyleType: 'none', + padding: 0 + } + ], + group: [ + 'ms-Nav-group', + isExpanded && 'is-expanded' + ], + groupContent: [ + 'ms-Nav-groupContent', + { + display: 'none', + marginBottom: '40px' + }, + AnimationClassNames.slideDownIn20, + isExpanded && { + display: 'block' + } + ] }); }; diff --git a/packages/office-ui-fabric-react/src/components/Nav/Nav.types.ts b/packages/office-ui-fabric-react/src/components/Nav/Nav.types.ts index 37de48d7bb05f..872c17b669897 100644 --- a/packages/office-ui-fabric-react/src/components/Nav/Nav.types.ts +++ b/packages/office-ui-fabric-react/src/components/Nav/Nav.types.ts @@ -225,11 +225,120 @@ export interface INavStyleProps { * Accept custom classNames */ className?: string; + + /** + * is element on top boolean + */ + isOnTop?: boolean; + + /** + * is element a link boolean + */ + isLink?: boolean; + + /** + * is element a group boolean + */ + isGroup?: boolean; + + /** + * is element expanded boolean + */ + isExpanded?: boolean; + + /** + * is element selected boolean + */ + isSelected?: boolean; + + /** + * is button + */ + isButtonEntry?: boolean; + + /** + * Nav height value + */ + navHeight?: number; + + /** + * left padding value + */ + leftPadding?: number; + + /** + * left padding when expanded value + */ + leftPaddingExpanded?: number; + + /** + * right padding value + */ + rightPadding?: number; + + /** + * position value + */ + position?: number; + + /** + * Inherited from INavProps + * A collection of link groups to display in the navigation bar + */ + groups: INavLinkGroup[] | null; } export interface INavStyles { /** - * Style for the root element. + * Style set for the root element. + */ + root: IStyle; + + /** + * Style set for the link text container div element. + */ + linkText: IStyle; + + /** + * Style set for the link element extending the + * root style set for ActionButton component. + */ + link: IStyle; + + /** + * Style set for the composite link container div element + */ + compositeLink: IStyle; + + /** + * Style set for the chevron button inside the composite + * link and group elements. + */ + chevronButton: IStyle; + + /** + * Style set for the chevron icon inside the composite + * link and group elements. + */ + chevronIcon: IStyle; + + /** + * Style set for the nav links ul element. + */ + navItems: IStyle; + + /** + * Style set for the nav links li element. + */ + navItem: IStyle; + + /** + * Style set for the group root div. + */ + group: IStyle; + + /** + * Style set for the group content div inside group. */ - root?: IStyle; + groupContent: IStyle; } \ No newline at end of file diff --git a/packages/office-ui-fabric-react/src/components/Nav/__snapshots__/Nav.test.tsx.snap b/packages/office-ui-fabric-react/src/components/Nav/__snapshots__/Nav.test.tsx.snap index 25e12db86478e..dc17a9038bfb2 100644 --- a/packages/office-ui-fabric-react/src/components/Nav/__snapshots__/Nav.test.tsx.snap +++ b/packages/office-ui-fabric-react/src/components/Nav/__snapshots__/Nav.test.tsx.snap @@ -12,11 +12,20 @@ exports[`Nav renders Nav correctly 1`] = ` role="presentation" >