planning
All checks were successful
Publish To Prod / deploy_and_publish (push) Successful in 35s

This commit is contained in:
2024-10-14 09:15:30 +02:00
parent bcba00a730
commit 6e64e138e2
21059 changed files with 2317811 additions and 1 deletions

129
node_modules/react-aria-menubutton/src/Button.js generated vendored Normal file
View File

@@ -0,0 +1,129 @@
const React = require('react');
const PropTypes = require('prop-types');
const ManagerContext = require('./ManagerContext');
const { refType } = require("./propTypes");
const specialAssign = require('./specialAssign');
const checkedProps = {
ambManager: PropTypes.object.isRequired,
children: PropTypes.node.isRequired,
disabled: PropTypes.bool,
forwardedRef: refType,
tag: PropTypes.string
};
// List retrieved from https://www.w3schools.com/tags/att_disabled.asp
const disabledSupportedTags = () => [
'button',
'fieldset',
'input',
'optgroup',
'option',
'select',
'textarea'
];
class AriaMenuButtonButton extends React.Component {
static propTypes = checkedProps;
static defaultProps = { tag: 'span' };
ref = React.createRef();
componentDidMount() {
this.props.ambManager.button = this;
}
componentWillUnmount() {
this.props.ambManager.destroy();
}
handleKeyDown = event => {
if (this.props.disabled) return;
const ambManager = this.props.ambManager;
switch (event.key) {
case 'ArrowDown':
event.preventDefault();
if (!ambManager.isOpen) {
ambManager.openMenu();
} else {
ambManager.focusItem(0);
}
break;
case 'Enter':
case ' ':
event.preventDefault();
ambManager.toggleMenu();
break;
case 'Escape':
ambManager.handleMenuKey(event);
break;
default:
// (Potential) letter keys
ambManager.handleButtonNonArrowKey(event);
}
};
handleClick = () => {
if (this.props.disabled) return;
this.props.ambManager.toggleMenu({}, { focusMenu: false });
};
setRef = instance => {
this.ref.current = instance;
if (typeof this.props.forwardedRef === "function") {
this.props.forwardedRef(instance);
} else if (this.props.forwardedRef) {
this.props.forwardedRef.current = instance;
}
};
render() {
const props = this.props;
const ambManager = this.props.ambManager;
const buttonProps = {
// "The menu button itself has a role of button."
role: 'button',
tabIndex: props.disabled ? '' : '0',
// "The menu button has an aria-haspopup property, set to true."
'aria-haspopup': true,
'aria-expanded': ambManager.isOpen,
'aria-disabled': props.disabled,
onKeyDown: this.handleKeyDown,
onClick: this.handleClick
};
const reserved = {};
specialAssign(reserved, checkedProps);
// The disabled property should be passed down to the Button element
// if the tag has support for disabled attribute. So it needs to be removed
// from the reserved property object
if (disabledSupportedTags().indexOf(props.tag) >= 0) {
delete reserved.disabled;
}
if (ambManager.options.closeOnBlur) {
buttonProps.onBlur = ambManager.handleBlur;
}
specialAssign(buttonProps, props, reserved);
specialAssign(buttonProps, { ref: this.setRef });
return React.createElement(props.tag, buttonProps, props.children);
}
}
module.exports = React.forwardRef((props, ref) => React.createElement(
ManagerContext.Consumer,
null,
(ambManager) => {
const buttonProps = { ambManager, forwardedRef: ref };
specialAssign(buttonProps, props, {
ambManager: checkedProps.ambManager,
children: checkedProps.children,
forwardedRef: checkedProps.forwardedRef
});
return React.createElement(AriaMenuButtonButton, buttonProps, props.children);
}
));

View File

@@ -0,0 +1,5 @@
const React = require('react');
const AriaMenuButtonManagerContext = React.createContext();
module.exports = AriaMenuButtonManagerContext;

118
node_modules/react-aria-menubutton/src/Menu.js generated vendored Normal file
View File

