GlobalEventsHandlerProvider
GlobalEventsHandlerProvider is a React context provider that allows sharing global event handlers with consuming components.
also known as OnLinkNavigationProvider
Props
SheetMobile handlers
onOpen, onClose
GlobalEventsHandlerProvider has one prop for each component subscribing to the provider.
GlobalEventsHandlerProvider's sheetMobileHandlers
props only subscribes SheetMobile and those adaptive components that use SheetMobile in their mobile UI (Modal, Dropdown.)
In the example below, SheetMobile's logs when opens and closes.
import { useCallback, useMemo, useState } from 'react'; import { Box, Button, CompositeZIndex, DeviceTypeProvider, FixedZIndex, Flex, GlobalEventsHandlerProvider, Layer, SheetMobile, } from 'gestalt'; export default function Example() { const [showComponent, setShowComponent] = useState(true); const PAGE_HEADER_ZINDEX = new FixedZIndex(10); const ABOVE_PAGE_HEADER_ZINDEX = new CompositeZIndex([PAGE_HEADER_ZINDEX]); const sheetMobileOnOpen = useCallback( () => console.log('On open handler'), [] ); const sheetMobileOnClose = useCallback( () => console.log('On close handler'), [] ); const sheetMobileHandlers = useMemo( () => ({ onOpen: sheetMobileOnOpen, onClose: sheetMobileOnClose }), [sheetMobileOnOpen, sheetMobileOnClose] ); return ( <GlobalEventsHandlerProvider sheetMobileHandlers={sheetMobileHandlers}> <DeviceTypeProvider deviceType="mobile"> {showComponent ? ( <Layer zIndex={ABOVE_PAGE_HEADER_ZINDEX}> <SheetMobile heading="Heading" onDismiss={() => setShowComponent(false)} size="auto" > <SheetMobile.DismissingElement> {({ onDismissStart }) => ( <Flex alignItems="center" height="100%" justifyContent="center" > <Button color="gray" onClick={onDismissStart} text="Close" /> </Flex> )} </SheetMobile.DismissingElement> </SheetMobile> </Layer> ) : null} <Box padding={2}> <Button accessibilityLabel="Show SheetMobile" color="red" onClick={() => setShowComponent(true)} size="lg" text="Show SheetMobile" /> </Box> </DeviceTypeProvider> </GlobalEventsHandlerProvider> ); }
Link handlers
Components with links use simple <a>
tags. In order to replace the default link behavior with custom ones (e.g.
react-router), onNavigation
provides an interface to pass external logic into the 'onClick' event handler in children links.
This example illustrates a custom navigation implementations to externally control the link functionality of Link: setting a default navigation logic with GlobalEventsHandlerProvider.
If onNavigation
prop is passed to GlobalEventsHandlerProvider, it's passed down to all children links and sets a customized default link navigation behavior. onNavigation
is a higher-order function: it takes named arguments: href
and target
and returns an event handler function. In the component's onClick
event handler, the onClick
prop gets called first, followed by the function passed down by the GlobalEventsHandlerProvider.
If onNavigation
is a hook function, it can contain complex logic, including
React hooks, to perform side effects.
In this example, the useOnNavigation
hook function is passed to GlobalEventsHandlerProvider and executes the following actions:
- Disable the default link behavior
- Show an alert message
- Open a different URL in a new window
The returned onNavigationClick
function inside the hook function uses the event access to
preventDefault(). It could also be used to
stopPropagation().
All components that consume from the GlobalEventsHandlerProvider also allow to disable the default navigation logic set in the provider from the component itself.
The triggering events, like onClick
, provide access to the event and "dangerouslyDisableOnNavigation".
"dangerouslyDisableOnNavigation" is a callback function that, when called, disables the logic set in GlobalEventsHandlerProvider in that component instance.
"dangerouslyDisableOnNavigation" can be used to use the native anchor element directly or to use an alternative navigation
logic.
Don't forget to call event.preventDefault
when implementing an alternative navigation, e.g. router logic inside the onClick event. event.preventDefault
will prevent your underlying anchor and the alternative navigation to act at the same time, having two navigations occurring at the same time.
The example below demonstrates the correct use of "dangerouslyDisableOnNavigation".
import { useCallback, useMemo, useRef, useState } from 'react'; import { Button, Dropdown, Flex, GlobalEventsHandlerProvider, Text, } from 'gestalt'; export default function Example() { const [open, setOpen] = useState(false); const anchorRef = useRef(null); const useRouter = { push: (href) => { window.location.href = `${window.location.href}${href}`; }, }; const useOnNavigation = useCallback(({ href }) => { const onNavigationClick = ({ event }) => { event.preventDefault(); alert(`Disabled link: ${href}. Opening help.pinterest.com instead.`); }; return onNavigationClick; }, []); const linkHandlers = useMemo( () => ({ onNavigation: useOnNavigation }), [useOnNavigation] ); return ( <Flex alignItems="center" gap={4} height="100%" justifyContent="center" width="100%" > <GlobalEventsHandlerProvider linkHandlers={linkHandlers}> <Flex direction="column" gap={2}> <Text>Example url: {window.location.href}</Text> <Flex justifyContent="center"> <Button ref={anchorRef} accessibilityControls="basic-dropdown-example" accessibilityExpanded={open} accessibilityHaspopup iconEnd="arrow-down" onClick={() => setOpen((prevVal) => !prevVal)} selected={open} text="Menu" /> {open && ( <Dropdown anchor={anchorRef.current} id="basic-dropdown-example" onDismiss={() => { setOpen(false); }} > <Dropdown.Link href="#Disabling-the-provider" onClick={({ event, dangerouslyDisableOnNavigation }) => { event.preventDefault(); dangerouslyDisableOnNavigation(); useRouter.push('#'); setOpen(false); }} option={{ value: 'link item', label: 'This item is a link', }} /> </Dropdown> )} </Flex> </Flex> </GlobalEventsHandlerProvider> </Flex> ); }
Implementation in Link, ButtonLink, IconButtonLink, TapAreaLink
import { useCallback, useMemo, useState } from 'react'; import { Box, ButtonLink, Divider, Flex, GlobalEventsHandlerProvider, IconButtonLink, Image, Link, Mask, RadioGroup, TapAreaLink, Text, } from 'gestalt'; export default function Example() { const [onNavigationMode, setOnNavigationMode] = useState('default'); const useOnNavigation = useCallback(({ href }) => { const onNavigationClick = ({ event }) => { event.preventDefault(); alert(`Disabled link: ${href}. Opening help.pinterest.com instead.`); }; return onNavigationClick; }, []); const linkHandlers = useMemo( () => ({ onNavigation: onNavigationMode === 'custom' ? useOnNavigation : undefined, }), [onNavigationMode, useOnNavigation] ); return ( <Flex alignItems="center" gap={4} height="100%" justifyContent="center" width="100%" > <GlobalEventsHandlerProvider linkHandlers={linkHandlers}> <Flex direction="column" gap={2}> <Flex direction="column" gap={2}> <RadioGroup id="navigation-type" legend="Navigation type"> <RadioGroup.RadioButton checked={onNavigationMode === 'default'} id="default1" label="Default Link Navigation" name="default" onChange={() => setOnNavigationMode('default')} value="default" /> <RadioGroup.RadioButton checked={onNavigationMode === 'custom'} id="custom1" label="Custom GlobalEventsHandlerProvider Navigation" name="custom" onChange={() => setOnNavigationMode('custom')} value="custom" /> </RadioGroup> <Divider /> </Flex> <Flex alignItems="center" gap={4}> <Text> <Link href="#">Visit pinterest.com</Link> </Text> <ButtonLink href="#" text="Visit pinterest.com" /> <IconButtonLink accessibilityLabel="Link IconButton" href="#" icon="visit" iconColor="darkGray" size="lg" /> <Box width={100}> <TapAreaLink href="#" rounding={2}> <Box borderStyle="sm" color="tertiary" rounding={4}> <Mask rounding={2}> <Image alt="Antelope Canyon" naturalHeight={1} naturalWidth={1} src="https://i.ibb.co/DwYrGy6/stock14.jpg" /> </Mask> </Box> </TapAreaLink> </Box> </Flex> </Flex> </GlobalEventsHandlerProvider> </Flex> ); }
Implementation in BannerCallout, BannerUpsell, ActivationCard
import { useCallback, useMemo, useState } from 'react'; import { ActivationCard, BannerCallout, BannerUpsell, Divider, Flex, GlobalEventsHandlerProvider, Icon, RadioGroup, } from 'gestalt'; export default function Example() { const [onNavigationMode, setOnNavigationMode] = useState('default'); const useOnNavigation = useCallback(({ href }) => { const onNavigationClick = ({ event }) => { event.preventDefault(); alert(`Disabled link: ${href}. Opening help.pinterest.com instead.`); }; return onNavigationClick; }, []); const linkHandlers = useMemo( () => ({ onNavigation: onNavigationMode === 'custom' ? useOnNavigation : undefined, }), [onNavigationMode, useOnNavigation] ); return ( <Flex alignItems="center" gap={4} height="100%" justifyContent="center" width="100%" > <GlobalEventsHandlerProvider linkHandlers={linkHandlers}> <Flex direction="column" gap={2}> <Flex direction="column" gap={2}> <RadioGroup id="navigation-type" legend="Navigation type"> <RadioGroup.RadioButton checked={onNavigationMode === 'default'} id="default2" label="Default Link Navigation" name="default" onChange={() => setOnNavigationMode('default')} value="default" /> <RadioGroup.RadioButton checked={onNavigationMode === 'custom'} id="custom" label="Custom GlobalEventsHandlerProvider Navigation" name="custom2" onChange={() => setOnNavigationMode('custom')} value="custom" /> </RadioGroup> <Divider /> </Flex> <Flex alignItems="center" direction="column" gap={{ column: 4, row: 0 }} > <BannerCallout dismissButton={{ accessibilityLabel: 'Dismiss banner', onDismiss: () => {}, }} iconAccessibilityLabel="Info icon" message="Apply to the Verified Merchant Program!" primaryAction={{ href: '#', label: 'Get started', accessibilityLabel: 'Get started: verified merchant program', role: 'link', }} secondaryAction={{ href: '#', label: 'Learn more', accessibilityLabel: 'Learn more: verified merchant program', role: 'link', }} title="Your business account was created!" type="info" /> <BannerUpsell dismissButton={{ accessibilityLabel: 'Dismiss banner', onDismiss: () => {}, }} imageData={{ component: ( <Icon accessibilityLabel="Pin" color="default" icon="pinterest" size={32} /> ), }} message="Earn $60 of ads credit, and give $30 of ads credit to a friend" primaryAction={{ href: '#', label: 'Send invite', accessibilityLabel: 'Send invite for ads credit', role: 'link', }} title="Give $30, get $60 in ads credit" /> <ActivationCard dismissButton={{ accessibilityLabel: 'Dismiss card', onDismiss: () => {}, }} link={{ href: '#', label: 'Claim your website now', accessibilityLabel: '', }} message="Grow distribution and track Pins linked to your website" status="notStarted" statusMessage="Not started" title="Claim your website" /> </Flex> </Flex> </GlobalEventsHandlerProvider> </Flex> ); }
Implementation in Dropdown
import { useCallback, useMemo, useRef, useState } from 'react'; import { Button, Divider, Dropdown, Flex, GlobalEventsHandlerProvider, RadioGroup, } from 'gestalt'; export default function Example() { const [onNavigationMode, setOnNavigationMode] = useState('default'); const [open, setOpen] = useState(false); const anchorRef = useRef(null); const useOnNavigation = useCallback(({ href }) => { const onNavigationClick = ({ event }) => { event.preventDefault(); alert(`Disabled link: ${href}. Opening help.pinterest.com instead.`); }; return onNavigationClick; }, []); const linkHandlers = useMemo( () => ({ onNavigation: onNavigationMode === 'custom' ? useOnNavigation : undefined, }), [onNavigationMode, useOnNavigation] ); return ( <Flex alignItems="center" gap={4} height="100%" justifyContent="center" width="100%" > <GlobalEventsHandlerProvider linkHandlers={linkHandlers}> <Flex direction="column" gap={2}> <Flex direction="column" gap={2}> <RadioGroup id="navigation-type" legend="Navigation type"> <RadioGroup.RadioButton checked={onNavigationMode === 'default'} id="default3" label="Default Link Navigation" name="default" onChange={() => setOnNavigationMode('default')} value="default" /> <RadioGroup.RadioButton checked={onNavigationMode === 'custom'} id="custom3" label="Custom GlobalEventsHandlerProvider Navigation" name="custom" onChange={() => setOnNavigationMode('custom')} value="custom" /> </RadioGroup> <Divider /> </Flex> <Flex justifyContent="center"> <Button ref={anchorRef} accessibilityControls="basic-dropdown-example" accessibilityExpanded={open} accessibilityHaspopup iconEnd="arrow-down" onClick={() => setOpen((prevVal) => !prevVal)} selected={open} text="Menu" /> {open && ( <Dropdown anchor={anchorRef.current} id="basic-dropdown-example" onDismiss={() => { setOpen(false); }} > <Dropdown.Link href="#" option={{ value: 'link item', label: 'This item is a link', }} /> </Dropdown> )} </Flex> </Flex> </GlobalEventsHandlerProvider> </Flex> ); }
Other handlers
onRender
onRender
is only called when the component mounts for the first time.
It's implemented in the following components:
- DateField:
dateFieldHandlers
- DatePicker:
datePickerHandlers
- DateRange:
dateRangeHandlers