Skip to content

Commit

Permalink
feat: add 'as' and 'lockProps', #44
Browse files Browse the repository at this point in the history
  • Loading branch information
theKashey committed Sep 28, 2018
1 parent 0f0437f commit 5274e24
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 16 deletions.
1 change: 1 addition & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -110,7 +112,16 @@ Only `last`, or `deepest` one will work. No fighting.
<button>Click</button>
<button data-autofocus>will be focused</button>
</FocusLock>


<FocusLock as="section">
<button>Click</button>
<button data-autofocus>will be focused</button>
</FocusLock>

<FocusLock as={AnotherComponent} lockProps={{anyProp: 4}}>
<button>Click</button>
<button data-autofocus>will be focused</button>
</FocusLock>
```

If there is more than one auto-focusable target - the first will be selected.
Expand Down
12 changes: 10 additions & 2 deletions react-focus-lock.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import * as React from "react";

This comment has been minimized.

Copy link
@danielweck

danielweck Oct 2, 2018

Isn't that redundant with line 4?
Also, with TypeScript 3 I get the following error regarding line 4, so perhaps it would be best to only preserve line 1?
TS2667: Imports are not permitted in module augmentations. Consider moving them to the enclosing external module.

This comment has been minimized.

Copy link
@danielweck

danielweck Oct 2, 2018

Confirmed. Sticking to version 1.13.2 for now. Let me know when 1.14.1 is out :)

This comment has been minimized.

Copy link
@theKashey

theKashey Oct 2, 2018

Author Owner

I hate WebStorm, always importing this shit :(

This comment has been minimized.

Copy link
@theKashey

theKashey Oct 2, 2018

Author Owner

1.14.1 is out

This comment has been minimized.

Copy link
@danielweck

danielweck Oct 3, 2018

Use VisualStudio Code ;)
Thanks for the update, trying now...

This comment has been minimized.

Copy link
@theKashey

theKashey Oct 3, 2018

Author Owner

Hope would work.


declare module 'react-focus-lock' {
import * as React from 'react';

Expand Down Expand Up @@ -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 {
Expand Down
33 changes: 20 additions & 13 deletions src/Lock.js
Original file line number Diff line number Diff line change
@@ -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 }) => <div>{children}</div>;
const RenderChildren = ({children}) => <div>{children}</div>;
RenderChildren.propTypes = {
children: PropTypes.node.isRequired,
};
Expand Down Expand Up @@ -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 (
<Fragment>
{!noFocusGuards && [
<div key="guard-first" data-focus-guard tabIndex={disabled ? -1 : 0} style={hidden} />, // nearest focus guard
<div key="guard-nearest" data-focus-guard tabIndex={disabled ? -1 : 1} style={hidden} />, // first tabbed element guard
<div key="guard-first" data-focus-guard tabIndex={disabled ? -1 : 0} style={hidden}/>, // nearest focus guard
<div key="guard-nearest" data-focus-guard tabIndex={disabled ? -1 : 1} style={hidden}/>, // first tabbed element guard
]}
<div
<Container
ref={this.setObserveNode}
{...lockProps}
className={className}
Expand All @@ -106,8 +108,8 @@ class FocusLock extends Component {
onActivation={this.onActivation}
/>
{children}
</div>
{!noFocusGuards && <div data-focus-guard tabIndex={disabled ? -1 : 0} style={hidden} />}
</Container>
{!noFocusGuards && <div data-focus-guard tabIndex={disabled ? -1 : 0} style={hidden}/>}
</Fragment>
);
}
Expand All @@ -127,6 +129,9 @@ FocusLock.propTypes = {
className: PropTypes.string,

whiteList: PropTypes.func,

as: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
lockProps: PropTypes.object,
};

FocusLock.defaultProps = {
Expand All @@ -139,6 +144,8 @@ FocusLock.defaultProps = {
group: undefined,
className: undefined,
whiteList: undefined,
as: 'div',
lockProps: {},
};


Expand Down
84 changes: 84 additions & 0 deletions stories/Custom.js
Original file line number Diff line number Diff line change
@@ -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 (
<div>
should be a styled section
{disabled && <div>
! this is a <b>real trap</b>.<br/>
We will steal your focus ! <br/><br/>
<button onClick={this.toggle}>!ACTIVATE THE TRAP!</button>
<br/>
<br/>
</div>
}
<FocusLock disabled={this.state.disabled} as="section" lockProps={{style:{border:"1px solid red"}}}>
<button>BUTTON</button>
<button data-autofocus>Will be autofocused</button>
<button>BUTTON</button>
<br/>

<a href='#'>link somethere</a> <br/>

{
!disabled && <div>
<button onClick={this.toggle}>DEACTIVATE</button>
<br/>
</div>
}
</FocusLock>
</div>
)
}
}

const Comp = (props) => <main {...props} data-focus-locked-test/>;

export class StyledComponent extends Component {
state = {
disabled: true
}

toggle = () => this.setState({disabled: !this.state.disabled});

render() {
const {disabled} = this.state;
return (
<div>
should be a styled section
{disabled && <div>
! this is a <b>real trap</b>.<br/>
We will steal your focus ! <br/><br/>
<button onClick={this.toggle}>!ACTIVATE THE TRAP!</button>
<br/>
<br/>
</div>
}
<FocusLock disabled={this.state.disabled} as={Comp} lockProps={{style: {border: "1px solid red"}}}>
<button>BUTTON</button>
<button data-autofocus>Will be autofocused</button>
<button>BUTTON</button>
<br/>

<a href='#'>link somethere</a> <br/>

{
!disabled && <div>
<button onClick={this.toggle}>DEACTIVATE</button>
<br/>
</div>
}
</FocusLock>
</div>
)
}
}
4 changes: 4 additions & 0 deletions stories/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -58,3 +59,6 @@ storiesOf('Material UI', module)
storiesOf('Focus fighting', module)
.add('fight', () => <Frame><Fight /></Frame>);

storiesOf('Custom component', module)
.add('as styled section', () => <Frame><StyledSection /></Frame>)
.add('as custom component', () => <Frame><StyledComponent /></Frame>);

0 comments on commit 5274e24

Please sign in to comment.