From 5274e2433a471c70640675a72a8285d563aaba88 Mon Sep 17 00:00:00 2001 From: Anton Korzunov Date: Fri, 28 Sep 2018 21:32:28 +1000 Subject: [PATCH] feat: add 'as' and 'lockProps', #44 --- .eslintrc | 1 + README.md | 13 ++++++- react-focus-lock.d.ts | 12 +++++-- src/Lock.js | 33 ++++++++++------- stories/Custom.js | 84 +++++++++++++++++++++++++++++++++++++++++++ stories/index.js | 4 +++ 6 files changed, 131 insertions(+), 16 deletions(-) create mode 100644 stories/Custom.js diff --git a/.eslintrc b/.eslintrc index e17a0bf..1f6d407 100644 --- a/.eslintrc +++ b/.eslintrc @@ -19,6 +19,7 @@ // Turn off https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-filename-extension.md "react/jsx-filename-extension": 0, + "react/forbid-prop-types" : 0, // Turn off https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-named-as-default-member.md "import/no-named-as-default-member": 0, diff --git a/README.md b/README.md index 4be8b7f..79cc894 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,8 @@ I'v got a good [article about focus management, dialogs and WAI-ARIA](https://m - `noFocusGuards` disabled _focus guards_ - virtual inputs which secure tab index. - `group` named focus group for focus scattering aka [combined lock targets](https://github.com/theKashey/vue-focus-lock/issues/2) - `whiteList` you could _whitelist_ locations FocusLock should carry about. Everything outside it will ignore. For example - any modals. + - `as` if you need to change internal `div` element, to any other + - `lockProps` to pass any extra props (except className) to the internal wrapper. # Behavior 0. It will always keep focus inside Lock. @@ -110,7 +112,16 @@ Only `last`, or `deepest` one will work. No fighting. - + + + + + + + + + + ``` If there is more than one auto-focusable target - the first will be selected. diff --git a/react-focus-lock.d.ts b/react-focus-lock.d.ts index 40e7cec..3152dae 100644 --- a/react-focus-lock.d.ts +++ b/react-focus-lock.d.ts @@ -1,3 +1,5 @@ +import * as React from "react"; + declare module 'react-focus-lock' { import * as React from 'react'; @@ -40,16 +42,22 @@ declare module 'react-focus-lock' { */ group?: string; - children: React.ReactNode; - className?: string; + /** + * Component to use, defaults to 'div' + */ + as?: React.ReactType, + lockProps?: { [key:string]: any }, + /** * Controls focus lock working areas. Lock will silently ignore all the events from `not allowed` areas * @param activeElement * @returns {Boolean} true if focus lock should handle activeElement, false if not */ whiteList?: (activeElement: HTMLElement) => boolean; + + children: React.ReactNode; } interface AutoFocusProps { diff --git a/src/Lock.js b/src/Lock.js index 1f0d4fb..4c90736 100644 --- a/src/Lock.js +++ b/src/Lock.js @@ -1,10 +1,10 @@ -import React, { Component } from 'react'; +import React, {Component} from 'react'; import PropTypes from 'prop-types'; -import { constants } from 'focus-lock'; -import FocusTrap, { onBlur, onFocus } from './Trap'; -import { deferAction } from './util'; +import {constants} from 'focus-lock'; +import FocusTrap, {onBlur, onFocus} from './Trap'; +import {deferAction} from './util'; -const RenderChildren = ({ children }) =>
{children}
; +const RenderChildren = ({children}) =>
{children}
; RenderChildren.propTypes = { children: PropTypes.node.isRequired, }; @@ -71,26 +71,28 @@ class FocusLock extends Component { group, className, whiteList, + as: Container = 'div', + lockProps: containerProps = {}, } = this.props; - const { observed } = this.state; + const {observed} = this.state; if (typeof allowTextSelection !== 'undefined') { // eslint-disable-next-line no-console console.warn('React-Focus-Lock: allowTextSelection is deprecated and enabled by default'); } - const lockProps = { + const lockProps = Object.assign({ [constants.FOCUS_DISABLED]: disabled && 'disabled', [constants.FOCUS_GROUP]: group, - }; + }, containerProps); return ( {!noFocusGuards && [ -
, // nearest focus guard -
, // first tabbed element guard +
, // nearest focus guard +
, // first tabbed element guard ]} -
{children} -
- {!noFocusGuards &&
} + + {!noFocusGuards &&
} ); } @@ -127,6 +129,9 @@ FocusLock.propTypes = { className: PropTypes.string, whiteList: PropTypes.func, + + as: PropTypes.oneOfType([PropTypes.string, PropTypes.func]), + lockProps: PropTypes.object, }; FocusLock.defaultProps = { @@ -139,6 +144,8 @@ FocusLock.defaultProps = { group: undefined, className: undefined, whiteList: undefined, + as: 'div', + lockProps: {}, }; diff --git a/stories/Custom.js b/stories/Custom.js new file mode 100644 index 0000000..eff61e8 --- /dev/null +++ b/stories/Custom.js @@ -0,0 +1,84 @@ +import React, {Component} from "react"; +import FocusLock from "../src/index"; + +export class StyledSection extends Component { + state = { + disabled: true + } + + toggle = () => this.setState({disabled: !this.state.disabled}); + + render() { + const {disabled} = this.state; + return ( +
+ should be a styled section + {disabled &&
+ ! this is a real trap.
+ We will steal your focus !

+ +
+
+
+ } + + + + +
+ + link somethere
+ + { + !disabled &&
+ +
+
+ } +
+
+ ) + } +} + +const Comp = (props) =>
; + +export class StyledComponent extends Component { + state = { + disabled: true + } + + toggle = () => this.setState({disabled: !this.state.disabled}); + + render() { + const {disabled} = this.state; + return ( +
+ should be a styled section + {disabled &&
+ ! this is a real trap.
+ We will steal your focus !

+ +
+
+
+ } + + + + +
+ + link somethere
+ + { + !disabled &&
+ +
+
+ } +
+
+ ) + } +} diff --git a/stories/index.js b/stories/index.js index 3181dec..2eac451 100644 --- a/stories/index.js +++ b/stories/index.js @@ -16,6 +16,7 @@ import GroupCase from './Group'; import PortalCase from './Portal'; import {MUISelect, MUISelectWhite} from './MUI'; import Fight from './FocusFighting'; +import {StyledComponent, StyledSection} from "./Custom"; const frameStyle = { width: '400px', @@ -58,3 +59,6 @@ storiesOf('Material UI', module) storiesOf('Focus fighting', module) .add('fight', () => ); +storiesOf('Custom component', module) + .add('as styled section', () => ) + .add('as custom component', () => );