@@ -0,0 +1,118 @@
const React = require('react');
const PropTypes = require('prop-types');
const createTapListener = require('teeny-tap');
const ManagerContext = require('./ManagerContext');
const { refType } = require("./propTypes");
const specialAssign = require('./specialAssign');
const checkedProps = {
ambManager: PropTypes.object.isRequired,
children: PropTypes.oneOfType([PropTypes.func, PropTypes.node]).isRequired,
forwardedRef: refType,
tag: PropTypes.string
};
class AriaMenuButtonMenu extends React.Component {
static propTypes = checkedProps;
static defaultProps = { tag: 'div' };
ref = React.createRef();
componentDidMount() {
this.props.ambManager.menu = this;
}
componentDidUpdate() {
const ambManager = this.props.ambManager;
if (!ambManager.options.closeOnBlur) return;
if (ambManager.isOpen && !this.tapListener) {
this.addTapListener();
} else if (!ambManager.isOpen && this.tapListener) {
this.tapListener.remove();
delete this.tapListener;
}
if (!ambManager.isOpen) {
// Clear the ambManager's items, so they
// can be reloaded next time this menu opens
ambManager.clearItems();
}
}
componentWillUnmount() {
if (this.tapListener) this.tapListener.remove();
this.props.ambManager.destroy();
}
addTapListener = () => {
const el = this.ref.current;
if (!el) return;
const doc = el.ownerDocument;
if (!doc) return;
this.tapListener = createTapListener(doc.documentElement, this.handleTap);
};
handleTap = event => {
if (this.ref.current.contains(event.target)) return;
if (
this.props.ambManager.button.ref.current.contains(
event.target
)
)
return;
this.props.ambManager.closeMenu();
};
setRef = instance => {
this.ref.current = instance;
if (typeof this.props.forwardedRef === "function") {
this.props.forwardedRef(instance);
} else if (this.props.forwardedRef) {
this.props.forwardedRef.current = instance;
}
};
render() {
const props = this.props;
const ambManager = this.props.ambManager;
const childrenToRender = (function() {
if (typeof props.children === 'function') {
return props.children({ isOpen: ambManager.isOpen });
}
if (ambManager.isOpen) return props.children;
return false;
})();
if (!childrenToRender) return false;
const menuProps = {
onKeyDown: ambManager.handleMenuKey,
role: 'menu',
tabIndex: -1
};
if (ambManager.options.closeOnBlur) {
menuProps.onBlur = ambManager.handleBlur;
}
specialAssign(menuProps, props, checkedProps);
specialAssign(menuProps, { ref: this.setRef });
return React.createElement(props.tag, menuProps, childrenToRender);
}
}
module.exports = React.forwardRef((props, ref) => React.createElement(
ManagerContext.Consumer,
null,
(ambManager) => {
const buttonProps = { ambManager, forwardedRef: ref };
specialAssign(buttonProps, props, {
ambManager: checkedProps.ambManager,
children: checkedProps.children,
forwardedRef: checkedProps.forwardedRef
});
return React.createElement(AriaMenuButtonMenu, buttonProps, props.children);
}
));

84
node_modules/react-aria-menubutton/src/MenuItem.js generated vendored Normal file
View File

@@ -0,0 +1,84 @@
const React = require('react');
const PropTypes = require('prop-types');
const ManagerContext = require('./ManagerContext');
const { refType } = require("./propTypes");
const specialAssign = require('./specialAssign');
const checkedProps = {
ambManager: PropTypes.object.isRequired,
children: PropTypes.node.isRequired,
forwardedRef: refType,
tag: PropTypes.string,
text: PropTypes.string,
value: PropTypes.any
};
class AriaMenuButtonMenuItem extends React.Component {
static propTypes = checkedProps;
static defaultProps = { tag: 'div' };
ref = React.createRef();
componentDidMount() {
this.props.ambManager.addItem({
node: this.ref.current,
text: this.props.text
});
}
handleKeyDown = event => {
if (event.key !== 'Enter' && event.key !== ' ') return;
if (this.props.tag === 'a' && this.props.href) return;
event.preventDefault();
this.selectItem(event);
};
selectItem = event => {
// If there's no value, we'll send the child
const value = typeof this.props.value !== 'undefined'
? this.props.value
: this.props.children;
this.props.ambManager.handleSelection(value, event);
};
setRef = instance => {
this.ref.current = instance;
if (typeof this.props.forwardedRef === "function") {
this.props.forwardedRef(instance);
} else if (this.props.forwardedRef) {
this.props.forwardedRef.current = instance;
}
};
render() {
const menuItemProps = {
onClick: this.selectItem,
onKeyDown: this.handleKeyDown,
role: 'menuitem',
tabIndex: '-1',
ref: this.setRef
};
specialAssign(menuItemProps, this.props, checkedProps);
return React.createElement(
this.props.tag,
menuItemProps,
this.props.children
);
}
}
module.exports = React.forwardRef((props, ref) => React.createElement(
ManagerContext.Consumer,
null,
(ambManager) => {
const buttonProps = { ambManager, forwardedRef: ref };
specialAssign(buttonProps, props, {
ambManager: checkedProps.ambManager,
children: checkedProps.children,
forwardedRef: checkedProps.forwardedRef
});
return React.createElement(AriaMenuButtonMenuItem, buttonProps, props.children);
}
));

62
node_modules/react-aria-menubutton/src/Wrapper.js generated vendored Normal file
View File

@@ -0,0 +1,62 @@
const React = require('react');
const PropTypes = require('prop-types');
const createManager = require('./createManager');
const ManagerContext = require('./ManagerContext');
const { refType } = require("./propTypes");
const specialAssign = require('./specialAssign');
const checkedProps = {
children: PropTypes.node.isRequired,
forwardedRef: refType,
onMenuToggle: PropTypes.func,
onSelection: PropTypes.func,
closeOnSelection: PropTypes.bool,
closeOnBlur: PropTypes.bool,
tag: PropTypes.string
};
const managerOptionsFromProps = (props) => {
return {
onMenuToggle: props.onMenuToggle,
onSelection: props.onSelection,
closeOnSelection: props.closeOnSelection,
closeOnBlur: props.closeOnBlur,
id: props.id
}
}
class AriaMenuButtonWrapper extends React.Component {
static propTypes = checkedProps;
static defaultProps = { tag: 'div' };
constructor(props) {
super(props);
this.manager = createManager(managerOptionsFromProps(props));
}
componentDidUpdate() {
this.manager.updateOptions(managerOptionsFromProps(this.props))
}
render() {
const wrapperProps = {};
specialAssign(wrapperProps, this.props, checkedProps);
return React.createElement(
ManagerContext.Provider,
{ value: this.manager },
React.createElement(
this.props.tag,
wrapperProps,
this.props.children,
),
);
}
}
module.exports = React.forwardRef((props, ref) => {
const wrapperProps = { forwardedRef: ref };
specialAssign(wrapperProps, props, { children: checkedProps.children, forwardedRef: checkedProps.forwardedRef });
specialAssign(wrapperProps, { forwardedRef: ref });
return React.createElement(AriaMenuButtonWrapper, wrapperProps, props.children);
});

