Skip to content

Commit 2196974

Browse files
Gilad Leknerglekner
Gilad Lekner
authored andcommitted
feat(ApplicationLauncher): application Launcher Component
affects: patternfly-react fix #184 ISSUES CLOSED: #184
1 parent d51c760 commit 2196974

13 files changed

+788
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
import classNames from 'classnames';
4+
import ApplicationLauncherToggle from './ApplicationLauncherToggle';
5+
import { Dropdown } from '../Dropdown';
6+
7+
class ApplicationLauncher extends React.Component {
8+
constructor(props) {
9+
super(props);
10+
11+
this.state = {
12+
showLauncher: false
13+
};
14+
}
15+
16+
toggleLauncher = () => {
17+
this.setState({ showLauncher: !this.state.showLauncher });
18+
};
19+
20+
render() {
21+
const classes = classNames(
22+
'applauncher-pf dropdown dropdown-kebab-pf',
23+
{
24+
'applauncher-pf-block-list': this.props.grid
25+
},
26+
{ open: this.state.showLauncher }
27+
);
28+
return (
29+
<li className={classes}>
30+
<ApplicationLauncherToggle
31+
tooltip={this.props.tooltip}
32+
tooltipPlacement={this.props.tooltipPlacement}
33+
onClick={this.toggleLauncher}
34+
/>
35+
<Dropdown.Menu className="dropdown-menu-right">
36+
{this.props.children}
37+
</Dropdown.Menu>
38+
</li>
39+
);
40+
}
41+
}
42+
ApplicationLauncher.propTypes = {
43+
/** Children Node */
44+
children: PropTypes.node.isRequired,
45+
/** Toggle Tooltip */
46+
tooltip: PropTypes.string,
47+
/** tooltipPlacement */
48+
tooltipPlacement: PropTypes.string,
49+
/** Application Launcher Type (Default List) */
50+
grid: PropTypes.bool
51+
};
52+
ApplicationLauncher.defaultProps = {
53+
tooltip: 'Application Launcher',
54+
tooltipPlacement: 'left',
55+
grid: false
56+
};
57+
58+
export default ApplicationLauncher;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { storiesOf } from '@storybook/react';
2+
import { withKnobs } from '@storybook/addon-knobs';
3+
import {
4+
NavApplicationLauncherStory,
5+
WrapperNavApplicationLauncherStory
6+
} from './Stories/index';
7+
import { name } from '../../../package.json';
8+
9+
const stories = storiesOf(`${name}/ApplicationLauncher`, module);
10+
stories.addDecorator(withKnobs);
11+
12+
NavApplicationLauncherStory(stories);
13+
WrapperNavApplicationLauncherStory(stories);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import React from 'react';
2+
import { mount } from 'enzyme';
3+
import {
4+
ApplicationLauncher,
5+
ApplicationLauncherItem,
6+
ApplicationLauncherToggle
7+
} from './index';
8+
9+
const handleClick = e => {
10+
e.preventDefault();
11+
};
12+
13+
test('ApplicationLauncher is working properly', () => {
14+
const component = mount(
15+
<ApplicationLauncher type="grid" tooltipPlacement="left">
16+
<ApplicationLauncherItem
17+
icon="pficon pficon-storage-domain"
18+
title="Recteque"
19+
tooltip="Tooltip!"
20+
onClick={handleClick}
21+
/>
22+
</ApplicationLauncher>
23+
);
24+
25+
expect(component.render()).toMatchSnapshot();
26+
});
27+
28+
test('ApplicationLauncherItem is working properly', () => {
29+
const component = mount(
30+
<ApplicationLauncherItem
31+
icon="pficon pficon-storage-domain"
32+
title="Recteque"
33+
tooltip="Tooltip!"
34+
onClick={handleClick}
35+
/>
36+
);
37+
38+
expect(component.render()).toMatchSnapshot();
39+
});
40+
41+
test('ApplicationLauncherToggle is working properly', () => {
42+
const component = mount(
43+
<ApplicationLauncherToggle tooltipPlacement="left" onClick={handleClick} />
44+
);
45+
46+
expect(component.render()).toMatchSnapshot();
47+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import classNames from 'classnames';
2+
import React from 'react';
3+
import PropTypes from 'prop-types';
4+
import { Tooltip } from '../Tooltip';
5+
import { OverlayTrigger } from '../OverlayTrigger';
6+
import { Icon } from '../Icon';
7+
8+
const ApplicationLauncherItem = ({
9+
onClick,
10+
tooltip,
11+
tooltipPlacement,
12+
title,
13+
icon,
14+
noIcons,
15+
className,
16+
...props
17+
}) => {
18+
const classes = classNames('applauncher-pf-item', className);
19+
20+
if (tooltip !== null) {
21+
return (
22+
<OverlayTrigger
23+
overlay={<Tooltip id="tooltip">{tooltip}</Tooltip>}
24+
placement={tooltipPlacement}
25+
trigger={['hover', 'focus']}
26+
rootClose={false}
27+
>
28+
<li className={classes} role="menuitem">
29+
<a className="applauncher-pf-link" href="#" onClick={e => onClick(e)}>
30+
{!noIcons && (
31+
<Icon
32+
type="pf"
33+
name={icon}
34+
className="applauncher-pf-link-icon"
35+
/>
36+
)}
37+
<span className="applauncher-pf-link-title">{title}</span>
38+
</a>
39+
</li>
40+
</OverlayTrigger>
41+
);
42+
}
43+
return (
44+
<li className={classes} role="menuitem">
45+
<a className="applauncher-pf-link" href="#" onClick={e => onClick(e)}>
46+
{!noIcons && (
47+
<Icon type="pf" name={icon} className="applauncher-pf-link-icon" />
48+
)}
49+
<span className="applauncher-pf-link-title">{title}</span>
50+
</a>
51+
</li>
52+
);
53+
};
54+
ApplicationLauncherItem.propTypes = {
55+
/** Additional element css classes */
56+
className: PropTypes.string,
57+
/** onClick func */
58+
onClick: PropTypes.func,
59+
/** Title String */
60+
title: PropTypes.string.isRequired,
61+
/** Icon Type */
62+
icon: PropTypes.string.isRequired,
63+
/** App Tooltip */
64+
tooltip: PropTypes.string,
65+
/** Tooltip Placement */
66+
tooltipPlacement: PropTypes.string,
67+
/** No Icons Bool */
68+
noIcons: PropTypes.bool
69+
};
70+
ApplicationLauncherItem.defaultProps = {
71+
className: '',
72+
onClick: null,
73+
noIcons: false,
74+
tooltipPlacement: 'left',
75+
tooltip: null
76+
};
77+
export default ApplicationLauncherItem;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
import { OverlayTrigger } from '../OverlayTrigger';
4+
import { Tooltip } from '../Tooltip';
5+
import { Icon } from '../Icon';
6+
import { Button } from '../Button';
7+
8+
const ApplicationLauncherToggle = ({ tooltip, onClick, tooltipPlacement }) => (
9+
<OverlayTrigger
10+
placement={tooltipPlacement}
11+
id="applauncher-pf-block-list"
12+
overlay={<Tooltip id="tooltip">{tooltip}</Tooltip>}
13+
>
14+
<Button onClick={onClick} bsStyle="link" className="nav-item-iconic">
15+
<Icon
16+
className="fa fa-th applauncher-pf-icon"
17+
aria-describedby="tooltip"
18+
name=""
19+
/>
20+
</Button>
21+
</OverlayTrigger>
22+
);
23+
ApplicationLauncherToggle.propTypes = {
24+
/** onClick func */
25+
onClick: PropTypes.func,
26+
/** tooltipPlacement */
27+
tooltipPlacement: PropTypes.string,
28+
/** Toggle Tooltip */
29+
tooltip: PropTypes.string
30+
};
31+
ApplicationLauncherToggle.defaultProps = {
32+
onClick: null,
33+
tooltipPlacement: 'bottom',
34+
tooltip: 'Application Launcher'
35+
};
36+
37+
export default ApplicationLauncherToggle;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import React from 'react';
2+
import { action } from '@storybook/addon-actions';
3+
import { boolean, select } from '@storybook/addon-knobs';
4+
import { inlineTemplate } from '../../../../../../storybook/decorators/storyTemplates';
5+
import { DOCUMENTATION_URL } from '../../../../../../storybook/constants';
6+
import ApplicationLauncher from '../ApplicationLauncher';
7+
import ApplicationLauncherItem from '../ApplicationLauncherItem';
8+
9+
const handleClick = e => {
10+
e.preventDefault();
11+
action('app clicked!')();
12+
};
13+
14+
const NavApplicationLauncherStory = stories => {
15+
stories.addWithInfo('Application Launcher', '', () => {
16+
const type = select(
17+
'Launcher Type',
18+
{ true: 'Grid', false: 'List' },
19+
'true'
20+
);
21+
22+
const iconBool = boolean('Icons', true);
23+
24+
const story = (
25+
<nav className="navbar navbar-pf-vertical">
26+
<nav className="collapse navbar-collapse">
27+
<ul className="nav navbar-nav navbar-right navbar-iconic">
28+
<ApplicationLauncher grid={type === 'true'} tooltipPlacement="left">
29+
<ApplicationLauncherItem
30+
icon="storage-domain"
31+
title="Recteque"
32+
tooltip="Tooltip!"
33+
tooltipPlacement="left"
34+
onClick={handleClick}
35+
noIcons={!iconBool}
36+
/>
37+
<ApplicationLauncherItem
38+
icon="virtual-machine"
39+
title="No Tooltip"
40+
onClick={handleClick}
41+
noIcons={!iconBool}
42+
/>
43+
<ApplicationLauncherItem
44+
icon="domain"
45+
title="Lorem"
46+
tooltip="Tooltip!"
47+
tooltipPlacement="left"
48+
onClick={handleClick}
49+
noIcons={!iconBool}
50+
/>
51+
<ApplicationLauncherItem
52+
icon="home"
53+
title="Home"
54+
tooltip="Tooltip!"
55+
tooltipPlacement="left"
56+
onClick={handleClick}
57+
noIcons={!iconBool}
58+
/>
59+
</ApplicationLauncher>
60+
</ul>
61+
</nav>
62+
</nav>
63+
);
64+
return inlineTemplate({
65+
title: 'ApplicationLauncher',
66+
documentationLink: `${
67+
DOCUMENTATION_URL.PATTERNFLY_ORG_APPLICATION_FRAMEWORK
68+
}launcher/`,
69+
story
70+
});
71+
});
72+
};
73+
74+
export default NavApplicationLauncherStory;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import React from 'react';
2+
import { action } from '@storybook/addon-actions';
3+
import { boolean, select } from '@storybook/addon-knobs';
4+
import { inlineTemplate } from '../../../../../../storybook/decorators/storyTemplates';
5+
import { DOCUMENTATION_URL } from '../../../../../../storybook/constants';
6+
import ApplicationLauncherWrapper from '../Wrappers/ApplicationLauncherWrapper';
7+
8+
const handleClick = e => {
9+
e.preventDefault();
10+
action('app clicked!')();
11+
};
12+
13+
const Apps = [
14+
{
15+
title: 'No Tooltip',
16+
icon: 'home',
17+
onClick: e => {
18+
handleClick(e);
19+
}
20+
},
21+
{
22+
title: 'Royal',
23+
icon: 'virtual-machine',
24+
tooltip: 'Tooltip!',
25+
onClick: e => {
26+
handleClick(e);
27+
}
28+
},
29+
{
30+
title: 'Lemon',
31+
icon: 'storage-domain',
32+
tooltip: 'Tooltip!',
33+
onClick: e => {
34+
handleClick(e);
35+
}
36+
},
37+
{
38+
title: 'Domain',
39+
icon: 'domain',
40+
tooltip: 'Tooltip!',
41+
onClick: e => {
42+
handleClick(e);
43+
}
44+
}
45+
];
46+
47+
const WrapperNavApplicationLauncherStory = stories => {
48+
stories.addWithInfo('Wrapper Application Launcher', '', () => {
49+
const type = select(
50+
'Launcher Type',
51+
{ true: 'Grid', false: 'List' },
52+
'true'
53+
);
54+
const iconBool = boolean('Icons', true);
55+
56+
const story = (
57+
<nav className="navbar navbar-pf-vertical">
58+
<nav className="collapse navbar-collapse">
59+
<ul className="nav navbar-nav navbar-right navbar-iconic">
60+
<ApplicationLauncherWrapper
61+
apps={Apps}
62+
noIcons={!iconBool}
63+
grid={type === 'true'}
64+
tooltip="App Launcher Tooltip"
65+
tooltipPlacement="left"
66+
/>
67+
</ul>
68+
</nav>
69+
</nav>
70+
);
71+
return inlineTemplate({
72+
title: 'WrapperApplicationLauncher',
73+
documentationLink: `${
74+
DOCUMENTATION_URL.PATTERNFLY_ORG_APPLICATION_FRAMEWORK
75+
}launcher/`,
76+
story
77+
});
78+
});
79+
};
80+
81+
export default WrapperNavApplicationLauncherStory;

0 commit comments

Comments
 (0)