Popover

Display additional information in a popover on click or hover

<dui-button variant="ButtonVariant.Outline" popovertarget="--popover-intro">
    <dui-icon name="sliders-horizontal" class="text-muted-foreground"/>
    Configure View
</dui-button>
<dui-popover id="--popover-intro">
    <div class="flex flex-col gap-y-3">
        <dui-field-set>
            <dui-field-legend variant="FieldLegendVariant.Label">Sort By</dui-field-legend>
            <dui-field-group data-slot="radio-group">
                <dui-field orientation="FieldOrientation.Horizontal">
                    <dui-input type="radio" name="sort-by" value="departure" id="sort-departure" checked/>
                    <dui-field-label for="sort-departure" class="font-normal">
                        Departure Date
                    </dui-field-label>
                </dui-field>
                <dui-field orientation="FieldOrientation.Horizontal">
                    <dui-input type="radio" name="sort-by" value="posted" id="sort-posted"/>
                    <dui-field-label for="sort-posted" class="font-normal">
                        Date Posted
                    </dui-field-label>
                </dui-field>
            </dui-field-group>
        </dui-field-set>
        <dui-separator orientation="SeparatorOrientation.Horizontal"/>
        <dui-field-set>
            <dui-field-legend variant="FieldLegendVariant.Label">View As</dui-field-legend>
            <dui-field-group data-slot="radio-group">
                <dui-field orientation="FieldOrientation.Horizontal">
                    <dui-input type="radio" name="view-as" value="departure" id="view-list" checked/>
                    <dui-field-label for="view-list" class="font-normal">
                        List
                    </dui-field-label>
                </dui-field>
                <dui-field orientation="FieldOrientation.Horizontal">
                    <dui-input type="radio" name="view-as" value="posted" id="view-gallery"/>
                    <dui-field-label for="view-gallery" class="font-normal">
                        Gallery
                    </dui-field-label>
                </dui-field>
            </dui-field-group>
        </dui-field-set>
    </div>
</dui-popover>

Usage

<dui-popover> uses the HTML Popover API and renders a div element with a popover attribute. The popover can be opened by using the popovertarget attribute on a <dui-button> element.

<dui-button popovertarget="--my-popover">Element that triggers popover</dui-button>
<dui-popover id="--my-popover">
  <!-- add the popover content here -->
</dui-popover>

Examples

With Button Group

Create a split button that opens a popover using the <dui-button-group> Tag Helper.

<dui-button-group>
    <dui-button variant="ButtonVariant.Outline">
        <dui-icon name="bot"/>
        Copilot
    </dui-button>
    <dui-button variant="ButtonVariant.Outline" size="ButtonSize.Icon" popovertarget="--popover-offset">
        <dui-icon name="chevron-down"/>
    </dui-button>
</dui-button-group>
<dui-popover id="--popover-offset" position="PositionArea.BottomSpanLeft">
    <dui-stack gap="StackGap.Small">
        <dui-popover-header>
            <dui-popover-title>Start a new task with Copilot</dui-popover-title>
            <dui-popover-description>
                Describe your task in natural language.
            </dui-popover-description>
        </dui-popover-header>
        <dui-field>
            <dui-field-label for="--popopver-button-group-task" class="sr-only">
                Task Description
            </dui-field-label>
            <dui-textarea
                id="--popopver-button-group-task"
                placeholder="I need to..."
                class="resize-none"/>
            <dui-field-description>
                Copilot will open a pull request for review.
            </dui-field-description>
        </dui-field>
    </dui-stack>
</dui-popover>

Hover

You can trigger the popover on hover using an Interest Invokers.

For more information on interest invokers, please see our Tooltip documentation.

<p>While most tourists stick to the bustling markets of Marrakech, our resident history expert,
    <a href="#" interestfor="--popover-hover" class="underline decoration-dotted decoration-2 underline-offset-2">Ibn Battuta</a>,
    suggests heading north to the ancient streets of Tangier to truly capture the spirit of the Maghreb
