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 ( - - ); + const { id, addonBefore, children } = props; + return ( + + ); }; 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 ( +
    +
    Shellbar
    + + 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 ? ( +
    + CoPilot +
    + ) : null} +
    +
    + {actions && + actions.map((action, index) => { + return ( +
    + {action.menu ? ( + + {action.notificationCount > 0 && ( + + {action.notificationCount} + + )} + + } + body={action.menu} + /> + ) : ( + + )} +
    + ); + })} + {this.state.collapsed && actions && ( +
    +
    + + +
    + } + 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={ +
    + +
    + } + /> +
    +
    + )} +
    +
    + + ); + } +} 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();