GlobalEventsHandlerProvider

GlobalEventsHandlerProvider is a React context provider that allows sharing global event handlers with consuming components.

also known as OnLinkNavigationProvider

Props

Component props
Name
Type
Default
children
Required
React.Node
-

Context lets a parent component provide data to the entire tree below it. Only components within the GlobalEventsHandlerProvider tree will be able to subscribe to it.

dateFieldHandlers
{ onRender?: () => void }
-

Handlers consumed by DateField.

datePickerHandlers
{ onRender?: () => void }
-

Handlers consumed by DatePicker.

dateRangeHandlers
{ onRender?: () => void }
-

Handlers consumed by DateRange.

linkHandlers
{
  onNavigation?: ({
    href: string,
    target?: null | "self" | "blank",
  }) => ?({
    +event: SyntheticEvent<>,
  }) => void,
}
-

Handlers consumed by Link.

radioGroupHandlers
{ onRender?: () => void }
-

Handlers consumed by RadioGroup.

sheetMobileHandlers
{ onOpen?: () => void, onClose?: () => void }
-

Handlers consumed by SheetMobile.

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>
  );
}

onNavigation: custom navigation

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().

Accessible links in Gestalt announce to assistive technologies that the link opens in a new tab. Always make sure your external logic aligns with the 'target' prop values. For example, if your external logic opens a url in a new tab, set 'target' to 'blank'. Localize the default label with DefaultLabelProvider. Learn more

onNavigation: disabling default navigation

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,
  Link,
  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>
  );
}

Examples from start to end: Link, Button, IconButton, TapArea
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

Examples from top to bottom: BannerCallout, BannerUpsell, ActivationCard
import { useCallback, useMemo, useState } from 'react';
import {
  ActivationCard,
  BannerCallout,
  BannerUpsell,
  Divider,
  Flex,
  GlobalEventsHandlerProvider,
  Icon,
  Link,
  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

Examples: Dropdown.Link
import { useCallback, useMemo, useRef, useState } from 'react';
import {
  Button,
  Divider,
  Dropdown,
  Flex,
  GlobalEventsHandlerProvider,
  Link,
  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

Experimental feature: The "onRender" prop is experimental and might be removed in the future.

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

Internal documentation