</p>
<dui-popover id="--popover-hover">
    <div class="flex flex-col gap-3">
        <dui-avatar src="https://api.dicebear.com/9.x/adventurer-neutral/svg?seed=Brooklynn"/>
        <div>
            <p class="font-medium">Ibn Battuta</p>
            <div class="flex items-center gap-2">
                <div>@@ibnbattuta</div>
                <dui-badge>Follows you</dui-badge>
            </div>
        </div>
        <div class="flex items-center gap-4">
            <p><span class="font-medium">655</span> following</p>
            <p><span class="font-medium">100.3k</span> followers</p>
        </div>
        <div class="flex gap-2">
            <dui-button variant="ButtonVariant.Secondary" class="flex-1">
                <dui-icon name="check"/>
                Following
            </dui-button>
            <dui-button class="flex-1">
                <dui-icon name="mail"/>
                Message
            </dui-button>
        </div>
    </div>
</dui-popover>

Position

Control the position of the popover relative to the trigger element using the position attribute. The default value is PositionArea.Bottom.

You can visualize the various values by using the Chrome team's Anchor Position Tool.

<div class="grid grid-cols-2 gap-4">
    <dui-button variant="ButtonVariant.Outline" popovertarget="--popover-position-top-span-right" class="w-full">
        Top Span Right
    </dui-button>
    <dui-button variant="ButtonVariant.Outline" popovertarget="--popover-position-right-center" class="w-full">
        Right Center
    </dui-button>
    <dui-button variant="ButtonVariant.Outline" popovertarget="--popover-position-left-span-top" class="w-full">
        Left Span Top
    </dui-button>
    <dui-button variant="ButtonVariant.Outline" popovertarget="--popover-position-bottom-span-left" class="w-full">
        Bottom Span Left
    </dui-button>
</div>
<dui-popover position="PositionArea.TopSpanRight" id="--popover-position-top-span-right" class="w-[230px]">
    <dui-stack>
        <dui-skeleton class="h-4 w-[200px]"/>
        <dui-skeleton class="h-4 w-[200px]"/>
    </dui-stack>
</dui-popover>
<dui-popover position="PositionArea.RightCenter" id="--popover-position-right-center" class="w-[230px]">
    <dui-stack>
        <dui-skeleton class="h-4 w-[200px]"/>
        <dui-skeleton class="h-4 w-[200px]"/>
    </dui-stack>
</dui-popover>
<dui-popover position="PositionArea.LeftSpanTop" id="--popover-position-left-span-top" class="w-[230px]">
    <dui-stack>
        <dui-skeleton class="h-4 w-[200px]"/>
        <dui-skeleton class="h-4 w-[200px]"/>
    </dui-stack>
</dui-popover>
<dui-popover position="PositionArea.BottomSpanLeft" id="--popover-position-bottom-span-left" class="w-[230px]">
    <dui-stack>
        <dui-skeleton class="h-4 w-[200px]"/>
        <dui-skeleton class="h-4 w-[200px]"/>
    </dui-stack>
</dui-popover>

Offset

Control the offset of the popover from the trigger element using one of Tailwind's margin utilities.

<dui-button variant="ButtonVariant.Outline" popovertarget="--popover-offset">
    Offset
</dui-button>
<dui-popover id="--popover-offset" class="mt-5">
    <dui-stack>
        <dui-skeleton class="h-4 w-[250px]"/>
        <dui-skeleton class="h-4 w-[250px]"/>
    </dui-stack>
</dui-popover>

Manual dismiss

By default, <dui-popover> is light dismissable - meaning you can hide the popover by clicking outside of it or pressing the Esc key. You can change this behaviour by setting the popover attribute to manual which will require it to be explicitly closed using popovertargetaction="hide" or JavaScript.

<dui-button variant="ButtonVariant.Outline" popovertarget="--popover-manual-dismiss" popovertargetaction="show">
    Open Popover
</dui-button>
<dui-button variant="ButtonVariant.Outline" popovertarget="--popover-manual-dismiss"
            popovertargetaction="toggle">
    Toggle Popover
</dui-button>
<dui-popover id="--popover-manual-dismiss" popover="manual">
    <dui-button variant="ButtonVariant.Outline" popovertarget="--popover-manual-dismiss"
                popovertargetaction="hide">
        Close Popover
    </dui-button>