View File

@@ -0,0 +1,169 @@
const React = require('react');
const ReactDOMServer = require('react-dom/server');
const shallow = require('enzyme').shallow;
const shallowToJson = require('enzyme-to-json').shallowToJson;
const Button = require('../Button');
const ManagerContext = require('../ManagerContext');
const MockWrapper = require('./helpers/MockWrapper');
const createMockKeyEvent = require('./helpers/createMockKeyEvent');
const createMockManager = require('./helpers/createMockManager');
const createManager = require('../createManager');
const el = React.createElement;
describe('<Button>', function() {
let shallowOptions;
let ambManager;
let downEvent;
let enterEvent;
let spaceEvent;
let escapeEvent;
let fEvent;
beforeEach(function() {
ambManager = createMockManager();
shallowOptions = {
wrappingComponent: ManagerContext.Provider,
wrappingComponentProps: { value: ambManager },
};
downEvent = createMockKeyEvent('ArrowDown');
enterEvent = createMockKeyEvent('Enter');
spaceEvent = createMockKeyEvent(' ');
escapeEvent = createMockKeyEvent('Escape');
fEvent = createMockKeyEvent(null, 70);
});
test('DOM with only required props and text child', function() {
const wrapper = shallow(el(Button, null, 'foo'), shallowOptions).dive().dive();
expect(shallowToJson(wrapper)).toMatchSnapshot();
});
test('no onBlur prop when closeOnBlur is false', function() {
const manager = createManager({ closeOnBlur: false });
const shallowOptions = {
wrappingComponent: ManagerContext.Provider,
wrappingComponentProps: { value: manager }
};
const wrapper = shallow(el(Button, null, ''), shallowOptions).dive().dive();
expect(shallowToJson(wrapper).props).not.toHaveProperty('onBlur');
});
test('DOM with all possible props and element child', function() {
const button = el(
Button,
{
id: 'foo',
className: 'bar',
style: { top: 2 },
tag: 'button',
disabled: true,
'data-something-something': 'seven' // arbitrary prop
},
el('span', null, 'hooha')
);
const wrapper = shallow(button, shallowOptions).dive().dive();
expect(shallowToJson(wrapper)).toMatchSnapshot();
});
test('click', function() {
const wrapper = shallow(el(Button, null, 'foo'), shallowOptions).dive().dive();
wrapper.simulate('click');
expect(ambManager.toggleMenu).toHaveBeenCalledTimes(1);
});
test('click when disabled', function() {
const wrapper = shallow(
el(Button, { disabled: true }, 'foo'),
shallowOptions
).dive().dive();
wrapper.simulate('click');
expect(ambManager.toggleMenu).not.toHaveBeenCalled();
});
test('element has disabled attribute when disabled property is set to true', function() {
const wrapper = shallow(
el(Button, { disabled: true, tag: 'button' }, 'foo'),
shallowOptions
).dive().dive();
expect(wrapper.props().disabled).toBeTruthy();
});
test('down arrow when closed', function() {
const wrapper = shallow(el(Button, null, 'foo'), shallowOptions).dive().dive();
wrapper.simulate('keyDown', downEvent);
expect(downEvent.preventDefault).toHaveBeenCalledTimes(1);
expect(ambManager.openMenu).toHaveBeenCalledTimes(1);
expect(ambManager.openMenu).toHaveBeenCalledWith();
});
test('down arrow when open', function() {
const wrapper = shallow(el(Button, null, 'foo'), shallowOptions).dive().dive();
ambManager.isOpen = true;
wrapper.simulate('keyDown', downEvent);
expect(downEvent.preventDefault).toHaveBeenCalledTimes(1);
expect(ambManager.openMenu).not.toHaveBeenCalled();
});
test('enter key', function() {
const wrapper = shallow(el(Button, null, 'foo'), shallowOptions).dive().dive();
wrapper.simulate('keyDown', enterEvent);
expect(enterEvent.preventDefault).toHaveBeenCalledTimes(1);
expect(ambManager.toggleMenu).toHaveBeenCalledTimes(1);
});
test('space key', function() {
const wrapper = shallow(el(Button, null, 'foo'), shallowOptions).dive().dive();
wrapper.simulate('keyDown', spaceEvent);
expect(spaceEvent.preventDefault).toHaveBeenCalledTimes(1);
expect(ambManager.toggleMenu).toHaveBeenCalledTimes(1);
});
test('escape key', function() {
const wrapper = shallow(el(Button, null, 'foo'), shallowOptions).dive().dive();
wrapper.simulate('keyDown', escapeEvent);
expect(ambManager.handleMenuKey).toHaveBeenCalledTimes(1);
expect(ambManager.handleMenuKey.mock.calls[0][0].key).toBe('Escape');
});
test('f key', function() {
const wrapper = shallow(el(Button, null, 'foo'), shallowOptions).dive().dive();
wrapper.simulate('keyDown', fEvent);
expect(ambManager.handleButtonNonArrowKey).toHaveBeenCalledTimes(1);
expect(ambManager.handleButtonNonArrowKey.mock.calls[0][0].keyCode).toBe(
70
);
});
test('enter key when disabled', function() {
const wrapper = shallow(
el(Button, { disabled: true }, 'foo'),
shallowOptions
).dive().dive();
wrapper.simulate('keyDown', enterEvent);
expect(enterEvent.preventDefault).not.toHaveBeenCalled();
expect(ambManager.toggleMenu).not.toHaveBeenCalled();
});
});
describe('<Button> rendered via renderToString', function() {
test('does not throw', function() {
const output = ReactDOMServer.renderToString(
el(
MockWrapper,
{ mockManager: createMockManager() },
el(Button, null, 'foo')
)
);
expect(output).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,130 @@
const React = require('react');
const ReactDOMServer = require('react-dom/server');
const shallow = require('enzyme').shallow;
const mount = require('enzyme').mount;
const shallowToJson = require('enzyme-to-json').shallowToJson;
const ManagerContext = require('../ManagerContext');
const Menu = require('../Menu');
const MockWrapper = require('./helpers/MockWrapper');
const createMockManager = require('./helpers/createMockManager');
const createManager = require('../createManager');
const el = React.createElement;
describe('<Menu>', function() {
let shallowOptions;
let ambManager;
beforeEach(function() {
ambManager = createMockManager();
shallowOptions = {
wrappingComponent: ManagerContext.Provider,
wrappingComponentProps: { value: ambManager }
};
});
test('closed Menu DOM with only required props and element child', function() {
const menuEl = el(Menu, null, el('div', null, 'foo'));
const wrapper = shallow(menuEl, shallowOptions).dive().dive();
expect(shallowToJson(wrapper)).toMatchSnapshot();
});
test('no onBlur prop when closeOnBlur is false', function() {
const manager = createManager({ closeOnBlur: false });
manager.isOpen = true;
const shallowOptions = {
wrappingComponent: ManagerContext.Provider,
wrappingComponentProps: { value: manager }
};
const menuEl = el(Menu, null, el('div', null, 'foo'));
const wrapper = shallow(menuEl, shallowOptions).dive().dive();
expect(shallowToJson(wrapper).props).not.toHaveProperty('onBlur');
});
test('open Menu DOM with only required props and element child', function() {
ambManager.isOpen = true;
const menuEl = el(Menu, null, el('div', null, el('div', null, 'foo')));
const wrapper = shallow(menuEl, shallowOptions).dive().dive();
expect(shallowToJson(wrapper)).toMatchSnapshot();
});
test('closed menu DOM with all possible props and function child', function() {
const childFunction = jest.fn().mockImplementation(function(menuState) {
return 'isOpen = ' + menuState.isOpen;
});
const menuEl = el(
Menu,
{
id: 'foo',
className: 'bar',
style: { bottom: 1 },
tag: 'ul',
'data-something-something': 'seven' // arbitrary prop
},
childFunction
);
const wrapper = shallow(menuEl, shallowOptions).dive().dive();
expect(shallowToJson(wrapper)).toMatchSnapshot();
expect(childFunction).toHaveBeenCalledTimes(1);
expect(childFunction.mock.calls[0]).toEqual([{ isOpen: false }]);
});
test('open menu DOM with all possible props and function child', function() {
ambManager.isOpen = true;
const childFunction = jest.fn().mockImplementation(function(menuState) {
return 'isOpen = ' + menuState.isOpen;
});
const menuEl = el(
Menu,
{
id: 'bar',
className: 'foo',
style: { left: 1 },
tag: 'section'
},
childFunction
);
const wrapper = shallow(menuEl, shallowOptions).dive().dive();
expect(shallowToJson(wrapper)).toMatchSnapshot();
expect(childFunction).toHaveBeenCalledTimes(1);
expect(childFunction.mock.calls[0]).toEqual([{ isOpen: true }]);
});
test('menu updating', function() {
ambManager.menuItems = [1, 2];
const childFunction = jest.fn();
class LittleApp extends React.Component {
state = { open: false };
toggleMenu = () => {
this.setState({ open: !this.state.open });
};
render() {
return el(
MockWrapper,
{ mockManager: ambManager },
el(Menu, null, childFunction)
);
}
}
const wrapper = mount(el(LittleApp));
wrapper.instance().toggleMenu();
expect(ambManager.clearItems).toHaveBeenCalledTimes(1);
});
});
describe('<Menu> rendered via renderToString', function() {
test('does not throw', function() {
const output = ReactDOMServer.renderToString(
el(
MockWrapper,
{ mockManager: createMockManager() },
el(Menu, null, el('div', null, 'foo'))
)
);
expect(output).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,106 @@
const React = require('react');
const ReactDOMServer = require('react-dom/server');
const shallow = require('enzyme').shallow;
const shallowToJson = require('enzyme-to-json').shallowToJson;
const ManagerContext = require('../ManagerContext');
const MenuItem = require('../MenuItem');
const MockWrapper = require('./helpers/MockWrapper');
const createMockKeyEvent = require('./helpers/createMockKeyEvent');
const createMockManager = require('./helpers/createMockManager');
const el = React.createElement;
describe('<MenuItem>', function() {
let shallowOptions;
let ambManager;
beforeEach(function() {
ambManager = createMockManager();
shallowOptions = {
wrappingComponent: ManagerContext.Provider,
wrappingComponentProps: { value: ambManager }
};
});
it('DOM with only required props', function() {
const menuItem = el(MenuItem, null, 'foo');
const wrapper = shallow(menuItem, shallowOptions).dive().dive();
expect(shallowToJson(wrapper)).toMatchSnapshot();
});
it('DOM with all possible props and element child', function() {
const menuItem = el(
MenuItem,
{
className: 'foobar',
id: 'hogwash',
tag: 'li',
style: { right: '1em' },
text: 'horse',
value: 'lamb',
'data-something-something': 'seven' // arbitrary prop
},
el('a', { href: '#' }, 'foo')
);
const wrapper = shallow(menuItem, shallowOptions).dive().dive();
expect(shallowToJson(wrapper)).toMatchSnapshot();
});
it('click without specified value prop', function() {
const mockEvent = { bee: 'baa' };
const menuItem = el(MenuItem, null, 'foo');
const wrapper = shallow(menuItem, shallowOptions).dive().dive();
wrapper.simulate('click', mockEvent);
expect(ambManager.handleSelection).toHaveBeenCalledTimes(1);
expect(ambManager.handleSelection.mock.calls[0]).toEqual([
'foo',
mockEvent
]);
});
it('click with specified value prop', function() {
const mockEvent = { bee: 'baa' };
const menuItem = el(MenuItem, { value: 'bar' }, 'foo');
const wrapper = shallow(menuItem, shallowOptions).dive().dive();
wrapper.simulate('click', mockEvent);
expect(ambManager.handleSelection).toHaveBeenCalledTimes(1);
expect(ambManager.handleSelection.mock.calls[0]).toEqual([
'bar',
mockEvent
]);
});
it('click with specified value prop', function() {
const mockEnterEvent = createMockKeyEvent('Enter');
const mockSpaceEvent = createMockKeyEvent(' ');
const mockEscapeEvent = createMockKeyEvent('Escape');
const menuItem = el(MenuItem, null, 'foo');
const wrapper = shallow(menuItem, shallowOptions).dive().dive();
wrapper.simulate('keyDown', mockEnterEvent);
wrapper.simulate('keyDown', mockSpaceEvent);
wrapper.simulate('keyDown', mockEscapeEvent);
expect(ambManager.handleSelection).toHaveBeenCalledTimes(2);
expect(ambManager.handleSelection.mock.calls[0]).toEqual([
'foo',
mockEnterEvent
]);
expect(ambManager.handleSelection.mock.calls[1]).toEqual([
'foo',
mockSpaceEvent
]);
});
});
describe('<MenuItem> rendered via renderToString', function() {
it('does not throw', function() {
const output = ReactDOMServer.renderToString(
el(
MockWrapper,
{ mockManager: createMockManager() },
el(MenuItem, null, 'foo')
)
);
expect(output).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,41 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<Button> DOM with all possible props and element child 1`] = `
<button
aria-disabled={true}
aria-expanded={false}
aria-haspopup={true}
className="bar"
data-something-something="seven"
disabled={true}
id="foo"
onClick={[Function]}
onKeyDown={[Function]}
role="button"
style={
Object {
"top": 2,
}
}
tabIndex=""
>
<span>
hooha
</span>
</button>
`;
exports[`<Button> DOM with only required props and text child 1`] = `
<span
aria-expanded={false}
aria-haspopup={true}
onClick={[Function]}
onKeyDown={[Function]}
role="button"
tabIndex="0"
>
foo
</span>
`;
exports[`<Button> rendered via renderToString does not throw 1`] = `"<div><span role=\\"button\\" tabindex=\\"0\\" aria-haspopup=\\"true\\" aria-expanded=\\"false\\">foo</span></div>"`;

View File

@@ -0,0 +1,54 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<Menu> closed Menu DOM with only required props and element child 1`] = `""`;
exports[`<Menu> closed menu DOM with all possible props and function child 1`] = `
<ul
className="bar"
data-something-something="seven"
id="foo"
onKeyDown={[Function]}
role="menu"
style={
Object {
"bottom": 1,
}
}
tabIndex={-1}
>
isOpen = false
</ul>
`;
exports[`<Menu> open Menu DOM with only required props and element child 1`] = `
<div
onKeyDown={[Function]}
role="menu"
tabIndex={-1}
>
<div>
<div>
foo
</div>
</div>
</div>
`;
exports[`<Menu> open menu DOM with all possible props and function child 1`] = `
<section
className="foo"
id="bar"
onKeyDown={[Function]}
role="menu"
style={
Object {
"left": 1,
}
}
tabIndex={-1}
>
isOpen = true
</section>
`;
exports[`<Menu> rendered via renderToString does not throw 1`] = `"<div></div>"`;

View File

@@ -0,0 +1,37 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<MenuItem> DOM with all possible props and element child 1`] = `
<li
className="foobar"
data-something-something="seven"
id="hogwash"
onClick={[Function]}
onKeyDown={[Function]}
role="menuitem"
style={
Object {
"right": "1em",
}
}
tabIndex="-1"
>
<a
href="#"
>
foo
</a>
</li>
`;
exports[`<MenuItem> DOM with only required props 1`] = `
<div
onClick={[Function]}
onKeyDown={[Function]}
role="menuitem"
tabIndex="-1"
>
foo
</div>
`;
exports[`<MenuItem> rendered via renderToString does not throw 1`] = `"<div><div role=\\"menuitem\\" tabindex=\\"-1\\">foo</div></div>"`;

View File

@@ -0,0 +1,190 @@
/* globals Promise */
var createManager = require('../createManager');
var createMockKeyEvent = require('./helpers/createMockKeyEvent');
var mockNode = document.createElement('button');
var nodeOne = document.createElement('button');
nodeOne.focus = jest.fn();
var nodeTwo = document.createElement('button');
nodeTwo.focus = jest.fn();
document.body.appendChild(nodeOne);
document.body.appendChild(nodeTwo);
function createManagerWithMockedElements(options) {
var manager = createManager(options);
manager.focusGroup.addMember({
node: nodeOne,
text: 'first'
});
manager.focusGroup.addMember({
node: nodeTwo,
text: 'second'
});
manager.button = {
ref: { current: mockNode },
setState: jest.fn()
};
manager.menu = {
setState: jest.fn()
};
manager.focusItem = jest.fn();
return manager;
}
describe('createManager', function() {
it('initalizes', function() {
var manager = createManagerWithMockedElements();
expect(manager.isOpen).toBe(false);
expect(manager.options.closeOnSelection).toBeTruthy();
expect(manager.options.closeOnBlur).toBeTruthy();
});
it('Manager#update', function() {
var manager = createManagerWithMockedElements({
onMenuToggle: jest.fn()
});
manager.update();
expect(manager.menu.setState).toHaveBeenCalledTimes(1);
expect(manager.menu.setState.mock.calls[0]).toEqual([
{ isOpen: manager.isOpen }
]);
expect(manager.button.setState).toHaveBeenCalledTimes(1);
expect(manager.button.setState.mock.calls[0]).toEqual([
{ menuOpen: manager.isOpen }
]);
expect(manager.options.onMenuToggle).toHaveBeenCalledTimes(1);
expect(manager.options.onMenuToggle.mock.calls[0]).toEqual([
{ isOpen: manager.isOpen }
]);
});
it('Manager#openMenu without focusing in menu', function() {
var manager = createManagerWithMockedElements();
manager.openMenu({ focusMenu: false });
expect(manager.isOpen).toBe(true);
expect(manager.menu.setState).toHaveBeenCalledTimes(1);
expect(manager.menu.setState.mock.calls[0]).toEqual([{ isOpen: true }]);
expect(manager.button.setState).toHaveBeenCalledTimes(1);
expect(manager.button.setState.mock.calls[0]).toEqual([{ menuOpen: true }]);
return new Promise(function(resolve) {
setTimeout(function() {
expect(manager.focusItem).toHaveBeenCalledTimes(0);
resolve();
}, 0);
});
});
it('Manager#openMenu focusing in menu', function() {
var manager = createManagerWithMockedElements();
manager.openMenu();
expect(manager.isOpen).toBe(true);
expect(manager.menu.setState).toHaveBeenCalledTimes(1);
expect(manager.menu.setState.mock.calls[0]).toEqual([{ isOpen: true }]);
expect(manager.button.setState).toHaveBeenCalledTimes(1);
expect(manager.button.setState.mock.calls[0]).toEqual([{ menuOpen: true }]);
return new Promise(function(resolve) {
setTimeout(function() {
expect(manager.focusItem).toHaveBeenCalledTimes(1);
expect(manager.focusItem.mock.calls[0]).toEqual([0]);
resolve();
}, 0);
});
});
it('Manager#closeMenu focusing on button', function() {
mockNode.focus = jest.fn();
var manager = createManagerWithMockedElements();
manager.isOpen = true;
manager.closeMenu({ focusButton: true });
expect(manager.isOpen).toBe(false);
expect(manager.menu.setState).toHaveBeenCalledTimes(1);
expect(manager.menu.setState.mock.calls[0]).toEqual([{ isOpen: false }]);
expect(manager.button.setState).toHaveBeenCalledTimes(1);
expect(manager.button.setState.mock.calls[0]).toEqual([
{ menuOpen: false }
]);
expect(mockNode.focus).toHaveBeenCalledTimes(1);
});
it('Manager#closeMenu without focusing on button', function() {
mockNode.focus = jest.fn();
var manager = createManagerWithMockedElements();
manager.isOpen = true;
manager.closeMenu({ focusButton: false });
expect(mockNode.focus).not.toHaveBeenCalled();
});
it('Manager#toggleMenu when closed', function() {
var manager = createManagerWithMockedElements();
manager.openMenu = jest.fn();
manager.closeMenu = jest.fn();
manager.toggleMenu();
expect(manager.openMenu).toHaveBeenCalledTimes(1);
expect(manager.closeMenu).not.toHaveBeenCalled();
});
it('Manager#toggleMenu when open', function() {
var manager = createManagerWithMockedElements();
manager.isOpen = true;
manager.openMenu = jest.fn();
manager.closeMenu = jest.fn();
manager.toggleMenu();
expect(manager.openMenu).not.toHaveBeenCalled();
expect(manager.closeMenu).toHaveBeenCalledTimes(1);
});
it('Manager#handleSelection A', function() {
var mockOnSelection = jest.fn();
var manager = createManagerWithMockedElements({
onSelection: mockOnSelection
});
manager.closeMenu = jest.fn();
manager.handleSelection('foo', { bar: 1 });
expect(manager.closeMenu).toHaveBeenCalledTimes(1);
expect(manager.closeMenu.mock.calls[0]).toEqual([{ focusButton: true }]);
expect(mockOnSelection).toHaveBeenCalledTimes(1);
expect(mockOnSelection.mock.calls[0]).toEqual(['foo', { bar: 1 }]);
});
it('Manager#handleSelection B', function() {
var mockOnSelection = jest.fn();
var manager = createManagerWithMockedElements({
onSelection: mockOnSelection,
closeOnSelection: false
});
manager.closeMenu = jest.fn();
manager.handleSelection('foo', { bar: 1 });
expect(manager.closeMenu).not.toHaveBeenCalled();
expect(mockOnSelection).toHaveBeenCalledTimes(1);
expect(mockOnSelection.mock.calls[0]).toEqual(['foo', { bar: 1 }]);
});
it('Manager#handleMenuKey on closed menu', function() {
var escapeEvent = createMockKeyEvent('Escape');
var manager = createManagerWithMockedElements();
manager.closeMenu = jest.fn();
manager.handleMenuKey(escapeEvent);
expect(escapeEvent.preventDefault).not.toHaveBeenCalled();
expect(manager.closeMenu).not.toHaveBeenCalled();
});
it('Manager#handleMenuKey on open menu', function() {
var escapeEvent = createMockKeyEvent('Escape');
var manager = createManagerWithMockedElements();
manager.isOpen = true;
manager.closeMenu = jest.fn();
manager.handleMenuKey(escapeEvent);
expect(escapeEvent.preventDefault).toHaveBeenCalledTimes(1);
expect(manager.closeMenu).toHaveBeenCalledTimes(1);
expect(manager.closeMenu.mock.calls[0]).toEqual([{ focusButton: true }]);
});
});

View File

@@ -0,0 +1,24 @@
const React = require('react');
const PropTypes = require('prop-types');
const ManagerContext = require('../../ManagerContext');
class MockWrapper extends React.Component {
static propTypes = {
mockManager: PropTypes.object.isRequired,
};
constructor(props) {
super(props);
this.manager = this.props.mockManager;
}
render() {
return React.createElement(
ManagerContext.Provider,
{ value: this.props.mockManager },
React.createElement('div', null, this.props.children)
);
}
}
module.exports = MockWrapper;

View File

@@ -0,0 +1,7 @@
module.exports = function(key, keyCode) {
return {
key: key,
keyCode: keyCode,
preventDefault: jest.fn(),
};
};

View File

@@ -0,0 +1,22 @@
module.exports = function() {
return {
isOpen: false,
toggleMenu: jest.fn(),
handleMenuKey: jest.fn(),
moveFocusDown: jest.fn(),
openMenu: jest.fn(),
handleKeyDown: jest.fn(),
handleClick: jest.fn(),
handleSelection: jest.fn(),
handleButtonNonArrowKey: jest.fn(),
focusItem: jest.fn(),
menuItems: [1, 2],
clearItems: jest.fn(),
currentFocus: -1,
addItem: jest.fn(),
options: {
closeOnBlur: true,
closeOnSelection: true
}
};
}

View File

@@ -0,0 +1,5 @@
import './raf';
import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
Enzyme.configure({ adapter: new Adapter() });

View File

@@ -0,0 +1,3 @@
global.requestAnimationFrame = (callback) => {
setTimeout(callback, 0);
};

173
node_modules/react-aria-menubutton/src/createManager.js generated vendored Normal file
View File

@@ -0,0 +1,173 @@
const createFocusGroup = require('focus-group');
const externalStateControl = require('./externalStateControl');
const focusGroupOptions = {
wrap: true,
stringSearch: true
};
const protoManager = {
init(options) {
this.updateOptions(options);
this.handleBlur = handleBlur.bind(this);
this.handleSelection = handleSelection.bind(this);
this.handleMenuKey = handleMenuKey.bind(this);
// "With focus on the drop-down menu, the Up and Down Arrow
// keys move focus within the menu items, "wrapping" at the top and bottom."
// "Typing a letter (printable character) key moves focus to the next
// instance of a visible node whose title begins with that printable letter."
//
// All of the above is handled by focus-group.
this.focusGroup = createFocusGroup(focusGroupOptions);
// These component references are added when the relevant components mount
this.button = null;
this.menu = null;
// State trackers
this.isOpen = false;
},
updateOptions(options) {
const oldOptions = this.options;
this.options = options || this.options || {};
if (typeof this.options.closeOnSelection === 'undefined') {
this.options.closeOnSelection = true;
}
if (typeof this.options.closeOnBlur === 'undefined') {
this.options.closeOnBlur = true;
}
if (this.options.id) {
externalStateControl.registerManager(this.options.id, this);
}
if (oldOptions && oldOptions.id && oldOptions.id !== this.options.id) {
externalStateControl.unregisterManager(this.options.id, this);
}
},
focusItem(index) {
this.focusGroup.focusNodeAtIndex(index);
},
addItem(item) {
this.focusGroup.addMember(item);
},
clearItems() {
this.focusGroup.clearMembers();
},
handleButtonNonArrowKey(event) {
this.focusGroup._handleUnboundKey(event);
},
destroy() {
this.button = null;
this.menu = null;
this.focusGroup.deactivate();
clearTimeout(this.blurTimer);
clearTimeout(this.moveFocusTimer);
},
update() {
this.menu.setState({ isOpen: this.isOpen });
this.button.setState({ menuOpen: this.isOpen });
this.options.onMenuToggle &&
this.options.onMenuToggle({ isOpen: this.isOpen });
},
openMenu(openOptions) {
if (this.isOpen) return;
openOptions = openOptions || {};
if (openOptions.focusMenu === undefined) {
openOptions.focusMenu = true;
}
this.isOpen = true;
this.update();
this.focusGroup.activate();
if (openOptions.focusMenu) {
const self = this;
this.moveFocusTimer = setTimeout(function() {
self.focusItem(0);
}, 0);
}
},
closeMenu(closeOptions) {
if (!this.isOpen) return;
closeOptions = closeOptions || {};
this.isOpen = false;
this.update();
if (closeOptions.focusButton) {
this.button.ref.current.focus();
}
},
toggleMenu(closeOptions, openOptions) {
closeOptions = closeOptions || {};
openOptions = openOptions || {};
if (this.isOpen) {
this.closeMenu(closeOptions);
} else {
this.openMenu(openOptions);
}
}
};
function handleBlur() {
const self = this;
self.blurTimer = setTimeout(function() {
if (!self.button) return;
const buttonNode = self.button.ref.current;
if (!buttonNode) return;
const activeEl = buttonNode.ownerDocument.activeElement;
if (buttonNode && activeEl === buttonNode) return;
const menuNode = self.menu.ref.current;
if (menuNode === activeEl) {
self.focusItem(0);
return;
}
if (menuNode && menuNode.contains(activeEl)) return;
if (self.isOpen) self.closeMenu({ focusButton: false });
}, 0);
}
function handleSelection(value, event) {
if (this.options.closeOnSelection) this.closeMenu({ focusButton: true });
if (this.options.onSelection) this.options.onSelection(value, event);
}
function handleMenuKey(event) {
if (this.isOpen) {
switch (event.key) {
// With focus on the drop-down menu, pressing Escape closes
// the menu and returns focus to the button.
case 'Escape':
event.preventDefault();
this.closeMenu({ focusButton: true });
break;
case 'Home':
event.preventDefault();
this.focusGroup.moveFocusToFirst();
break;
case 'End':
event.preventDefault();
this.focusGroup.moveFocusToLast();
break;
}
}
}
module.exports = function(options) {
const newManager = Object.create(protoManager);
newManager.init(options);
return newManager;
};

View File

@@ -0,0 +1,31 @@
const registeredManagers = {};
const errorCommon =
'a menu outside a mounted Wrapper with an id, or a menu that does not exist';
function registerManager(menuId, manager) {
registeredManagers[menuId] = manager;
}
function unregisterManager(menuId) {
delete registeredManagers[menuId];
}
function openMenu(menuId, openOptions) {
const manager = registeredManagers[menuId];
if (!manager) throw new Error('Cannot open ' + errorCommon);
manager.openMenu(openOptions);
}
function closeMenu(menuId, closeOptions) {
const manager = registeredManagers[menuId];
if (!manager) throw new Error('Cannot close ' + errorCommon);
manager.closeMenu(closeOptions);
}
module.exports = {
registerManager: registerManager,
unregisterManager: unregisterManager,
openMenu: openMenu,
closeMenu: closeMenu
};

10
node_modules/react-aria-menubutton/src/index.js generated vendored Normal file
View File

@@ -0,0 +1,10 @@
const externalStateControl = require('./externalStateControl');
module.exports = {
Wrapper: require('./Wrapper'),
Button: require('./Button'),
Menu: require('./Menu'),
MenuItem: require('./MenuItem'),
openMenu: externalStateControl.openMenu,
closeMenu: externalStateControl.closeMenu
};

8
node_modules/react-aria-menubutton/src/propTypes.js generated vendored Normal file
View File

@@ -0,0 +1,8 @@
const PropTypes = require("prop-types");
module.exports = {
refType: PropTypes.oneOfType([
PropTypes.func,
PropTypes.shape({ current: PropTypes.elementType })
])
};

View File

@@ -0,0 +1,9 @@
module.exports = function(a, b, reserved) {
reserved = reserved || {};
// This will get id, className, style, etc.
for (var x in b) {
if (!b.hasOwnProperty(x)) continue;
if (reserved[x]) continue;
a[x] = b[x];
}
};