-
Notifications
You must be signed in to change notification settings - Fork 349
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
732 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import { Dropdown } from '../Dropdown'; | ||
|
||
class InfoTip extends React.Component { | ||
constructor(props) { | ||
super(props); | ||
this.handleKeyDown = this.handleKeyDown.bind(this); | ||
this.handleClick = this.handleClick.bind(this); | ||
this.handleBackFocus = this.handleBackFocus.bind(this); | ||
this.state = { open: false, footerFocused: false }; | ||
} | ||
|
||
handleEnterKeyDown(event) { | ||
this.setState({ open: !this.state.open }); | ||
event.preventDefault(); | ||
} | ||
|
||
handleTabKeyDown(event) { | ||
if (this.state.footerFocused) { | ||
this.setState({ open: false, footerFocused: false }); | ||
} else { | ||
this.setState({ footerFocused: true }); | ||
} | ||
event.stopPropagation(); | ||
event.nativeEvent.stopImmediatePropagation(); | ||
} | ||
|
||
handleKeyDown(event) { | ||
console.log(event.keyCode); | ||
if (event.shiftKey && event.keyCode) { | ||
return this.handleBackFocus(); | ||
} | ||
switch (event.key) { | ||
case 'Enter': | ||
return this.handleEnterKeyDown(event); | ||
case 'Tab': | ||
return this.handleTabKeyDown(event); | ||
case 'Escape': | ||
return this.setState({ open: false }); | ||
default: | ||
} | ||
} | ||
|
||
handleBackFocus() { | ||
if (this.state.open) { | ||
this.setState({ open: false }); | ||
} | ||
} | ||
|
||
handleClick(event) { | ||
event.preventDefault(); | ||
this.setState({ open: !this.state.open }); | ||
} | ||
|
||
render() { | ||
const { children, onToggle, ...props } = this.props; | ||
|
||
return ( | ||
<Dropdown | ||
componentClass="li" | ||
onClick={this.handleClick} | ||
onKeyDown={this.handleKeyDown} | ||
onToggle={this.handleKeyDown} | ||
open={this.state.open} | ||
{...props} | ||
> | ||
{children} | ||
</Dropdown> | ||
); | ||
} | ||
} | ||
|
||
InfoTip.propTypes = { | ||
...Dropdown.propTypes, | ||
children: PropTypes.node.isRequired, | ||
id: PropTypes.string.isRequired | ||
}; | ||
|
||
export default InfoTip; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import React from 'react'; | ||
import { storiesOf } from '@storybook/react'; | ||
import { defaultTemplate } from '../../../storybook/decorators/storyTemplates'; | ||
import { DOCUMENTATION_URL } from '../../../storybook/constants'; | ||
import { ListGroup, ListGroupItem } from '../ListGroup'; | ||
|
||
import { InfoTip } from './index'; | ||
|
||
const stories = storiesOf('InfoTip', module); | ||
|
||
stories.addDecorator( | ||
defaultTemplate({ | ||
title: 'InfoTip', | ||
documentationLink: DOCUMENTATION_URL.PATTERNFLY_ORG_WIDGETS + '#info-tip' | ||
}) | ||
); | ||
|
||
stories.addWithInfo('InfoTip', '', () => ( | ||
<nav className="navbar navbar-default navbar-pf" role="navigation"> | ||
<div className="navbar-header"> | ||
<button | ||
type="button" | ||
className="navbar-toggle" | ||
data-toggle="collapse" | ||
data-target=".navbar-collapse-1" | ||
> | ||
<span className="sr-only">Toggle navigation</span> | ||
<span className="icon-bar" /> | ||
<span className="icon-bar" /> | ||
<span className="icon-bar" /> | ||
</button> | ||
<a className="navbar-brand" href="/"> | ||
<img | ||
src="http://www.patternfly.org/assets/img/brand.svg" | ||
alt="PatternFly Enterprise Application" | ||
/> | ||
</a> | ||
</div> | ||
<div className="collapse navbar-collapse navbar-collapse-1"> | ||
<ul className="nav navbar-nav navbar-utility"> | ||
<InfoTip id="infotip-widget"> | ||
<InfoTip.Toggle>Messages: 2</InfoTip.Toggle> | ||
<InfoTip.Menu> | ||
<ListGroup> | ||
<ListGroupItem> | ||
<InfoTip.MenuItemIcon type="pf" name="info" /> Added Datasources | ||
TestDS | ||
</ListGroupItem> | ||
<ListGroupItem> | ||
<InfoTip.MenuItemIcon type="pf" name="info" /> Modified | ||
Datasources ExampleDS | ||
</ListGroupItem> | ||
</ListGroup> | ||
<InfoTip.MenuFooter> | ||
<a href="#">Clear Messages</a> | ||
</InfoTip.MenuFooter> | ||
</InfoTip.Menu> | ||
</InfoTip> | ||
</ul> | ||
<ul className="nav navbar-nav navbar-primary"> | ||
<li> | ||
<a href="#">First Link</a> | ||
</li> | ||
</ul> | ||
</div> | ||
</nav> | ||
)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,217 @@ | ||
/* eslint-env jest */ | ||
|
||
import React from 'react'; | ||
import renderer from 'react-test-renderer'; | ||
import { InfoTip } from './index'; | ||
import { ListGroup, ListGroupItem } from '../ListGroup'; | ||
|
||
test('InfoTip renders properly with item children', () => { | ||
const component = renderer.create( | ||
<InfoTip id="infotip-widget"> | ||
<InfoTip.Toggle>Messages: 2</InfoTip.Toggle> | ||
<InfoTip.Menu> | ||
<ListGroup> | ||
<ListGroupItem> | ||
<InfoTip.MenuItemIcon type="pf" name="info" /> Added Datasources | ||
TestDS | ||
</ListGroupItem> | ||
<ListGroupItem> | ||
<InfoTip.MenuItemIcon type="pf" name="info" /> Modified Datasources | ||
ExampleDS | ||
</ListGroupItem> | ||
</ListGroup> | ||
<InfoTip.MenuFooter> | ||
<a href="#">Clear Messages</a> | ||
</InfoTip.MenuFooter> | ||
</InfoTip.Menu> | ||
</InfoTip> | ||
); | ||
|
||
const tree = component.toJSON(); | ||
expect(tree).toMatchSnapshot(); | ||
}); | ||
|
||
test('InfoTip renders properly with a default MenuItemIcon', () => { | ||
const component = renderer.create( | ||
<InfoTip id="infotip-widget"> | ||
<InfoTip.Toggle>Messages: 2</InfoTip.Toggle> | ||
<InfoTip.Menu> | ||
<ListGroup> | ||
<ListGroupItem> | ||
<InfoTip.MenuItemIcon /> Added Datasources TestDS | ||
</ListGroupItem> | ||
<ListGroupItem> | ||
<InfoTip.MenuItemIcon /> Modified Datasources ExampleDS | ||
</ListGroupItem> | ||
</ListGroup> | ||
<InfoTip.MenuFooter> | ||
<a href="#">Clear Messages</a> | ||
</InfoTip.MenuFooter> | ||
</InfoTip.Menu> | ||
</InfoTip> | ||
); | ||
|
||
const tree = component.toJSON(); | ||
expect(tree).toMatchSnapshot(); | ||
}); | ||
|
||
test('InfoTip renders properly with another MenuItemIcon', () => { | ||
const component = renderer.create( | ||
<InfoTip id="infotip-widget"> | ||
<InfoTip.Toggle bsRole="toggle">Messages: 2</InfoTip.Toggle> | ||
<InfoTip.Menu bsRole="menu"> | ||
<ListGroup> | ||
<ListGroupItem> | ||
<InfoTip.MenuItemIcon type="pf" name="cube" /> Added Datasources | ||
TestDS | ||
</ListGroupItem> | ||
<ListGroupItem> | ||
<InfoTip.MenuItemIcon type="pf" name="check" /> Modified Datasources | ||
ExampleDS | ||
</ListGroupItem> | ||
</ListGroup> | ||
<InfoTip.MenuFooter> | ||
<a href="#">Clear Messages</a> | ||
</InfoTip.MenuFooter> | ||
</InfoTip.Menu> | ||
</InfoTip> | ||
); | ||
|
||
const tree = component.toJSON(); | ||
expect(tree).toMatchSnapshot(); | ||
}); | ||
|
||
const component = renderer.create( | ||
<InfoTip id="infotip-widget"> | ||
<InfoTip.Toggle>Messages: 2</InfoTip.Toggle> | ||
<InfoTip.Menu> | ||
<ListGroup> | ||
<ListGroupItem> | ||
<InfoTip.MenuItemIcon type="pf" name="info" /> Added Datasources | ||
TestDS | ||
</ListGroupItem> | ||
<ListGroupItem> | ||
<InfoTip.MenuItemIcon type="pf" name="info" /> Modified Datasources | ||
ExampleDS | ||
</ListGroupItem> | ||
</ListGroup> | ||
<InfoTip.MenuFooter> | ||
<a href="#">Clear Messages</a> | ||
</InfoTip.MenuFooter> | ||
</InfoTip.Menu> | ||
</InfoTip> | ||
); | ||
|
||
test('InfoTip is a instance', () => { | ||
var instance = component.getInstance(); | ||
expect(instance).toBeTruthy(); | ||
}); | ||
|
||
test('InfoTip handleEnterKeyDown', () => { | ||
var instance = component.getInstance(); | ||
var event = { | ||
preventDefault() {}, | ||
key: 'Enter', | ||
keyCode: 13, | ||
which: 13 | ||
}; | ||
expect(instance.state.wasEnabled).toBeFalsy(); | ||
expect(instance.state.open).toBeFalsy(); | ||
instance.handleEnterKeyDown(event); | ||
expect(instance.state.open).toBeTruthy(); | ||
instance.handleEnterKeyDown(event); | ||
expect(instance.state.open).toBeFalsy(); | ||
}); | ||
|
||
test('InfoTip handleTabKeyDown', () => { | ||
var instance = component.getInstance(); | ||
var event = { | ||
stopPropagation() {}, | ||
nativeEvent: { | ||
stopImmediatePropagation() {} | ||
}, | ||
key: 'Tab', | ||
keyCode: 9, | ||
which: 9 | ||
}; | ||
expect(instance.state.footerFocused).toBeFalsy(); | ||
// Should focus the Footer | ||
instance.handleTabKeyDown(event); | ||
expect(instance.state.footerFocused).toBeTruthy(); | ||
instance.state.open = true; | ||
// Should close the menu | ||
instance.handleTabKeyDown(event); | ||
expect(instance.state.footerFocused).toBeFalsy(); | ||
expect(instance.state.open).toBeFalsy(); | ||
}); | ||
|
||
test('InfoTip handleClick', () => { | ||
var instance = component.getInstance(); | ||
var event = { | ||
preventDefault() {} | ||
}; | ||
instance.state.open = false; | ||
|
||
// Should open the menu | ||
instance.handleClick(event); | ||
expect(instance.state.open).toBeTruthy(); | ||
|
||
// Should close the menu | ||
instance.handleClick(event); | ||
expect(instance.state.open).toBeFalsy(); | ||
}); | ||
|
||
test('InfoTip handleBackFocus', () => { | ||
var instance = component.getInstance(); | ||
instance.state.open = true; | ||
|
||
// Should close the menu | ||
instance.handleBackFocus(); | ||
expect(instance.state.open).toBeFalsy(); | ||
|
||
// Should do nothing if the menus is not open | ||
instance.handleBackFocus(); | ||
expect(instance.state.open).toBeFalsy(); | ||
}); | ||
|
||
test('InfoTip handleKeyDown', () => { | ||
var instance = component.getInstance(); | ||
var eventEnterKey = { | ||
preventDefault() {}, | ||
key: 'Enter', | ||
keyCode: 13, | ||
which: 13 | ||
}; | ||
instance.state.open = true; | ||
instance.handleKeyDown(eventEnterKey); | ||
expect(instance.state.open).toBeFalsy(); | ||
|
||
var eventTabKey = { | ||
stopPropagation() {}, | ||
nativeEvent: { | ||
stopImmediatePropagation() {} | ||
}, | ||
key: 'Tab', | ||
keyCode: 9, | ||
which: 9 | ||
}; | ||
instance.state.footerFocused = false; | ||
instance.handleKeyDown(eventTabKey); | ||
expect(instance.state.footerFocused).toBeTruthy(); | ||
instance.handleKeyDown(eventTabKey); | ||
expect(instance.state.open).toBeFalsy(); | ||
expect(instance.state.footerFocused).toBeFalsy(); | ||
|
||
var eventEscKey = { | ||
stopPropagation() {}, | ||
nativeEvent: { | ||
stopImmediatePropagation() {} | ||
}, | ||
key: 'Escape', | ||
keyCode: 27, | ||
which: 27 | ||
}; | ||
instance.state.open = true; | ||
instance.handleKeyDown(eventEscKey); | ||
expect(instance.state.open).toBeFalsy(); | ||
}); |
Oops, something went wrong.