</dui-popover>

JavaScript API

Since <dui-popover> uses the Popover API, you can use the showPopover(), hidePopover() and togglePopover() methods to control visibility of the popover.

When using the JavaScript API, you also need to make sure you specify the source element to ensure the tooltip is aligned correctly with the UI element for which the tooltip is shown.

<dui-stack align="StackAlign.Center">
    <dui-group>
        <dui-button variant="ButtonVariant.Outline" id="--popover-js-api-button-open">
            Open
        </dui-button>
        <dui-button variant="ButtonVariant.Outline" id="--popover-js-api-button-close">
            Close
        </dui-button>
        <dui-button variant="ButtonVariant.Outline" id="--popover-js-api-button-toggle">
            Toggle
        </dui-button>
    </dui-group>
    <dui-avatar src="/avatars/avatar-1.jpg" id="--popover-js-api-avatar"/>
</dui-stack>
<dui-popover id="--popover-js-api" popover="manual">
    <dui-stack>
        <dui-skeleton class="h-4 w-[250px]"/>
        <dui-skeleton class="h-4 w-[250px]"/>
    </dui-stack>
</dui-popover>
<script>
    const apiPopover = document.getElementById('--popover-js-api');
    const apiPopoverAvatar = document.getElementById('--popover-js-api-avatar');
    const apiPopoverButtonOpen = document.getElementById('--popover-js-api-button-open');
    const apiPopoverButtonClose = document.getElementById('--popover-js-api-button-close');
    const apiTooltipButtonToggle = document.getElementById('--popover-js-api-button-toggle');

    apiPopoverButtonOpen.addEventListener('click', () => {
        apiPopover.showPopover({
            source: apiPopoverAvatar
        });
    });
    apiPopoverButtonClose.addEventListener('click', () => {
        apiPopover.hidePopover();
    });
    apiTooltipButtonToggle.addEventListener('click', () => {
        apiPopover.togglePopover({
            source: apiPopoverAvatar
        });
    });
</script>

JavaScript Events

The Popover API fires the beforetoggle and toggle events on <dui-popover> before it is shown/hidden and after it is show/hidden respectively. You can prevent opening the popover by calling event.preventDefault() in the beforetoggle event handler.

<dui-stack gap="StackGap.Small" class="min-w-md">
    <dui-button popovertarget="--popover-js-events" variant="ButtonVariant.Outline">
        Toggle Popover
    </dui-button>
    <dui-input type="checkbox" id="--popover-prevent-checkbox" label="Prevent popover toggle"/>
    <label class="text-sm font-bold">Events:</label>
    <div class="font-mono w-full h-40 overflow-y-auto bg-gray-50" id="--popover-js-events-output">
    </div>
</dui-stack>
<dui-popover id="--popover-js-events" class="mt-5">
    <dui-stack>
        <dui-skeleton class="h-4 w-[250px]"/>
        <dui-skeleton class="h-4 w-[250px]"/>
    </dui-stack>
</dui-popover>
<script>
    let counter = 0;
    const eventsPopover = document.getElementById('--popover-js-events');
    const eventsPopoverEventOutput = document.getElementById('--popover-js-events-output');
    const eventsPopoverPreventCheckbox = document.getElementById('--popover-prevent-checkbox');

    eventsPopover.addEventListener("beforetoggle", (e) => {
        if (eventsPopoverPreventCheckbox.checked === true) {
            eventsPopoverEventOutput.innerText = `${++counter}: Toggle prevented!\n` + eventsPopoverEventOutput.innerText;
            e.preventDefault();
            return;
        }
        eventsPopoverEventOutput.innerText = `${++counter}: Before toggle: ${e.oldState} -> ${e.newState}\n` + eventsPopoverEventOutput.innerText;
    });
    eventsPopover.addEventListener("toggle", (e) => {
        eventsPopoverEventOutput.innerText = `${++counter}: Toggle: ${e.oldState} -> ${e.newState}\n` + eventsPopoverEventOutput.innerText;
    });
</script>