Skip to content

Commit 034be7d

Browse files
committed
Use React.forwardRef
1 parent 5c20b15 commit 034be7d

14 files changed

+114
-104
lines changed

demo/js/basicDemo.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,4 @@ class DemoOne extends React.Component {
7373
}
7474
}
7575

76-
ReactDOM.render(<DemoOne />, document.getElementById('demo-one'));
76+
ReactDOM.render(<React.StrictMode><DemoOne /></React.StrictMode>, document.getElementById('demo-one'));

demo/js/fancyDemo.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ class Fancy extends React.Component {
8989
}
9090
}
9191

92-
ReactDOM.render(<Fancy />, document.getElementById('demo-fancy'));
92+
ReactDOM.render(<React.StrictMode><Fancy /></React.StrictMode>, document.getElementById('demo-fancy'));
9393

9494
// Pre-load the initially hidden SVGs
9595
fancyStuff.forEach(t => {

package-lock.json

Lines changed: 0 additions & 27 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,15 +50,12 @@
5050
"widget"
5151
],
5252
"dependencies": {
53-
"create-react-context": "^0.3.0",
5453
"focus-group": "^0.3.1",
5554
"prop-types": "^15.6.0",
56-
"react-display-name": "^0.2.4",
5755
"teeny-tap": "^0.2.0"
5856
},
5957
"peerDependencies": {
60-
"react": "0.14.x || ^15.0.0 || ^16.0.0",
61-
"react-dom": "0.14.x || ^15.0.0 || ^16.0.0"
58+
"react": "^16.3.0"
6259
},
6360
"devDependencies": {
6461
"babel-cli": "^6.26.0",

src/Button.js

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
const React = require('react');
22
const PropTypes = require('prop-types');
3+
const ManagerContext = require('./ManagerContext');
4+
const { refType } = require("./propTypes");
35
const specialAssign = require('./specialAssign');
4-
const withManagerContext = require('./withManagerContext');
56

67
const checkedProps = {
78
ambManager: PropTypes.object.isRequired,
89
children: PropTypes.node.isRequired,
910
disabled: PropTypes.bool,
11+
forwardedRef: refType,
1012
tag: PropTypes.string
1113
};
1214

@@ -26,6 +28,8 @@ class AriaMenuButtonButton extends React.Component {
2628

2729
static defaultProps = { tag: 'span' };
2830

31+
ref = React.createRef();
32+
2933
componentDidMount() {
3034
this.props.ambManager.button = this;
3135
}
@@ -67,6 +71,15 @@ class AriaMenuButtonButton extends React.Component {
6771
this.props.ambManager.toggleMenu({}, { focusMenu: false });
6872
};
6973

74+
setRef = instance => {
75+
this.ref.current = instance;
76+
if (typeof this.props.forwardedRef === "function") {
77+
this.props.forwardedRef(instance);
78+
} else if (this.props.forwardedRef) {
79+
this.props.forwardedRef.current = instance;
80+
}
81+
};
82+
7083
render() {
7184
const props = this.props;
7285
const ambManager = this.props.ambManager;
@@ -95,9 +108,22 @@ class AriaMenuButtonButton extends React.Component {
95108
buttonProps.onBlur = ambManager.handleBlur;
96109
}
97110
specialAssign(buttonProps, props, reserved);
111+
specialAssign(buttonProps, { ref: this.setRef });
98112

99113
return React.createElement(props.tag, buttonProps, props.children);
100114
}
101115
}
102116

103-
module.exports = withManagerContext(AriaMenuButtonButton);
117+
module.exports = React.forwardRef((props, ref) => React.createElement(
118+
ManagerContext.Consumer,
119+
null,
120+
(ambManager) => {
121+
const buttonProps = { ambManager, forwardedRef: ref };
122+
specialAssign(buttonProps, props, {
123+
ambManager: checkedProps.ambManager,
124+
children: checkedProps.children,
125+
forwardedRef: checkedProps.forwardedRef
126+
});
127+
return React.createElement(AriaMenuButtonButton, buttonProps, props.children);
128+
}
129+
));

src/ManagerContext.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
const createReactContext = require('create-react-context');
1+
const React = require('react');
22

3-
const AriaMenuButtonManagerContext = createReactContext();
3+
const AriaMenuButtonManagerContext = React.createContext();
44

55
module.exports = AriaMenuButtonManagerContext;

src/Menu.js

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,23 @@
11
const React = require('react');
2-
const ReactDOM = require('react-dom');
32
const PropTypes = require('prop-types');
43
const createTapListener = require('teeny-tap');
4+
const ManagerContext = require('./ManagerContext');
5+
const { refType } = require("./propTypes");
56
const specialAssign = require('./specialAssign');
6-
const withManagerContext = require('./withManagerContext');
77

88
const checkedProps = {
99
ambManager: PropTypes.object.isRequired,
1010
children: PropTypes.oneOfType([PropTypes.func, PropTypes.node]).isRequired,
11+
forwardedRef: refType,
1112
tag: PropTypes.string
1213
};
1314

1415
class AriaMenuButtonMenu extends React.Component {
1516
static propTypes = checkedProps;
1617
static defaultProps = { tag: 'div' };
1718

19+
ref = React.createRef();
20+
1821
componentDidMount() {
1922
this.props.ambManager.menu = this;
2023
}
@@ -42,24 +45,33 @@ class AriaMenuButtonMenu extends React.Component {
4245
}
4346

4447
addTapListener = () => {
45-
const el = ReactDOM.findDOMNode(this);
48+
const el = this.ref.current;
4649
if (!el) return;
4750
const doc = el.ownerDocument;
4851
if (!doc) return;
4952
this.tapListener = createTapListener(doc.documentElement, this.handleTap);
5053
};
5154

5255
handleTap = event => {
53-
if (ReactDOM.findDOMNode(this).contains(event.target)) return;
56+
if (this.ref.current.contains(event.target)) return;
5457
if (
55-
ReactDOM.findDOMNode(this.props.ambManager.button).contains(
58+
this.props.ambManager.button.ref.current.contains(
5659
event.target
5760
)
5861
)
5962
return;
6063
this.props.ambManager.closeMenu();
6164
};
6265

66+
setRef = instance => {
67+
this.ref.current = instance;
68+
if (typeof this.props.forwardedRef === "function") {
69+
this.props.forwardedRef(instance);
70+
} else if (this.props.forwardedRef) {
71+
this.props.forwardedRef.current = instance;
72+
}
73+
};
74+
6375
render() {
6476
const props = this.props;
6577
const ambManager = this.props.ambManager;
@@ -85,9 +97,22 @@ class AriaMenuButtonMenu extends React.Component {
8597
}
8698

8799
specialAssign(menuProps, props, checkedProps);
100+
specialAssign(menuProps, { ref: this.setRef });
88101

89102
return React.createElement(props.tag, menuProps, childrenToRender);
90103
}
91104
}
92105

93-
module.exports = withManagerContext(AriaMenuButtonMenu);
106+
module.exports = React.forwardRef((props, ref) => React.createElement(
107+
ManagerContext.Consumer,
108+
null,
109+
(ambManager) => {
110+
const buttonProps = { ambManager, forwardedRef: ref };
111+
specialAssign(buttonProps, props, {
112+
ambManager: checkedProps.ambManager,
113+
children: checkedProps.children,
114+
forwardedRef: checkedProps.forwardedRef
115+
});
116+
return React.createElement(AriaMenuButtonMenu, buttonProps, props.children);
117+
}
118+
));

src/MenuItem.js

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
const React = require('react');
22
const PropTypes = require('prop-types');
3+
const ManagerContext = require('./ManagerContext');
4+
const { refType } = require("./propTypes");
35
const specialAssign = require('./specialAssign');
4-
const withManagerContext = require('./withManagerContext');
56

67
const checkedProps = {
78
ambManager: PropTypes.object.isRequired,
89
children: PropTypes.node.isRequired,
10+
forwardedRef: refType,
911
tag: PropTypes.string,
1012
text: PropTypes.string,
1113
value: PropTypes.any
@@ -15,9 +17,11 @@ class AriaMenuButtonMenuItem extends React.Component {
1517
static propTypes = checkedProps;
1618
static defaultProps = { tag: 'div' };
1719

20+
ref = React.createRef();
21+
1822
componentDidMount() {
1923
this.props.ambManager.addItem({
20-
node: this.node,
24+
node: this.ref.current,
2125
text: this.props.text
2226
});
2327
}
@@ -37,8 +41,13 @@ class AriaMenuButtonMenuItem extends React.Component {
3741
this.props.ambManager.handleSelection(value, event);
3842
};
3943

40-
registerNode = node => {
41-
this.node = node;
44+
setRef = instance => {
45+
this.ref.current = instance;
46+
if (typeof this.props.forwardedRef === "function") {
47+
this.props.forwardedRef(instance);
48+
} else if (this.props.forwardedRef) {
49+
this.props.forwardedRef.current = instance;
50+
}
4251
};
4352

4453
render() {
@@ -47,7 +56,7 @@ class AriaMenuButtonMenuItem extends React.Component {
4756
onKeyDown: this.handleKeyDown,
4857
role: 'menuitem',
4958
tabIndex: '-1',
50-
ref: this.registerNode
59+
ref: this.setRef
5160
};
5261

5362
specialAssign(menuItemProps, this.props, checkedProps);
@@ -60,4 +69,16 @@ class AriaMenuButtonMenuItem extends React.Component {
6069
}
6170
}
6271

63-
module.exports = withManagerContext(AriaMenuButtonMenuItem);
72+
module.exports = React.forwardRef((props, ref) => React.createElement(
73+
ManagerContext.Consumer,
74+
null,
75+
(ambManager) => {
76+
const buttonProps = { ambManager, forwardedRef: ref };
77+
specialAssign(buttonProps, props, {
78+
ambManager: checkedProps.ambManager,
79+
children: checkedProps.children,
80+
forwardedRef: checkedProps.forwardedRef
81+
});
82+
return React.createElement(AriaMenuButtonMenuItem, buttonProps, props.children);
83+
}
84+
));

src/Wrapper.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ const React = require('react');
22
const PropTypes = require('prop-types');
33
const createManager = require('./createManager');
44
const ManagerContext = require('./ManagerContext');
5+
const { refType } = require("./propTypes");
56
const specialAssign = require('./specialAssign');
67

78
const checkedProps = {
89
children: PropTypes.node.isRequired,
10+
forwardedRef: refType,
911
onMenuToggle: PropTypes.func,
1012
onSelection: PropTypes.func,
1113
closeOnSelection: PropTypes.bool,
@@ -44,4 +46,9 @@ class AriaMenuButtonWrapper extends React.Component {
4446
}
4547
}
4648

47-
module.exports = AriaMenuButtonWrapper;
49+
module.exports = React.forwardRef((props, ref) => {
50+
const wrapperProps = { forwardedRef: ref };
51+
specialAssign(wrapperProps, props, { children: checkedProps.children, forwardedRef: checkedProps.forwardedRef });
52+
specialAssign(wrapperProps, { forwardedRef: ref });
53+
return React.createElement(AriaMenuButtonWrapper, wrapperProps, props.children);
54+
});

src/__tests__/createManager.test.js

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
/* globals Promise */
2-
var findDOMNodeMock = jest.fn();
3-
jest.setMock('react-dom', {
4-
findDOMNode: findDOMNodeMock
5-
});
62

73
var createManager = require('../createManager');
84
var createMockKeyEvent = require('./helpers/createMockKeyEvent');
95

6+
var mockNode = document.createElement('button');
107
var nodeOne = document.createElement('button');
118
nodeOne.focus = jest.fn();
129
var nodeTwo = document.createElement('button');
@@ -25,7 +22,7 @@ function createManagerWithMockedElements(options) {
2522
text: 'second'
2623
});
2724
manager.button = {
28-
focus: jest.fn(),
25+
ref: { current: mockNode },
2926
setState: jest.fn()
3027
};
3128
manager.menu = {
@@ -98,10 +95,7 @@ describe('createManager', function() {
9895
});
9996

10097
it('Manager#closeMenu focusing on button', function() {
101-
var mockNode = { focus: jest.fn() };
102-
findDOMNodeMock.mockImplementation(function() {
103-
return mockNode;
104-
});
98+
mockNode.focus = jest.fn();
10599

106100
var manager = createManagerWithMockedElements();
107101
manager.isOpen = true;
@@ -118,10 +112,7 @@ describe('createManager', function() {
118112
});
119113

120114
it('Manager#closeMenu without focusing on button', function() {
121-
var mockNode = { focus: jest.fn() };
122-
findDOMNodeMock.mockImplementation(function() {
123-
return mockNode;
124-
});
115+
mockNode.focus = jest.fn();
125116

126117
var manager = createManagerWithMockedElements();
127118
manager.isOpen = true;

0 commit comments

Comments
 (0)