git.fiddlerwoaroof.com
Browse code

feature: add demonstration of focus management

Ed Langley authored on 06/10/2019 20:22:03
Showing 4 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,22 @@
1
+import React from "react";
2
+import PropTypes from "prop-types";
3
+
4
+export class FocusManager extends React.Component {
5
+  constructor() {
6
+    super();
7
+    this.ref = React.createRef();
8
+    this.doFocus = () => this.ref.current && this.ref.current.focus();
9
+  }
10
+  componentDidMount() {
11
+    if (this.ref.current) this.ref.current.focus();
12
+    //eslint-disable-next-line no-console
13
+    else console.error("ref unbound");
14
+  }
15
+  render() {
16
+    return this.props.children(this.ref, this.doFocus);
17
+  }
18
+}
19
+
20
+FocusManager.propTypes = {
21
+  children: PropTypes.func.isRequired
22
+};
... ...
@@ -1,6 +1,10 @@
1 1
 import React from "react";
2 2
 import PropTypes from "prop-types";
3 3
 
4
+const InputWithFocus = React.forwardRef((props, ref) => (
5
+  <input {...props} ref={ref} />
6
+));
7
+
4 8
 export class NameControl extends React.Component {
5 9
   constructor(props) {
6 10
     super(props);
... ...
@@ -13,7 +17,8 @@ export class NameControl extends React.Component {
13 17
     if (this.props.name === "") {
14 18
       return (
15 19
         <div>
16
-          <input
20
+          <InputWithFocus
21
+            ref={this.props.focusRef}
17 22
             type="text"
18 23
             value={this.state.cur_input}
19 24
             onChange={e => this.setState({ cur_input: e.target.value })}
... ...
@@ -14,6 +14,7 @@ import { rootReducer, updateName } from "./reducer";
14 14
 import { rootSaga, getIp, fail } from "./root-saga";
15 15
 
16 16
 import { put } from "redux-saga/effects";
17
+import { FocusManager } from "./FocusManager";
17 18
 
18 19
 // Store Setup ==============================================================================
19 20
 
... ...
@@ -46,7 +47,9 @@ function* toplevel() {
46 47
     ReactDOM.render(
47 48
       // The Toplevel component is wrapped in a provider, to make the store available to connect
48 49
       <Provider store={store}>
49
-        <ConnectedRoot />
50
+        <FocusManager>
51
+          {(ref, doFocus) => <ConnectedRoot focusRef={ref} {...{ doFocus }} />}
52
+        </FocusManager>
50 53
       </Provider>,
51 54
       document.getElementById("root"),
52 55
       resolve
... ...
@@ -3,7 +3,17 @@ import PropTypes from "prop-types";
3 3
 import { NameControl } from "./NameControl";
4 4
 import { IpControl } from "./IpControl";
5 5
 
6
-export const Root = ({ name, updateName, ip, getIp, fail, error, restart }) => (
6
+export const Root = ({
7
+  name,
8
+  updateName,
9
+  ip,
10
+  getIp,
11
+  fail,
12
+  error,
13
+  restart,
14
+  doFocus,
15
+  focusRef
16
+}) => (
7 17
   <div>
8 18
     {error ? (
9 19
       <div>
... ...
@@ -11,10 +21,11 @@ export const Root = ({ name, updateName, ip, getIp, fail, error, restart }) => (
11 21
         error resolved. <button onClick={restart}>Restart</button>
12 22
       </div>
13 23
     ) : null}
14
-    <NameControl name={name} updateName={updateName} />
24
+    <NameControl name={name} updateName={updateName} focusRef={focusRef} />
15 25
     <IpControl ip={ip} getIp={getIp} />
16 26
     <p />
17 27
     <button onClick={fail}>Fail</button>
28
+    <button onClick={doFocus}>Focus!</button>
18 29
   </div>
19 30
 );
20 31
 Root.propTypes = {