diff --git a/common/changes/@uifabric/utilities/fix-fabric-isfocusvisible_2018-04-25-01-59.json b/common/changes/@uifabric/utilities/fix-fabric-isfocusvisible_2018-04-25-01-59.json new file mode 100644 index 0000000000000..a9d39fa272cf4 --- /dev/null +++ b/common/changes/@uifabric/utilities/fix-fabric-isfocusvisible_2018-04-25-01-59.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@uifabric/utilities", + "comment": "Adding `isDirectionalKeyCode` helper.", + "type": "minor" + } + ], + "packageName": "@uifabric/utilities", + "email": "dzearing@microsoft.com" +} \ No newline at end of file diff --git a/common/changes/office-ui-fabric-react/fix-fabric-isfocusvisible_2018-04-25-01-59.json b/common/changes/office-ui-fabric-react/fix-fabric-isfocusvisible_2018-04-25-01-59.json new file mode 100644 index 0000000000000..485a397792895 --- /dev/null +++ b/common/changes/office-ui-fabric-react/fix-fabric-isfocusvisible_2018-04-25-01-59.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "office-ui-fabric-react", + "comment": "Fabric: the isFocusVisible class is no added to the Fabric component again, to preserve backwards compatibility. Also fixing index file to export the types.", + "type": "minor" + } + ], + "packageName": "office-ui-fabric-react", + "email": "dzearing@microsoft.com" +} \ No newline at end of file diff --git a/packages/office-ui-fabric-react/src/components/Fabric/Fabric.styles.ts b/packages/office-ui-fabric-react/src/components/Fabric/Fabric.styles.ts index 746a7facdcbc9..d6e533f41771f 100644 --- a/packages/office-ui-fabric-react/src/components/Fabric/Fabric.styles.ts +++ b/packages/office-ui-fabric-react/src/components/Fabric/Fabric.styles.ts @@ -15,11 +15,13 @@ export const getStyles = (props: IFabricStyleProps): IFabricStyles => { const { theme, className, + isFocusVisible } = props; return { root: [ 'ms-Fabric', + isFocusVisible && 'is-focusVisible', theme.fonts.medium, { color: theme.palette.neutralPrimary, diff --git a/packages/office-ui-fabric-react/src/components/Fabric/Fabric.test.tsx b/packages/office-ui-fabric-react/src/components/Fabric/Fabric.test.tsx new file mode 100644 index 0000000000000..5aaebd7e9c05c --- /dev/null +++ b/packages/office-ui-fabric-react/src/components/Fabric/Fabric.test.tsx @@ -0,0 +1,14 @@ +import * as React from 'react'; +import * as renderer from 'react-test-renderer'; +import { Fabric } from './Fabric'; + +describe('Fabric', () => { + it('renders correctly', () => { + const component = renderer.create( + test + ); + const tree = component.toJSON(); + expect(tree).toMatchSnapshot(); + }); + +}); diff --git a/packages/office-ui-fabric-react/src/components/Fabric/Fabric.tsx b/packages/office-ui-fabric-react/src/components/Fabric/Fabric.tsx index 1998193d3c869..9f4a1de1b5d66 100644 --- a/packages/office-ui-fabric-react/src/components/Fabric/Fabric.tsx +++ b/packages/office-ui-fabric-react/src/components/Fabric/Fabric.tsx @@ -1,10 +1,13 @@ import * as React from 'react'; import { BaseComponent, + createRef, customizable, getNativeProps, divProperties, - classNamesFunction + classNamesFunction, + getWindow, + isDirectionalKeyCode } from '../../Utilities'; import { getStyles } from './Fabric.styles'; import { IFabricProps, IFabricStyleProps, IFabricStyles } from './Fabric.types'; @@ -12,16 +15,49 @@ import { IFabricProps, IFabricStyleProps, IFabricStyles } from './Fabric.types'; const getClassNames = classNamesFunction(); @customizable('Fabric', ['theme']) -export class Fabric extends BaseComponent { +export class Fabric extends BaseComponent { + private _rootElement = createRef(); + + constructor(props: IFabricProps) { + super(props); + this.state = { isFocusVisible: false }; + } + public render() { - const classNames = getClassNames(getStyles, this.props as IFabricStyleProps); + const classNames = getClassNames(getStyles, + { + ...this.props as IFabricStyleProps, + ...this.state + }); const divProps = getNativeProps(this.props, divProperties); return (
); } + + public componentDidMount(): void { + const win = getWindow(this._rootElement.value); + + if (win) { + this._events.on(win, 'mousedown', this._onMouseDown, true); + this._events.on(win, 'keydown', this._onKeyDown, true); + } + } + + private _onMouseDown = (ev: MouseEvent): void => { + this.setState({ isFocusVisible: false }); + } + + private _onKeyDown = (ev: KeyboardEvent): void => { + if (isDirectionalKeyCode(ev.which)) { + this.setState({ isFocusVisible: true }); + } + } } diff --git a/packages/office-ui-fabric-react/src/components/Fabric/Fabric.types.ts b/packages/office-ui-fabric-react/src/components/Fabric/Fabric.types.ts index 7a521e18e5bad..9949f90bb89a1 100644 --- a/packages/office-ui-fabric-react/src/components/Fabric/Fabric.types.ts +++ b/packages/office-ui-fabric-react/src/components/Fabric/Fabric.types.ts @@ -7,6 +7,7 @@ export interface IFabricProps extends React.HTMLAttributes { export interface IFabricStyleProps extends IFabricProps { theme: ITheme; + isFocusVisible: boolean; } export interface IFabricStyles { diff --git a/packages/office-ui-fabric-react/src/components/Fabric/__snapshots__/Fabric.test.tsx.snap b/packages/office-ui-fabric-react/src/components/Fabric/__snapshots__/Fabric.test.tsx.snap new file mode 100644 index 0000000000000..12987076b5889 --- /dev/null +++ b/packages/office-ui-fabric-react/src/components/Fabric/__snapshots__/Fabric.test.tsx.snap @@ -0,0 +1,30 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Fabric renders correctly 1`] = ` +
+ test +
+`; diff --git a/packages/office-ui-fabric-react/src/components/Fabric/index.ts b/packages/office-ui-fabric-react/src/components/Fabric/index.ts index 3852b24a3fb89..5ada46526bbab 100644 --- a/packages/office-ui-fabric-react/src/components/Fabric/index.ts +++ b/packages/office-ui-fabric-react/src/components/Fabric/index.ts @@ -1 +1,2 @@ export * from './Fabric'; +export * from './Fabric.types'; diff --git a/packages/utilities/src/index.ts b/packages/utilities/src/index.ts index 7a5be668bb502..962253d3a2a66 100644 --- a/packages/utilities/src/index.ts +++ b/packages/utilities/src/index.ts @@ -31,6 +31,7 @@ export * from './hoist'; export * from './hoistStatics'; export * from './initializeFocusRects'; export * from './initials'; +export * from './keyboard'; export * from './language'; export * from './math'; export * from './memoize'; @@ -42,4 +43,4 @@ export * from './rtl'; export * from './scroll'; export * from './string'; export * from './styled'; -export * from './warn'; \ No newline at end of file +export * from './warn'; diff --git a/packages/utilities/src/initializeFocusRects.ts b/packages/utilities/src/initializeFocusRects.ts index 444a87ae8bc7d..9a60e2df47693 100644 --- a/packages/utilities/src/initializeFocusRects.ts +++ b/packages/utilities/src/initializeFocusRects.ts @@ -1,19 +1,7 @@ import { getWindow } from './dom'; -import { KeyCodes } from './KeyCodes'; -import { KeyboardEvent } from '../../../common/temp/node_modules/@types/react'; +import { isDirectionalKeyCode } from './keyboard'; export const IsFocusVisibleClassName = 'ms-Fabric--isFocusVisible'; -const DirectionalKeyCodes = [ - KeyCodes.up, - KeyCodes.down, - KeyCodes.left, - KeyCodes.right, - KeyCodes.home, - KeyCodes.end, - KeyCodes.tab, - KeyCodes.pageUp, - KeyCodes.pageDown -]; /** * Initializes the logic which: @@ -51,14 +39,13 @@ function _onMouseDown(ev: MouseEvent): void { } } -function _onKeyDown(ev: KeyboardEvent): void { +function _onKeyDown(ev: KeyboardEvent): void { const win = getWindow(ev.target as Element); if (win) { const { classList } = win.document.body; - const isDirectionalKeyCode = DirectionalKeyCodes.indexOf(ev.which) > -1; - if (isDirectionalKeyCode && !classList.contains(IsFocusVisibleClassName)) { + if (isDirectionalKeyCode(ev.which) && !classList.contains(IsFocusVisibleClassName)) { classList.add(IsFocusVisibleClassName); } } diff --git a/packages/utilities/src/keyboard.test.ts b/packages/utilities/src/keyboard.test.ts new file mode 100644 index 0000000000000..f0e44b1a4065c --- /dev/null +++ b/packages/utilities/src/keyboard.test.ts @@ -0,0 +1,9 @@ +import { isDirectionalKeyCode } from './keyboard'; +import { KeyCodes } from './KeyCodes'; + +describe('isDirectionalKeyCode', () => { + it('can return the expected value', () => { + isDirectionalKeyCode(KeyCodes.up); + isDirectionalKeyCode(KeyCodes.enter); + }); +}); \ No newline at end of file diff --git a/packages/utilities/src/keyboard.ts b/packages/utilities/src/keyboard.ts new file mode 100644 index 0000000000000..22e7552c52fac --- /dev/null +++ b/packages/utilities/src/keyboard.ts @@ -0,0 +1,20 @@ +import { KeyCodes } from './KeyCodes'; + +const DirectionalKeyCodes: { [key: number]: number } = { + [KeyCodes.up]: 1, + [KeyCodes.down]: 1, + [KeyCodes.left]: 1, + [KeyCodes.right]: 1, + [KeyCodes.home]: 1, + [KeyCodes.end]: 1, + [KeyCodes.tab]: 1, + [KeyCodes.pageUp]: 1, + [KeyCodes.pageDown]: 1 +}; + +/** + * Returns true if the keycode is a directional keyboard key. + */ +export function isDirectionalKeyCode(which: number): boolean { + return !!DirectionalKeyCodes[which]; +}