diff --git a/assets/.gitkeep b/assets/.gitkeep
new file mode 100644
index 000000000..e69de29bb
diff --git a/assets/01.png b/assets/01.png
new file mode 100644
index 000000000..a96e9e2b8
Binary files /dev/null and b/assets/01.png differ
diff --git a/assets/02.png b/assets/02.png
new file mode 100644
index 000000000..d4cff671b
Binary files /dev/null and b/assets/02.png differ
diff --git a/assets/03.png b/assets/03.png
new file mode 100644
index 000000000..c77fb7411
Binary files /dev/null and b/assets/03.png differ
diff --git a/assets/04.png b/assets/04.png
new file mode 100644
index 000000000..2267815a3
Binary files /dev/null and b/assets/04.png differ
diff --git a/assets/05.png b/assets/05.png
new file mode 100644
index 000000000..58d5d3e87
Binary files /dev/null and b/assets/05.png differ
diff --git a/assets/06.png b/assets/06.png
new file mode 100644
index 000000000..4cab086dc
Binary files /dev/null and b/assets/06.png differ
diff --git a/assets/07.png b/assets/07.png
new file mode 100644
index 000000000..d40dd55de
Binary files /dev/null and b/assets/07.png differ
diff --git a/assets/08.png b/assets/08.png
new file mode 100644
index 000000000..ec531051b
Binary files /dev/null and b/assets/08.png differ
diff --git a/assets/09.png b/assets/09.png
new file mode 100644
index 000000000..310f92ea6
Binary files /dev/null and b/assets/09.png differ
diff --git a/assets/10.png b/assets/10.png
new file mode 100644
index 000000000..8e5837f87
Binary files /dev/null and b/assets/10.png differ
diff --git a/assets/11.png b/assets/11.png
new file mode 100644
index 000000000..4b646af23
Binary files /dev/null and b/assets/11.png differ
diff --git a/assets/12.png b/assets/12.png
new file mode 100644
index 000000000..883bf95cb
Binary files /dev/null and b/assets/12.png differ
diff --git a/assets/headshot-male.jpg b/assets/headshot-male.jpg
new file mode 100644
index 000000000..6e93b6811
Binary files /dev/null and b/assets/headshot-male.jpg differ
diff --git a/assets/index.js b/assets/index.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/App.scss b/src/App.scss
index 117333ff5..73ec2b728 100644
--- a/src/App.scss
+++ b/src/App.scss
@@ -23,7 +23,7 @@ $fd-fonts-path: '../node_modules/fiori-fundamentals/dist/fonts/';
}
.fd-popover {
- margin-right: 60px;
+ // margin-right: 60px;
}
.fd-button--grouped {
diff --git a/src/Button/Button.js b/src/Button/Button.js
index dec630ecb..e9573f7fc 100644
--- a/src/Button/Button.js
+++ b/src/Button/Button.js
@@ -35,7 +35,7 @@ export const Button = props => {
};
Button.propTypes = {
- option: PropTypes.oneOf(['', 'emphasized' , 'light']),
+ option: PropTypes.oneOf(['', 'emphasized' , 'light', 'shell']),
type: PropTypes.oneOf(['', 'standard' , 'positive', 'negative', 'medium']),
compact: PropTypes.bool,
glyph: PropTypes.string,
diff --git a/src/Menu/Menu.js b/src/Menu/Menu.js
index e77b50ef4..c8d327522 100644
--- a/src/Menu/Menu.js
+++ b/src/Menu/Menu.js
@@ -4,79 +4,69 @@ import { Link } from 'react-router-dom';
// ------------------------------------------- Menu ------------------------------------------
export const Menu = props => {
- const { id, addonBefore, children } = props;
- return (
-
- {children}
-
- );
+ const { id, addonBefore, children } = props;
+ return (
+
+ {children}
+
+ );
};
Menu.propTypes = {
- id: PropTypes.string,
- addonBefore: PropTypes.bool
+ id: PropTypes.string,
+ addonBefore: PropTypes.bool
};
// ---------------------------------------- Menu List ----------------------------------------
export const MenuList = props => {
- const { children } = props;
- return
;
+ const { children } = props;
+ return ;
};
// ---------------------------------------- Menu Item ----------------------------------------
export const MenuItem = props => {
- const { url, link, isLink, separator, addon, children } = props;
- return (
-
-
- {addon ? (
-
- { }
-
- ) : null}
- {link ? (
-
- {children}
-
- ) : null}
- {url ? (
-
- {children}
-
- ) : null}
-
- {separator ? : null}
-
- );
+ const { url, link, isLink, separator, addon, children, onclick } = props;
+ return (
+
+
+ {addon ? (
+ { }
+ ) : null}
+ {link ? (
+
+ {children}
+
+ ) : null}
+ {url ? (
+
+ {children}
+
+ ) : null}
+ {!url && !link ? {children} : null}
+
+ {separator ? : null}
+
+ );
};
MenuItem.propTypes = {
- url: PropTypes.string,
- separator: PropTypes.bool,
- addon: PropTypes.string,
- isLink: PropTypes.bool
+ url: PropTypes.string,
+ separator: PropTypes.bool,
+ addon: PropTypes.string,
+ isLink: PropTypes.bool
};
// ---------------------------------------- Menu Group ----------------------------------------
export const MenuGroup = props => {
- const { title, children } = props;
- return (
-
-
{title}
- {children}
-
- );
+ const { title, children } = props;
+ return (
+
+
{title}
+ {children}
+
+ );
};
MenuGroup.propTypes = {
- title: PropTypes.string
+ title: PropTypes.string
};
diff --git a/src/Routes.js b/src/Routes.js
index 17f473b65..29420e830 100644
--- a/src/Routes.js
+++ b/src/Routes.js
@@ -43,6 +43,7 @@ import { ToggleComponent } from './Toggle/Toggle.Component';
import { TreeComponent } from './Tree/Tree.Component';
import { TimeComponent } from './Time/Time.Component';
import { TimePickerComponent } from './TimePicker/TimePicker.Component';
+import { ShellbarComponent } from './Shellbar/Shellbar.Component';
export default class Routes extends Component {
constructor(props) {
@@ -131,6 +132,11 @@ export default class Routes extends Component {
name: 'Search Input',
component: SearchInputComponent
},
+ {
+ url: '/shellbar',
+ name: 'Shellbar',
+ component: ShellbarComponent
+ },
{
url: '/sideNavigation',
name: 'Side Navigation',
diff --git a/src/Shellbar/Shellbar.Component.js b/src/Shellbar/Shellbar.Component.js
new file mode 100644
index 000000000..7aedec569
--- /dev/null
+++ b/src/Shellbar/Shellbar.Component.js
@@ -0,0 +1,246 @@
+import React from 'react';
+import { DocsTile, DocsText, Separator, Header, Description, Import, Properties, Menu, MenuList, MenuItem } from '..';
+import { Shellbar } from '..';
+var images = require.context('../../assets', true);
+
+export const ShellbarComponent = () => {
+ const simpleShellbarExampleCode = ` }
+ productTitle="Corporate Portal"
+ user={user1}
+ userMenu={userMenu}
+/>
+
+const user1 = {
+ initials: 'JS',
+ userName: 'John Snow',
+ colorAccent: 11
+};
+
+const userMenu = [
+ { name: 'Settings', glyph: 'action-settings', size: 's', callback: () => alert('Settings selected!') },
+ { name: 'Sign Out', glyph: 'log', size: 's', callback: () => alert('Sign Out selected!') }
+];`;
+
+ const shellbarExampleCode = ` }
+ productTitle="Corporate Portal"
+ productMenu={productMenu}
+ subtitle="Subtitle"
+ copilot
+ actions={actions}
+ user={user}
+ userMenu={userMenu}
+ productSwitcher={productSwitcherList}
+ productSwitcherCollapsed={productSwitcherCollapsed}
+/>
+
+const productMenu = [
+ { name: 'Application A', callback: () => alert('Application A selected!') },
+ { name: 'Application B', callback: () => alert('Application B selected!') },
+ { name: 'Application C', callback: () => alert('Application C selected!') },
+ { name: 'Application D', callback: () => alert('Application D selected!') }
+];
+
+const actions = [
+ { glyph: 'bell', notificationCount: 21, label: 'Notifications', callback: () => alert('Notification selected!')},
+ { glyph: 'post', notificationCount: 4, label: 'Post', callback: () => alert('Post selected!')},
+ { glyph: 'settings', label: 'Settings', notificationCount: 0, callback: () => alert('Settings selected!'), menu: (
+
+
+ Option 1
+ Option 2
+ Option 3
+
+
+ )}
+];
+
+const user = {
+ initials: 'JS',
+ image: images('./headshot-male.jpg')
+};
+
+const userMenu = [
+ { name: 'Settings', glyph: 'action-settings', size: 's', callback: () => alert('Settings selected!') },
+ { name: 'Sign Out', glyph: 'log', size: 's', callback: () => alert('Sign Out selected!') }
+];
+
+const productSwitcher = {
+ label: 'Product Switcher',
+ glyph: 'grid',
+ callback: () => alert('Product Switcher selected!')
+};
+
+const productSwitcherList = [
+ { title: 'Fiori Home', image: images('./01.png'), callback: () => alert('Fiori Home selected!') },
+ { title: 'S/4 HANA Cloud', image: images('./02.png'), callback: () => alert('S/4 HANA Cloud selected!') },
+ { title: 'Analytics Cloud', image: images('./03.png'), callback: () => alert('Analytics Cloud selected!') },
+ { title: 'Ariba', image: images('./04.png'), callback: () => alert('Ariba selected!') },
+ { title: 'SuccessFactors', image: images('./05.png'), callback: () => alert('SuccessFactors selected!') },
+ { title: 'Commerce Cloud', image: images('./06.png'), callback: () => alert('Commerce Cloud selected!') },
+ { title: 'Gigya', image: images('./07.png'), callback: () => alert('Gigya selected!') },
+ { title: 'Callidus Cloud', image: images('./08.png'), callback: () => alert('Callidus Cloud selected!') },
+ { title: 'Fieldglass', image: images('./09.png'), callback: () => alert('Fieldglass selected!') },
+ { title: 'Concur', image: images('./10.png'), callback: () => alert('Concur selected!') },
+ { title: 'Cloud for Customer', image: images('./11.png'), callback: () => alert('Cloud for Customer selected!')},
+ { title: 'Cloud Portal', image: images('./12.png'), callback: () => alert('Cloud Portal selected!') }
+];
+`;
+
+ const actions = [
+ {
+ glyph: 'bell',
+ notificationCount: 21,
+ label: 'Notifications',
+ callback: () => alert('Notification selected!')
+ },
+ {
+ glyph: 'post',
+ notificationCount: 4,
+ label: 'Post',
+ callback: () => alert('Post selected!')
+ },
+ {
+ glyph: 'settings',
+ label: 'Settings',
+ notificationCount: 0,
+ callback: () => alert('Settings selected!'),
+ menu: (
+
+
+ Option 1
+ Option 2
+ Option 3
+
+
+ )
+ }
+ ];
+
+ const user1 = {
+ initials: 'JS',
+ userName: 'John Snow',
+ colorAccent: 8
+ };
+
+ const user = {
+ image: images('./headshot-male.jpg'),
+ userName: 'John Snow'
+ };
+
+ const userMenu = [
+ { name: 'Settings', glyph: 'action-settings', size: 's', callback: () => alert('Settings selected!') },
+ { name: 'Sign Out', glyph: 'log', size: 's', callback: () => alert('Sign Out selected!') }
+ ];
+
+ const productMenu = [
+ { name: 'Application A', callback: () => alert('Application A selected!') },
+ { name: 'Application B', callback: () => alert('Application B selected!') },
+ { name: 'Application C', callback: () => alert('Application C selected!') },
+ { name: 'Application D', callback: () => alert('Application D selected!') }
+ ];
+
+ const productSwitcherList = [
+ { title: 'Fiori Home', image: images('./01.png'), callback: () => alert('Fiori Home selected!') },
+ { title: 'S/4 HANA Cloud', image: images('./02.png'), callback: () => alert('S/4 HANA Cloud selected!') },
+ { title: 'Analytics Cloud', image: images('./03.png'), callback: () => alert('Analytics Cloud selected!') },
+ { title: 'Ariba', image: images('./04.png'), callback: () => alert('Ariba selected!') },
+ { title: 'SuccessFactors', image: images('./05.png'), callback: () => alert('SuccessFactors selected!') },
+ { title: 'Commerce Cloud', image: images('./06.png'), callback: () => alert('Commerce Cloud selected!') },
+ { title: 'Gigya', image: images('./07.png'), callback: () => alert('Gigya selected!') },
+ { title: 'Callidus Cloud', image: images('./08.png'), callback: () => alert('Callidus Cloud selected!') },
+ { title: 'Fieldglass', image: images('./09.png'), callback: () => alert('Fieldglass selected!') },
+ { title: 'Concur', image: images('./10.png'), callback: () => alert('Concur selected!') },
+ {
+ title: 'Cloud for Customer',
+ image: images('./11.png'),
+ callback: () => alert('Cloud for Customer selected!')
+ },
+ { title: 'Cloud Portal', image: images('./12.png'), callback: () => alert('Cloud Portal selected!') }
+ ];
+
+ const productSwitcher = {
+ label: 'Product Switcher',
+ glyph: 'grid',
+ callback: () => alert('Product Switcher selected!')
+ };
+
+ return (
+
+
+
+ The shellbar offers consistent, responsive navigation across all products and applications. Includes
+ support for branding, product navigation, search, notifications, user settings, and CoPilot. This is a
+ composite component comprised of mandatory and optional elements. Before getting started, here are some
+ things to know.
+
+
+
+
+
+
+
Basic Shellbar
+
+ This example shows the minimum shellbar for a single application product with only user settings. If no
+ user thumbnail is available then display initials.
+
+
+ }
+ productTitle="Corporate Portal"
+ user={user1}
+ userMenu={userMenu}
+ />
+
+
{simpleShellbarExampleCode}
+
+
+
+
Links with collapsible menu, CoPilot and Product Switcher
+
+ When a product has multiple links, the product links should collapse into an overflow menu on mobile
+ screens. All actions, except for the user menu, should be collapsed. See the placement of the{' '}
+ <fd-shellbar-collapse> container below.
+
+
+ }
+ productTitle="Corporate Portal"
+ productMenu={productMenu}
+ subtitle="Subtitle"
+ copilot
+ actions={actions}
+ user={user}
+ userMenu={userMenu}
+ productSwitcher={productSwitcher}
+ productSwitcherList={productSwitcherList}
+ />
+
+
{shellbarExampleCode}
+
+ );
+};
diff --git a/src/Shellbar/Shellbar.js b/src/Shellbar/Shellbar.js
new file mode 100644
index 000000000..6eab207df
--- /dev/null
+++ b/src/Shellbar/Shellbar.js
@@ -0,0 +1,292 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import { Popover, Menu, MenuList, MenuItem, Identifier, Icon } from '../';
+
+export class Shellbar extends Component {
+ static propTypes = {
+ copilot: PropTypes.bool
+ };
+
+ static defaultProps = {
+ actions: null
+ };
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ collapsedActions: this.getCollapsedActions()
+ };
+ this.onResize = this.onResize.bind(this);
+ }
+
+ getCollapsedActions = () => {
+ if (this.props.actions) {
+ let collapsedList = [...this.props.actions];
+ collapsedList.push(this.props.productSwitcher);
+ return collapsedList;
+ }
+ };
+
+ componentWillMount() {
+ this.setState({
+ collapsed: false
+ });
+ }
+
+ componentDidMount() {
+ window.addEventListener('resize', this.onResize);
+ this.onResize();
+ }
+
+ componentWillUnmount() {
+ window.removeEventListener('resize', this.onResize);
+ }
+
+ onResize() {
+ this.setState({ collapsed: window.innerWidth <= 1024 });
+ }
+
+ render() {
+ const {
+ logo,
+ productTitle,
+ productMenu,
+ subtitle,
+ copilot,
+ actions,
+ productSwitcher,
+ productSwitcherList,
+ user,
+ userMenu
+ } = this.props;
+ return (
+
+
+
{logo}
+
+ {productTitle && !productMenu &&
{productTitle} }
+ {productMenu && (
+
+
+
+ {productTitle}
+
+
+ }
+ body={
+ productMenu && (
+
+
+ {productMenu.map((item, index) => {
+ return (
+
+ {item.glyph && (
+
+
+
+
+ )}
+ {item.name}
+
+ );
+ })}
+
+
+ )
+ }
+ />
+
+ )}
+
+ {subtitle &&
{subtitle}
}
+
+ {copilot ? (
+
+
+
+ ) : null}
+
+
+ {actions &&
+ actions.map((action, index) => {
+ return (
+
+ {action.menu ? (
+
+ {action.notificationCount > 0 && (
+
+ {action.notificationCount}
+
+ )}
+
+ }
+ body={action.menu}
+ />
+ ) : (
+
+ {action.notificationCount > 0 && (
+
+ {action.notificationCount}
+
+ )}
+
+ )}
+
+ );
+ })}
+ {this.state.collapsed && actions && (
+
+
+
+
+
+ {actions.reduce((r, d) => r + d.notificationCount, 0)}
+
+
+
+ }
+ body={
+ userMenu && (
+
+
+ {this.state.collapsedActions.map((item, index) => {
+ return (
+
+ {item.label}
+
+ );
+ })}
+
+
+ )
+ }
+ />
+
+
+ )}
+ {user && (
+
+
+
+ ) : (
+
+ {user.initials}
+
+ )
+ }
+ body={
+ userMenu && (
+
+
+ {user.userName}
+ {userMenu.map((item, index) => {
+ return (
+
+ {item.glyph && (
+
+
+
+
+ )}
+ {item.name}
+
+ );
+ })}
+
+
+ )
+ }
+ />
+
+
+ )}
+ {productSwitcher && (
+
+
+
}
+ body={
+
+
+
+ {productSwitcherList.map((item, index) => {
+ return (
+
+
+
+
+
+ {item.title}
+
+
+ );
+ })}
+
+
+
+ }
+ />
+
+
+ )}
+
+
+
+ );
+ }
+}
diff --git a/src/index.js b/src/index.js
index af9fb171a..0e88367f7 100644
--- a/src/index.js
+++ b/src/index.js
@@ -51,57 +51,19 @@ import {
FormFieldset,
FormLegend
} from '../src/Forms/Forms';
-import {
- Icon
-} from '../src/Icon/Icon';
-import {
- Identifier
-} from '../src/Identifier/Identifier';
-import {
- Image
-} from '../src/Image/Image';
-import {
- InlineHelp
-} from '../src/InlineHelp/InlineHelp';
-import {
- InputGroup,
- FormGroup
-} from '../src/InputGroup/InputGroup';
-import {
- ListGroup,
- ListGroupItem,
- ListGroupItemActions,
- ListGroupItemCheckbox
-} from '../src/ListGroup/ListGroup';
-import {
- LocalizationEditor
-} from '../src/LocalizationEditor/LocalizationEditor';
-import {
- MegaMenu,
- MegaMenuList,
- MegaMenuGroup
-} from '../src/MegaMenu/MegaMenu';
-import {
- Menu,
- MenuList,
- MenuItem,
- MenuGroup
-} from '../src/Menu/Menu';
-import {
- Modal
-} from '../src/Modal/Modal';
-import {
- Navbar,
- NavbarGroup,
- NavbarActions,
- NavbarElement
-} from '../src/Navbar/Navbar';
-import {
- MultiInput
-} from '../src/MultiInput/MultiInput';
-import {
- Pagination
-} from '../src/Pagination/Pagination';
+import { Icon } from '../src/Icon/Icon';
+import { Identifier } from '../src/Identifier/Identifier';
+import { Image } from '../src/Image/Image';
+import { InlineHelp } from '../src/InlineHelp/InlineHelp';
+import { InputGroup, FormGroup } from '../src/InputGroup/InputGroup';
+import { ListGroup, ListGroupItem, ListGroupItemActions, ListGroupItemCheckbox } from '../src/ListGroup/ListGroup';
+import { LocalizationEditor } from '../src/LocalizationEditor/LocalizationEditor';
+import { MegaMenu, MegaMenuList, MegaMenuGroup } from '../src/MegaMenu/MegaMenu';
+import { Menu, MenuList, MenuItem, MenuGroup } from '../src/Menu/Menu';
+import { Modal } from '../src/Modal/Modal';
+import { Navbar, NavbarGroup, NavbarActions, NavbarElement } from '../src/Navbar/Navbar';
+import { MultiInput } from '../src/MultiInput/MultiInput';
+import { Pagination } from '../src/Pagination/Pagination';
import {
Panel,
PanelGrid,
@@ -113,27 +75,12 @@ import {
PanelContent,
PanelFooter
} from '../src/Panel/Panel';
-import {
- Popover
-} from '../src/Popover/Popover';
-import {
- SearchInput
-} from '../src/SearchInput/SearchInput';
-import {
- SideNav,
- SideNavList,
- SideNavGroup
-} from '../src/SideNavigation/SideNavigation';
-import {
- Table
-} from '../src/Table/Table';
-import {
- Tabs,
- TabComponent
-} from '../src/Tabs/Tabs';
-import {
- Token
-} from './Token/Token';
+import { Popover } from '../src/Popover/Popover';
+import { SearchInput } from '../src/SearchInput/SearchInput';
+import { SideNav, SideNavList, SideNavGroup } from '../src/SideNavigation/SideNavigation';
+import { Table } from '../src/Table/Table';
+import { Tabs, TabComponent } from '../src/Tabs/Tabs';
+import { Token } from './Token/Token';
import {
Tile,
TileContent,
@@ -144,40 +91,18 @@ import {
TileGrid,
ProductTileContent
} from '../src/Tile/Tile';
-import {
- Toggle
-} from '../src/Toggle/Toggle';
-import {
- Tree
-} from '../src/Tree/Tree';
-import {
- Time
-} from '../src/Time/Time';
-import {
- TimePicker
-} from '../src/TimePicker/TimePicker';
-import {
- DocsTile,
- DocsText
-} from '../src/documentation/DocsTile/DocsTile';
-import {
- Separator
-} from '../src/documentation/Separator/Separator';
-import {
- Header
-} from '../src/documentation/Header/Header';
-import {
- Description
-} from '../src/documentation/Description/Description';
-import {
- Import
-} from '../src/documentation/Import/Import';
-import {
- Properties
-} from '../src/documentation/Properties/Properties';
-import {
- Playground
-} from '../src/documentation/Playground/Playground';
+import { Toggle } from '../src/Toggle/Toggle';
+import { Tree } from '../src/Tree/Tree';
+import { Time } from '../src/Time/Time';
+import { TimePicker } from '../src/TimePicker/TimePicker';
+import { Shellbar } from '../src/Shellbar/Shellbar';
+import { DocsTile, DocsText } from '../src/documentation/DocsTile/DocsTile';
+import { Separator } from '../src/documentation/Separator/Separator';
+import { Header } from '../src/documentation/Header/Header';
+import { Description } from '../src/documentation/Description/Description';
+import { Import } from '../src/documentation/Import/Import';
+import { Properties } from '../src/documentation/Properties/Properties';
+import { Playground } from '../src/documentation/Playground/Playground';
export {
DocsTile,
@@ -269,8 +194,9 @@ export {
TileGrid,
ProductTileContent,
Toggle,
- Tree
+ Tree,
+ Shellbar
};
-ReactDOM.render( < App / > , document.getElementById('root'));
-registerServiceWorker();
\ No newline at end of file
+ReactDOM.render( , document.getElementById('root'));
+registerServiceWorker();