UNPKG

@shopify/polaris

Version:

Shopify’s product component library

393 lines (358 loc) • 11.4 kB
--- name: Frame category: Structure keywords: - navigation - nav - links - primary navigation - main navigation - global - frame - sidebar - side bar - loading - top bar - menu - toast fullSizeExamples: true --- # Frame The frame component, while not visible in the user interface itself, provides the structure for any non-embedded application. It wraps the main elements and houses the primary [navigation](https://polaris.shopify.com/components/navigation/navigation), [top bar](https://polaris.shopify.com/components/structure/top-bar), [toast](https://polaris.shopify.com/components/feedback-indicators/toast), and [contextual save bar](https://polaris.shopify.com/components/forms/contextual-save-bar) components. --- ## Best practices For the best experience when creating an application frame, use the following components: - [Top bar](https://polaris.shopify.com/components/structure/top-bar) - [Navigation](https://polaris.shopify.com/components/navigation/navigation) - [Contextual save bar](https://polaris.shopify.com/components/forms/contextual-save-bar) - [Toast](https://polaris.shopify.com/components/feedback-indicators/toast) - [Loading](https://polaris.shopify.com/components/feedback-indicators/loading) --- ## Examples ### Frame in a stand-alone application Use to present the frame structure and all of its elements. ```jsx function FrameExample() { const defaultState = useRef({ emailFieldValue: 'dharma@jadedpixel.com', nameFieldValue: 'Jaded Pixel', }); const skipToContentRef = useRef(null); const [toastActive, setToastActive] = useState(false); const [isLoading, setIsLoading] = useState(false); const [isDirty, setIsDirty] = useState(false); const [searchActive, setSearchActive] = useState(false); const [searchValue, setSearchValue] = useState(''); const [userMenuActive, setUserMenuActive] = useState(false); const [mobileNavigationActive, setMobileNavigationActive] = useState(false); const [modalActive, setModalActive] = useState(false); const [nameFieldValue, setNameFieldValue] = useState( defaultState.current.nameFieldValue, ); const [emailFieldValue, setEmailFieldValue] = useState( defaultState.current.emailFieldValue, ); const [storeName, setStoreName] = useState( defaultState.current.nameFieldValue, ); const [supportSubject, setSupportSubject] = useState(''); const [supportMessage, setSupportMessage] = useState(''); const handleSubjectChange = useCallback( (value) => setSupportSubject(value), [], ); const handleMessageChange = useCallback( (value) => setSupportMessage(value), [], ); const handleDiscard = useCallback(() => { setEmailFieldValue(defaultState.current.emailFieldValue); setNameFieldValue(defaultState.current.nameFieldValue); setIsDirty(false); }, []); const handleSave = useCallback(() => { defaultState.current.nameFieldValue = nameFieldValue; defaultState.current.emailFieldValue = emailFieldValue; setIsDirty(false); setToastActive(true); setStoreName(defaultState.current.nameFieldValue); }, [emailFieldValue, nameFieldValue]); const handleNameFieldChange = useCallback((value) => { setNameFieldValue(value); value && setIsDirty(true); }, []); const handleEmailFieldChange = useCallback((value) => { setEmailFieldValue(value); value && setIsDirty(true); }, []); const handleSearchResultsDismiss = useCallback(() => { setSearchActive(false); setSearchValue(''); }, []); const handleSearchFieldChange = useCallback((value) => { setSearchValue(value); setSearchActive(value.length > 0); }, []); const toggleToastActive = useCallback( () => setToastActive((toastActive) => !toastActive), [], ); const toggleUserMenuActive = useCallback( () => setUserMenuActive((userMenuActive) => !userMenuActive), [], ); const toggleMobileNavigationActive = useCallback( () => setMobileNavigationActive( (mobileNavigationActive) => !mobileNavigationActive, ), [], ); const toggleIsLoading = useCallback( () => setIsLoading((isLoading) => !isLoading), [], ); const toggleModalActive = useCallback( () => setModalActive((modalActive) => !modalActive), [], ); const toastMarkup = toastActive ? ( <Toast onDismiss={toggleToastActive} content="Changes saved" /> ) : null; const userMenuActions = [ { items: [{content: 'Community forums'}], }, ]; const contextualSaveBarMarkup = isDirty ? ( <ContextualSaveBar message="Unsaved changes" saveAction={{ onAction: handleSave, }} discardAction={{ onAction: handleDiscard, }} /> ) : null; const userMenuMarkup = ( <TopBar.UserMenu actions={userMenuActions} name="Dharma" detail={storeName} initials="D" open={userMenuActive} onToggle={toggleUserMenuActive} /> ); const searchResultsMarkup = ( <Card> <ActionList items={[ {content: 'Shopify help center'}, {content: 'Community forums'}, ]} /> </Card> ); const searchFieldMarkup = ( <TopBar.SearchField onChange={handleSearchFieldChange} value={searchValue} placeholder="Search" /> ); const topBarMarkup = ( <TopBar showNavigationToggle userMenu={userMenuMarkup} searchResultsVisible={searchActive} searchField={searchFieldMarkup} searchResults={searchResultsMarkup} onSearchResultsDismiss={handleSearchResultsDismiss} onNavigationToggle={toggleMobileNavigationActive} /> ); const navigationMarkup = ( <Navigation location="/"> <Navigation.Section items={[ { label: 'Back to Shopify', icon: ArrowLeftMinor, }, ]} /> <Navigation.Section separator title="Jaded Pixel App" items={[ { label: 'Dashboard', icon: HomeMajorMonotone, onClick: toggleIsLoading, }, { label: 'Jaded Pixel Orders', icon: OrdersMajorTwotone, onClick: toggleIsLoading, }, ]} action={{ icon: ConversationMinor, accessibilityLabel: 'Contact support', onClick: toggleModalActive, }} /> </Navigation> ); const loadingMarkup = isLoading ? <Loading /> : null; const skipToContentTarget = ( <a id="SkipToContentTarget" ref={skipToContentRef} tabIndex={-1} /> ); const actualPageMarkup = ( <Page title="Account"> <Layout> {skipToContentTarget} <Layout.AnnotatedSection title="Account details" description="Jaded Pixel will use this as your account information." > <Card sectioned> <FormLayout> <TextField label="Full name" value={nameFieldValue} onChange={handleNameFieldChange} /> <TextField type="email" label="Email" value={emailFieldValue} onChange={handleEmailFieldChange} /> </FormLayout> </Card> </Layout.AnnotatedSection> </Layout> </Page> ); const loadingPageMarkup = ( <SkeletonPage> <Layout> <Layout.Section> <Card sectioned> <TextContainer> <SkeletonDisplayText size="small" /> <SkeletonBodyText lines={9} /> </TextContainer> </Card> </Layout.Section> </Layout> </SkeletonPage> ); const pageMarkup = isLoading ? loadingPageMarkup : actualPageMarkup; const modalMarkup = ( <Modal open={modalActive} onClose={toggleModalActive} title="Contact support" primaryAction={{ content: 'Send', onAction: toggleModalActive, }} > <Modal.Section> <FormLayout> <TextField label="Subject" value={supportSubject} onChange={handleSubjectChange} /> <TextField label="Message" value={supportMessage} onChange={handleMessageChange} multiline /> </FormLayout> </Modal.Section> </Modal> ); const theme = { colors: { topBar: { background: '#357997', }, }, logo: { width: 124, topBarSource: 'https://cdn.shopify.com/s/files/1/0446/6937/files/jaded-pixel-logo-color.svg?6215648040070010999', contextualSaveBarSource: 'https://cdn.shopify.com/s/files/1/0446/6937/files/jaded-pixel-logo-gray.svg?6215648040070010999', url: 'http://jadedpixel.com', accessibilityLabel: 'Jaded Pixel', }, }; return ( <div style={{height: '500px'}}> <AppProvider theme={theme} i18n={{ Polaris: { Avatar: { label: 'Avatar', labelWithInitials: 'Avatar with initials {initials}', }, ContextualSaveBar: { save: 'Save', discard: 'Discard', }, TextField: { characterCount: '{count} characters', }, TopBar: { toggleMenuLabel: 'Toggle menu', SearchField: { clearButtonLabel: 'Clear', search: 'Search', }, }, Modal: { iFrameTitle: 'body markup', }, Frame: { skipToContent: 'Skip to content', Navigation: { closeMobileNavigationLabel: 'Close navigation', }, }, }, }} > <Frame topBar={topBarMarkup} navigation={navigationMarkup} showMobileNavigation={mobileNavigationActive} onNavigationDismiss={toggleMobileNavigationActive} skipToContentTarget={skipToContentRef.current} > {contextualSaveBarMarkup} {loadingMarkup} {pageMarkup} {toastMarkup} {modalMarkup} </Frame> </AppProvider> </div> ); } ``` --- ## Related components - To display the navigation component on small screens, to provide search and a user menu, or to style the [frame](https://polaris.shopify.com/components/structure/frame) component to reflect an application’s brand, use the [top bar](https://polaris.shopify.com/components/structure/top-bar) component. - To display the primary navigation within the frame of a non-embedded 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.