UNPKG

@shopify/polaris

Version:

Shopify’s admin product component library

421 lines (345 loc) • 18.9 kB
--- name: Top bar category: Navigation keywords: - global chrome - global features - topbar - top bar - header - nav bar - bar - navbar - brand - search - user - menu - logo omitAppProvider: true --- # Top bar The top bar is a header component that allows merchants to search, access menus, and navigate by clicking on the logo. It’s always visible at the top of interfaces like Shopify or Shopify Plus. Third-party apps that use the top bar can customize the color to match their brand using the [app provider](https://polaris.shopify.com/components/structure/app-provider) component and are required to use their own logo. --- ## Required components The top bar component must be passed to the [frame](https://polaris.shopify.com/components/structure/frame) component. --- ## Best practices The top bar component should: - Not provide global navigation for an application - Use the [navigation component](https://polaris.shopify.com/components/structure/navigation) instead - Include search to help merchants find resources and navigate an application - Include a user menu component to indicate the logged-in merchant and provide them with global actions - Provide a color through the [app provider](https://polaris.shopify.com/components/structure/app-provider) component to style the background - The global menu text should contrast with the rest of the top bar and pass the minimum contrast ratio of the WCAG 2.0 guidelines - Use an SVG file for the logo - Use a logo that passes the minimum contrast ratio of the WCAG 2.0 guidelines when compared to the top bar background color - Show the navigation toggle so it appears on small screen --- ## Content guidelines ### Placeholder content The placeholder content for the search field should: - Always say "Search" - Never include an ellipsis <!-- usagelist --> #### Do - Search #### Don’t - search... <!-- end --> --- <a name="subcomponent-menu"></a> ## Top bar menu A component that composes together an activator and a popover containing an action list to create a dropdown menu. ### Menu properties | Prop | Type | Description | | ---------------- | ----------------------------- | -------------------------------------------------------------------------------------------------- | | activatorContent | React.ReactNode | Accepts an activator component that renders inside of a button that opens the menu | | actions | ActionListProps['sections'] | An array of action objects that are rendered inside of a popover triggered by this menu | | message | [MessageProps](#type-message) | Accepts a message that facilitates direct, urgent communication with the merchant through the menu | | open | boolean | A boolean property indicating whether the menu is currently open | | onOpen() | function | A callback function to handle opening the menu popover | | onClose() | function | A callback function to handle closing the menu popover | <a name="subcomponent-user-menu"></a> ## Top bar user menu A specialized menu component that is activated by a user avatar. ### Menu properties | Prop | Type | Description | | ---------- | ----------------------------- | ------------------------------------------------------------------------------------------------------- | | actions | {items: IconableAction[]}[] | An array of action objects that are rendered inside of a popover triggered by this menu | | message | [MessageProps](#type-message) | Accepts a message that facilitates direct, urgent communication with the merchant through the user menu | | name | string | A string detailing the merchant’s full name to be displayed in the user menu | | detail | string | A string allowing further details on the merchant’s name displayed in the user menu | | initials | AvatarProps['initials'] | The merchant’s initials, rendered in place of an avatar image when not provided | | avatar | AvatarProps['source'] | An avatar image representing the merchant | | open | boolean | A boolean property indicating whether the user menu is currently open | | onToggle() | function | A callback function to handle opening and closing the user menu | <a name="type-message"></a> ### Top bar menu message #### Message properties | Prop | Type | Description | | ----------- | ----------------------------------------------- | ----------------------------------------- | | title | string | A title for the message | | description | string | A description for the message | | action | {onClick(): void; content: string} | An action to render near the message | | link | {to: string; content: string} | A link to view the content of the message | | badge | {content: string; status: BadgeProps['status']} | A badge to render near the message | --- <a name="subcomponent-search-field"></a> ## Top bar search field A text field component that is tailor-made for a search use-case. ### Search field properties | Prop | Type | Description | | ----------------------- | -------- | -------------------------------------------------------------------------------- | | value | string | Initial value for the input | | placeholder | string | Hint text to display | | focused | boolean | Force the focus state on the input | | active | boolean | Force a state where search is active but the text field component is not focused | | onChange(value: string) | function | Callback when value is changed | | onFocus() | function | Callback when input is focused | | onBlur() | function | Callback when focus is removed | --- ## Examples ### Top bar with all of its elements Use to provide structure for the top of an application. Style the top bar component using the app provider component with a theme. Providing just the `background` key for the top bar component theme will result in intelligent defaults being set for complementary colors with contrasting text. ```jsx function TopBarExample() { const [isUserMenuOpen, setIsUserMenuOpen] = useState(false); const [isSecondaryMenuOpen, setIsSecondaryMenuOpen] = useState(false); const [isSearchActive, setIsSearchActive] = useState(false); const [searchValue, setSearchValue] = useState(''); const toggleIsUserMenuOpen = useCallback( () => setIsUserMenuOpen((isUserMenuOpen) => !isUserMenuOpen), [], ); const toggleIsSecondaryMenuOpen = useCallback( () => setIsSecondaryMenuOpen((isSecondaryMenuOpen) => !isSecondaryMenuOpen), [], ); const handleSearchResultsDismiss = useCallback(() => { setIsSearchActive(false); setSearchValue(''); }, []); const handleSearchChange = useCallback((value) => { setSearchValue(value); setIsSearchActive(value.length > 0); }, []); const handleNavigationToggle = useCallback(() => { console.log('toggle navigation visibility'); }, []); const theme = { logo: { width: 124, topBarSource: 'https://cdn.shopify.com/s/files/1/0446/6937/files/jaded-pixel-logo-color.svg?6215648040070010999', url: 'http://jadedpixel.com', accessibilityLabel: 'Jaded Pixel', }, }; const userMenuMarkup = ( <TopBar.UserMenu actions={[ { items: [{content: 'Back to Shopify', icon: ArrowLeftMinor}], }, { items: [{content: 'Community forums'}], }, ]} name="Dharma" detail="Jaded Pixel" initials="D" open={isUserMenuOpen} onToggle={toggleIsUserMenuOpen} /> ); const searchResultsMarkup = ( <ActionList items={[{content: 'Shopify help center'}, {content: 'Community forums'}]} /> ); const searchFieldMarkup = ( <TopBar.SearchField onChange={handleSearchChange} value={searchValue} placeholder="Search" showFocusBorder /> ); const secondaryMenuMarkup = ( <TopBar.Menu activatorContent={ <span> <Icon source={QuestionMarkMajor} /> <VisuallyHidden>Secondary menu</VisuallyHidden> </span> } open={isSecondaryMenuOpen} onOpen={toggleIsSecondaryMenuOpen} onClose={toggleIsSecondaryMenuOpen} actions={[ { items: [{content: 'Community forums'}], }, ]} /> ); const topBarMarkup = ( <TopBar showNavigationToggle userMenu={userMenuMarkup} secondaryMenu={secondaryMenuMarkup} searchResultsVisible={isSearchActive} searchField={searchFieldMarkup} searchResults={searchResultsMarkup} onSearchResultsDismiss={handleSearchResultsDismiss} onNavigationToggle={handleNavigationToggle} /> ); return ( <div style={{height: '250px'}}> <AppProvider theme={theme} i18n={{ Polaris: { Avatar: { label: 'Avatar', labelWithInitials: 'Avatar with initials {initials}', }, Frame: {skipToContent: 'Skip to content'}, TopBar: { toggleMenuLabel: 'Toggle menu', SearchField: { clearButtonLabel: 'Clear', search: 'Search', }, }, }, }} > <Frame topBar={topBarMarkup} /> </AppProvider> </div> ); } ``` ### Top bar themed with colorScheme Provide specific keys and corresponding colors to the top bar theme for finer control. When giving more than just the `background`, providing all keys is necessary to prevent falling back to default colors. ```jsx function TopBarExample() { const [isUserMenuOpen, setIsUserMenuOpen] = useState(false); const [isSearchActive, setIsSearchActive] = useState(false); const [searchValue, setSearchValue] = useState(''); const [isDarkTheme, setIsDarkTheme] = useState(false); const toggleIsUserMenuOpen = useCallback( () => setIsUserMenuOpen((isUserMenuOpen) => !isUserMenuOpen), [], ); const handleThemeChange = useCallback( () => setIsDarkTheme((isDarkTheme) => !isDarkTheme), [], ); const handleSearchResultsDismiss = useCallback(() => { setIsSearchActive(false); setSearchValue(''); }, []); const handleSearchChange = useCallback((value) => { setSearchValue(value); setIsSearchActive(value.length > 0); }, []); const handleNavigationToggle = useCallback(() => { console.log('toggle navigation visibility'); }, []); const theme = { colors: { surface: '#FFFFFF', onsurface: '#212B36', }, colorScheme: isDarkTheme ? 'dark' : 'light', logo: { width: 124, topBarSource: `data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 446 92.6' fill='%23${ isDarkTheme ? 'fff' : '000' }'%3E%3Cpath d='M112.2 51.6c-2.8-1.5-4.2-2.8-4.2-4.5 0-2.2 2-3.7 5.1-3.7 3.6 0 6.9 1.5 6.9 1.5l2.5-7.8s-2.3-1.8-9.2-1.8c-9.6 0-16.3 5.5-16.3 13.2 0 4.4 3.1 7.7 7.3 10.1 3.3 1.9 4.5 3.3 4.5 5.3 0 2.1-1.7 3.7-4.8 3.7-4.6 0-9-2.4-9-2.4L92.3 73s4 2.7 10.8 2.7c9.9 0 17-4.9 17-13.6 0-4.7-3.6-8-7.9-10.5zM151.5 35.2c-4.9 0-8.7 2.3-11.6 5.8l-.2-.1 4.2-22.1h-11L122.3 75h11l3.7-19.2c1.4-7.3 5.2-11.7 8.7-11.7 2.5 0 3.4 1.7 3.4 4.1 0 1.5-.2 3.3-.5 4.9l-4.1 22h11l4.3-22.7c.5-2.4.8-5.3.8-7.2-.1-6.3-3.3-10-9.1-10zM185.4 35.2c-13.2 0-22 12-22 25.3 0 8.5 5.3 15.4 15.1 15.4 13 0 21.8-11.6 21.8-25.3 0-7.9-4.6-15.4-14.9-15.4zM180 67.4c-3.7 0-5.3-3.2-5.3-7.2 0-6.3 3.3-16.6 9.2-16.6 3.9 0 5.2 3.3 5.2 6.6 0 6.8-3.3 17.2-9.1 17.2zM228.4 35.2c-7.4 0-11.6 6.5-11.6 6.5h-.2l.6-5.9h-9.7c-.5 4-1.4 10-2.2 14.6l-7.6 40.2h11l3-16.3h.2s2.3 1.4 6.5 1.4c12.9 0 21.4-13.2 21.4-26.6 0-7.3-3.3-13.9-11.4-13.9zm-10.5 32.3c-2.9 0-4.5-1.6-4.5-1.6l1.8-10.3c1.3-6.9 4.9-11.4 8.7-11.4 3.3 0 4.4 3.1 4.4 6.1 0 7.1-4.2 17.2-10.4 17.2zM255.5 19.4c-3.5 0-6.3 2.8-6.3 6.4 0 3.3 2.1 5.5 5.2 5.5h.2c3.4 0 6.4-2.3 6.5-6.4-.2-3.2-2.3-5.5-5.6-5.5zM240.1 75h11l7.5-38.9h-11.1zM286.5 36h-7.6l.4-1.8c.6-3.7 2.9-7.1 6.5-7.1 2 0 3.5.6 3.5.6l2.2-8.6s-1.9-1-6-1c-3.9 0-7.8 1.1-10.8 3.7-3.7 3.2-5.5 7.8-6.4 12.4L268 36h-5.1l-1.6 8.3h5.1L260.6 75h11l5.8-30.8h7.6l1.5-8.2zM313 36.1s-6.9 17.3-10 26.8h-.2c-.2-3-2.7-26.8-2.7-26.8h-11.6l6.6 35.8c.2.8.1 1.3-.2 1.8-1.3 2.5-3.4 4.9-6 6.6-2.1 1.5-4.4 2.5-6.2 3.1l3 9.3c2.2-.5 6.9-2.3 10.8-6 5-4.7 9.6-12 14.4-21.8l13.5-28.8H313zM54.3 10.8s-1 .3-2.7.8c-.3-.9-.7-2-1.3-3.2-1.9-3.6-4.7-5.6-8.1-5.6h-.7c-.1-.1-.2-.2-.3-.4C39.7.8 37.8.1 35.6.1 31.2.2 26.9 3.4 23.4 9c-2.5 3.9-4.4 8.9-4.9 12.7-5 1.6-8.5 2.6-8.6 2.7-2.5.8-2.6.9-2.9 3.3-.3 1.8-6.9 53-6.9 53l55 9.5V10.7c-.4 0-.7.1-.8.1zm-12.7 3.9c-2.9.9-6.1 1.9-9.3 2.9.9-3.4 2.6-6.8 4.7-9.1.8-.8 1.9-1.8 3.1-2.3 1.3 2.5 1.5 6.1 1.5 8.5zM35.7 3.2c1 0 1.9.2 2.6.7-1.2.6-2.3 1.5-3.4 2.6-2.8 3-4.9 7.6-5.7 12.1-2.6.8-5.2 1.6-7.6 2.4C23 13.9 28.9 3.4 35.7 3.2zm-8.5 40c.3 4.7 12.6 5.7 13.3 16.6.5 8.6-4.6 14.5-11.9 15-8.8.6-13.7-4.7-13.7-4.7l1.9-8s4.9 3.7 8.8 3.4c2.6-.2 3.5-2.2 3.4-3.7-.4-6.1-10.4-5.7-11-15.8-.5-8.4 5-17 17.2-17.8 4.7-.3 7.1.9 7.1.9l-2.8 10.5s-3.1-1.4-6.8-1.2c-5.5.5-5.6 3.9-5.5 4.8zm17.3-29.4c0-2.2-.3-5.3-1.3-7.9 3.3.6 4.9 4.4 5.6 6.6-1.3.4-2.7.8-4.3 1.3zm12.3 76.3l22.8-5.7S69.8 18 69.7 17.6c-.1-.5-.5-.7-.8-.7-.3 0-6.7-.1-6.7-.1s-3.9-3.8-5.4-5.2v78.5zM347.7 35.4c-5.7 0-10.3 2-13.6 5.8l.6-3.3c.2-1.1-.5-1.9-1.6-1.9s-2.1.9-2.3 1.9L321 88.7c-.2 1.1.5 1.9 1.6 1.9s2.1-.9 2.3-1.9l3.6-18.9c1.3 2.4 4.4 5.7 11.4 5.7 5.5 0 10-1.7 13.6-5 3.7-3.4 6.2-8.5 7.5-15.1 1.3-6.6.7-11.7-1.7-15.1-2.2-3.2-6.1-4.9-11.6-4.9zm-7 36.6c-8.3 0-11.6-5.7-9.5-16.5 2.1-10.9 7.4-16.5 15.6-16.5 9 0 12.4 5.4 10.2 16.5-2.1 11.2-7.3 16.5-16.3 16.5zm91.6-18.7c-4.9-1.8-9.6-3.5-8.7-8.1.4-2.3 2.3-6.2 9.9-6.2 4.2 0 6.9 1.2 9.2 4 .4.5.9.5 1.1.5 1 0 1.9-.8 2.1-1.8.1-.5 0-.8-.2-1.2-2.4-3.4-6.2-5.1-11.6-5.1-7.7 0-13.3 3.7-14.4 9.7-1.4 7.2 5 9.6 10.6 11.7 4.9 1.8 9.4 3.6 8.6 8-1.2 6.2-7.7 7.1-11.5 7.1-5.2 0-8.4-1.4-10.8-4.7-.3-.4-.7-.5-1.1-.5-.9 0-1.9.7-2.1 1.8-.1.5 0 .8.2 1.2 1.6 2.9 6.2 5.8 13.1 5.8 8.8 0 14.8-4 16.1-10.7 1.4-7.1-4.9-9.5-10.5-11.5zm-11.8-28h-4.1l.8-4.1c.2-1.1-.5-1.9-1.6-1.9s-2.1.9-2.3 1.9l-.8 4.1h-4.1c-1.1 0-2.1.9-2.3 1.9-.2 1.1.5 1.9 1.6 1.9h4.1l-.8 4.1c-.2 1.1.5 1.9 1.6 1.9s2.1-.9 2.3-1.9l.8-4.1h4.1c1.1 0 2.1-.9 2.3-1.9.2-1-.5-1.9-1.6-1.9zm-9 14.3c-1.1 0-2.1.9-2.3 1.9l-3.7 19.3c-2 9.8-8.1 11.2-13.7 11.2-10.2 0-10.4-5.8-9.4-11.5l4.4-22.7c.2-1.1-.5-1.9-1.6-1.9s-2.1.9-2.3 1.9l-4.4 22.7c-.9 4.6-.6 8 1 10.4 1.9 3.1 5.8 4.6 11.6 4.6s10.3-1.6 13.4-4.6c2.5-2.4 4.1-5.9 5-10.4l3.7-19c.1-1-.6-1.9-1.7-1.9zm-35.1-20.2c-1.1 0-2.1.9-2.3 1.9l-10 51.8c-.2 1.1.5 1.9 1.6 1.9s2.1-.9 2.3-1.9l10-51.8c.2-1-.5-1.9-1.6-1.9z'/%3E%3C/svg%3E`, url: 'https://plus.shopify.com', accessibilityLabel: 'Shopify Plus', }, }; const userMenuMarkup = ( <TopBar.UserMenu actions={[ { items: [{content: 'Back to Shopify Plus', icon: ArrowLeftMinor}], }, { items: [{content: 'Community forums'}], }, ]} name="Scott" detail="Snow Devil" initials="S" open={isUserMenuOpen} onToggle={toggleIsUserMenuOpen} /> ); const searchResultsMarkup = ( <ActionList items={[{content: 'Shopify help center'}, {content: 'Community forums'}]} /> ); const searchFieldMarkup = ( <TopBar.SearchField onChange={handleSearchChange} value={searchValue} placeholder="Search" showFocusBorder /> ); const topBarMarkup = ( <TopBar showNavigationToggle userMenu={userMenuMarkup} searchResultsVisible={isSearchActive} searchField={searchFieldMarkup} searchResults={searchResultsMarkup} onSearchResultsDismiss={handleSearchResultsDismiss} onNavigationToggle={handleNavigationToggle} /> ); return ( <div style={{height: '250px'}}> <AppProvider theme={theme} i18n={{ Polaris: { Avatar: { label: 'Avatar', labelWithInitials: 'Avatar with initials {initials}', }, Frame: {skipToContent: 'Skip to content'}, TopBar: { toggleMenuLabel: 'Toggle menu', SearchField: { clearButtonLabel: 'Clear', search: 'Search', }, }, }, }} > <Frame topBar={topBarMarkup}> <Button onClick={handleThemeChange}> Toggle between dark and light themes </Button> </Frame> </AppProvider> </div> ); } ``` --- ## Related components - To provide the structure for the top bar component, as well as the primary navigation use the [frame](https://polaris.shopify.com/components/structure/frame) component. - To display the primary navigation within the frame of an application, use the [navigation](https://polaris.shopify.com/components/structure/navigation) component. - To tell merchants their options once they have made changes to a form on the page use the [contextual save bar](https://polaris.shopify.com/components/forms/contextual-save-bar) component. - To provide quick, at-a-glance feedback on the outcome of an action, use the [toast](https://polaris.shopify.com/components/feedback-indicators/toast) component. - To indicate to merchants that a page is loading or an upload is processing use the [loading](https://polaris.shopify.com/components/feedback-indicators/loading) component.