示例#1
0
 def __init__(self, *children, **attributes):
     """
     Parameters
     ----------
     *children
         content within the tile
     **attributes : optional
         keyword arguments representing the specific HTML attributes for the tile
     """
     hg.merge_html_attrs(attributes, {"_class": "bx--tile"})
     super().__init__(*children, **attributes)
示例#2
0
 def __init__(
     self,
     label=None,
     help_text=None,
     errors=None,
     inputelement_attrs=None,
     boundfield=None,
     **attributes,
 ):
     inputelement_attrs = inputelement_attrs or {}
     attrs = {}
     if boundfield:
         attrs["checked"] = hg.F(
             lambda c: hg.resolve_lazy(boundfield, c).field.widget.
             check_test(hg.resolve_lazy(boundfield, c).value()))
     inputelement_attrs = _combine_lazy_dict(inputelement_attrs, attrs)
     label = None if label is None else label.label
     super().__init__(
         self.get_input_element(inputelement_attrs, errors),
         hg.LABEL(
             hg.SPAN(_class="bx--radio-button__appearance"),
             hg.SPAN(label, _class="bx--radio-button__label-text"),
             _class="bx--radio-button__label",
             _for=inputelement_attrs.get("id"),
         ),
         help_text,
         errors,
         **hg.merge_html_attrs(attributes,
                               {"_class": "bx--radio-button-wrapper"}),
     )
示例#3
0
 def __init__(
     self,
     label=None,
     help_text=None,
     errors=None,
     inputelement_attrs=None,
     boundfield=None,
     **attributes,
 ):
     inputelement_attrs = inputelement_attrs or {}
     super().__init__(
         label,
         hg.DIV(
             self.get_input_element(inputelement_attrs, errors),
             hg.DIV(
                 hg.BUTTON(
                     Icon("caret--up", size=16),
                     _class="bx--number__control-btn up-icon",
                     type="button",
                 ),
                 hg.BUTTON(
                     Icon("caret--down", size=16),
                     _class="bx--number__control-btn down-icon",
                     type="button",
                 ),
                 _class="bx--number__controls",
             ),
             _class="bx--number__input-wrapper",
         ),
         errors,
         help_text,
         data_numberinput=True,
         data_invalid=hg.If(errors.condition, True),
         **hg.merge_html_attrs(attributes, {"_class": "bx--number"}),
     )
示例#4
0
    def __init__(
        self,
        label: Any,
        description: Any,
        align: str = "center",
        position: str = "bottom",
        **attributes,
    ):
        """
        Parameters
        ----------
        label : Any
            Text to be displayed
        description : Any
            One line of string defining the label
        align : str, optional
            Specify where the arrow pointing the icon should align to the text.
            It can be either start, center, or end.
            The default value is 'center'.
        position : str
            Where should the tooltip appear besides the tooltip icon.
            It can be either top, left, right, or bottom.
            The default value is 'bottom'.
        """
        tooltip_attributes = {
            "_class": "bx--tooltip--definition bx--tooltip--a11y ",
            "data_tooltip_definition": True,
        }

        tooltip_attributes = hg.merge_html_attrs(tooltip_attributes, attributes)
        asst_txt_id = hg.html_id(self, "bx--tooltip--definition-id")

        super().__init__(
            hg.DIV(
                hg.DIV(
                    label,
                    _class=(
                        "bx--tooltip__trigger "
                        "bx--tooltip--a11y "
                        "bx--tooltip__trigger--definition "
                        "bx--tooltip--%s "
                        "bx--tooltip--align-%s"
                    )
                    % (position, align),
                    aria_describedby=asst_txt_id,
                ),
                hg.DIV(
                    description,
                    _class="bx--assistive-text",
                    id=asst_txt_id,
                    role="tooltip",
                ),
                **tooltip_attributes,
            )
        )
示例#5
0
    def __init__(
        self,
        label=None,
        help_text=None,
        errors=None,
        inputelement_attrs=None,
        boundfield=None,
        backend=None,
        **attributes,
    ):
        """
        :param SearchBackendConfig backend: Where and how to get search results
        """
        inputelement_attrs = inputelement_attrs or {}

        # This works inside a formset. Might need to be changed for other usages.

        widget_id = inputelement_attrs.get("id")
        resultcontainerid = hg.format("search-result-{}", widget_id)
        tag_id = hg.format("{}-tag", widget_id)
        super().__init__(
            label,
            Tag(
                hg.F(lambda c: hg.resolve_lazy(boundfield, c).field.to_python(
                    hg.resolve_lazy(boundfield, c).value()))
                if boundfield else "",
                id=tag_id,
                style=hg.If(
                    inputelement_attrs.get("value"),
                    hg.BaseElement(""),
                    hg.BaseElement("display: none;"),
                ),
                onclick="return false;",
            ),
            self.get_input_element(inputelement_attrs, errors, type="hidden"),
            Search(
                backend=backend,
                resultcontainerid=resultcontainerid,
                resultcontainer_onload_js=_resultcontainer_onload_js(
                    backend, resultcontainerid, tag_id, widget_id),
                size="lg",
                disabled=inputelement_attrs.get("disabled", False),
                widgetattributes={"id": hg.format("search__{}", widget_id)},
            ),
            help_text,
            errors,
            **hg.merge_html_attrs(attributes,
                                  {"_class": "bx--text-input-wrapper"}),
        )
示例#6
0
    def __init__(
        self,
        description: Any,
        icon: Union[Icon, str] = "information",
        align: str = "center",
        position: str = "bottom",
        **attributes,
    ):
        """
        Parameters
        ----------
        description : Any
            One line of string describing an icon.
        icon : Icon, str, optional
            Specify an icon for the tooltip.
            The default value is 'information'.
        align : str, optional
            Specify where the arrow pointing the icon should align to the text.
            It can be either start, center, or end.
            The default value is 'center'.
        position : str
            Where should the tooltip appear besides the tooltip icon.
            It can be either top, left, right, or bottom.
            The default value is 'bottom'.
        """
        tooltip_attributes = {
            "_class": (
                "bx--tooltip__trigger "
                "bx--tooltip--a11y "
                "bx--tooltip--%s "
                "bx--tooltip--align-%s"
            )
            % (position, align),
            "data_tooltip_icon": True,
        }

        tooltip_attributes = hg.merge_html_attrs(tooltip_attributes, attributes)
        icon = _get_icon(icon)

        super().__init__(
            hg.DIV(
                hg.SPAN(description, _class="bx--assistive-text"),
                icon,
                **tooltip_attributes,
            )
        )
示例#7
0
 def __init__(
     self,
     label=None,
     help_text=None,
     errors=None,
     inputelement_attrs=None,
     boundfield=None,
     **attributes,
 ):
     inputelement_attrs = inputelement_attrs or {}
     attrs = {}
     if boundfield:
         attrs["checked"] = hg.F(
             lambda c: hg.resolve_lazy(boundfield, c).field.widget.
             check_test(hg.resolve_lazy(boundfield, c).value()))
         attrs["value"] = None
     inputelement_attrs = _combine_lazy_dict(inputelement_attrs, attrs)
     # labels for checkboxes are treated a bit different, need to use plain value
     label = hg.F(lambda c, label=label: getattr(hg.resolve_lazy(label, c),
                                                 "label", label))
     required = hg.F(
         lambda c, label=label: hg.resolve_lazy(label, c) is not None)
     super().__init__(
         hg.LABEL(
             self.get_input_element(inputelement_attrs, errors),
             label,
             hg.If(inputelement_attrs.get("required"),
                   hg.If(required, REQUIRED_LABEL)),
             _class=hg.BaseElement(
                 "bx--checkbox-label",
                 hg.If(inputelement_attrs.get("disabled"),
                       " bx--label--disabled"),
             ),
             data_contained_checkbox_state=hg.If(
                 inputelement_attrs.get("checked"),
                 "true",
                 "false",
             ),
             data_invalid=hg.If(getattr(errors, "condition", False), True),
         ),
         help_text,
         errors,
         **hg.merge_html_attrs(attributes,
                               {"_class": "bx--checkbox-wrapper"}),
     )
示例#8
0
 def __init__(
     self,
     label=None,
     help_text=None,
     errors=None,
     inputelement_attrs=None,
     boundfield=None,
     icon=None,
     **attributes,
 ):
     inputelement_attrs = inputelement_attrs or {}
     super().__init__(
         label,
         hg.DIV(
             hg.If(
                 getattr(errors, "condition", None),
                 Icon(
                     "warning--filled",
                     size=16,
                     _class="bx--text-input__invalid-icon",
                 ),
             ),
             self.get_input_element(inputelement_attrs, errors),
             hg.If(
                 icon,
                 hg.If(
                     hg.F(lambda c: isinstance(icon, str)),
                     Icon(
                         icon,
                         size=16,
                         _class="text-input-icon",
                     ),
                     icon,
                 ),
             ),
             _class=("bx--text-input__field-wrapper" +
                     (" text-input-with-icon" if icon is not None else "")),
             data_invalid=hg.If(getattr(errors, "condition", None), True),
         ),
         errors,
         help_text,
         **hg.merge_html_attrs(attributes,
                               {"_class": "bx--text-input-wrapper"}),
     )
示例#9
0
 def __init__(
     self,
     label=None,
     help_text=None,
     errors=None,
     inputelement_attrs=None,
     boundfield=None,
     **attributes,
 ):
     inputelement_attrs = inputelement_attrs or {}
     showhidebtn = Button(
         _("Show password"),
         icon=hg.BaseElement(
             Icon("view--off",
                  _class="bx--icon--visibility-off",
                  hidden="true"),
             Icon("view", _class="bx--icon--visibility-on"),
         ),
         notext=True,
     )
     # override attributes from button
     showhidebtn.attributes["_class"] = (
         "bx--text-input--password__visibility__toggle bx--tooltip__trigger "
         "bx--tooltip--a11y bx--tooltip--bottom bx--tooltip--align-center")
     super().__init__(
         label=label,
         help_text=help_text,
         errors=errors,
         inputelement_attrs=_combine_lazy_dict(
             inputelement_attrs, {"data_toggle_password_visibility": True}),
         icon=showhidebtn,
         **hg.merge_html_attrs(
             attributes,
             {
                 "data_text_input": True,
                 "_class": "bx--password-input-wrapper"
             },
         ),
     )
示例#10
0
    def __init__(self, header: Any, content: Any, **attributes):
        """
        Parameters
        ----------
        header : Any
            the header of the tile that is not hidden under the fold
        content : Any
            the hidden content that only visible when user click to unfold the tile
        **attributes : optional
            keyword arguments representing the specific HTML attributes for the tile
        """

        super().__init__(
            hg.BUTTON(Icon("chevron--down", size="16"),
                      _class="bx--tile__chevron"),
            hg.DIV(
                hg.SPAN(
                    header,
                    data_tile_atf=True,
                    _class="bx--tile-content__above-the-fold",
                ),
                hg.SPAN(
                    content,
                    _class="bx--tile-content__below-the-fold",
                    onclick="event.stopPropagation();",
                    style="cursor: initial",
                ),
                _class="bx--tile-content",
            ),
            **hg.merge_html_attrs(
                attributes,
                {
                    "_class": "bx--tile bx--tile--expandable",
                    "data_tile": "expandable",
                    "tabindex": "0",
                },
            ))
示例#11
0
    def __init__(
        self,
        *tabs,
        container=False,
        tabpanel_attributes=None,
        labelcontainer_attributes=None,
        **attributes,
    ):
        tabpanel_attributes = collections.defaultdict(
            str, tabpanel_attributes or {})
        labelcontainer_attributes = collections.defaultdict(
            str, labelcontainer_attributes or {})

        self.tablabels = hg.UL(_class="bx--tabs__nav bx--tabs__nav--hidden",
                               style="flex-wrap: wrap;")
        labelcontainer_attributes["_class"] += " bx--tabs" + (
            " bx--tabs--container" if container else "")
        self.labelcontainer = hg.DIV(
            hg.DIV(
                hg.A(
                    href="javascript:void(0)",
                    _class="bx--tabs-trigger-text",
                    tabindex=-1,
                ),
                _class="bx--tabs-trigger",
                style="display: none",
                tabindex=0,
            ),
            self.tablabels,
            data_tabs=True,
            **hg.merge_html_attrs(
                {
                    "onload":
                    "if( $('.bx--tabs__nav-item--selected', this) == null ) "
                    "(new CarbonComponents.Tab(this)).setActive($('.bx--tabs__nav-item', this))",
                },
                labelcontainer_attributes,
            ),
        )
        tabpanel_attributes["_class"] += " bx--tab-content"
        self.tabpanels = hg.DIV(**tabpanel_attributes)

        firsttab = None
        for i, (label, content) in enumerate(tabs):
            tabid = f"tab-{slugify(label)}-{i}"
            panelid = f"panel-{slugify(label)}-{i}"
            if firsttab is None:
                firsttab = tabid
            self.tablabels.append(
                TabLabel(
                    label,
                    tabid,
                    panelid,
                    HasBreadCookieValue("selected-tab", tabid, firsttab),
                ))
            self.tabpanels.append(
                TabPanel(
                    content,
                    panelid,
                    tabid,
                    HasBreadCookieValue("selected-tab", tabid, firsttab),
                ))
        super().__init__(
            self.labelcontainer,
            self.tabpanels,
        )
示例#12
0
    def __init__(
        self,
        label=None,
        help_text=None,
        errors=None,
        inputelement_attrs=None,
        boundfield=None,
        style_short=False,
        style_simple=False,
        **attributes,
    ):
        inputelement_attrs = inputelement_attrs or {}

        def format_date_value(context):
            bfield = hg.resolve_lazy(boundfield, context)
            return bfield.field.widget.format_value(bfield.value())

        super().__init__(
            hg.DIV(
                label,
                hg.If(
                    style_simple,
                    self.get_input_element(
                        inputelement_attrs,
                        errors,
                        data_invalid=hg.If(getattr(errors, "condition"), True),
                        pattern=hg.F(lambda c: (TimeRE().compile(
                            hg.resolve_lazy(boundfield, c).field.widget.format
                            or formats.get_format(
                                hg.resolve_lazy(boundfield, c).field.widget.
                                format_key)[0]).pattern)),
                        value=hg.F(format_date_value),
                    ),
                    hg.DIV(
                        self.get_input_element(
                            inputelement_attrs,
                            errors,
                            data_date_picker_input=True,
                            data_invalid=hg.If(getattr(errors, "condition"),
                                               True),
                            data_date_format=hg.F(lambda c: to_php_formatstr(
                                hg.resolve_lazy(boundfield, c).field.widget.
                                format,
                                hg.resolve_lazy(boundfield, c
                                                ).field.widget.format_key,
                            )),
                            value=hg.F(format_date_value),
                        ),
                        Icon(
                            "calendar",
                            size=16,
                            _class="bx--date-picker__icon",
                            data_date_picker_icon="true",
                        ),
                        _class="bx--date-picker-input__wrapper",
                    ),
                ),
                help_text,
                errors,
                _class="bx--date-picker-container",
            ),
            **hg.merge_html_attrs(
                attributes,
                {
                    "data_date_picker":
                    not style_simple,
                    "data_date_picker_type":
                    None if style_simple else "single",
                    "_class":
                    hg.BaseElement(
                        "bx--date-picker",
                        hg.If(
                            style_simple,
                            " bx--date-picker--simple",
                            " bx--date-picker--single",
                        ),
                        hg.If(style_short, " bx--date-picker--short"),
                    ),
                },
            ),
        )
示例#13
0
    def __init__(
            self,
            label=None,
            help_text=None,
            errors=None,
            inputelement_attrs=None,
            boundfield=None,  # for django-form select elements use this
            choices=None,  # for non-django-form select elements use this
            **attributes,  # for non-django-form select elements use this
    ):
        inputelement_attrs = inputelement_attrs or {}
        optgroups = (_optgroups_from_choices(
            choices,
            name=inputelement_attrs.get("name"),
            value=inputelement_attrs.get("value"),
        ) if choices else _gen_optgroup(boundfield))

        def countselected(context):
            options = [
                o for og in hg.resolve_lazy(optgroups, context) for o in og[1]
            ]
            return len([o for o in options if o and o["selected"]])

        searchfieldid = hg.html_id(self)
        super().__init__(
            label,
            hg.If(
                inputelement_attrs.get("disabled"),
                hg.DIV(
                    hg.Iterator(
                        optgroups,
                        "optiongroup",
                        hg.Iterator(
                            hg.C("optiongroup.1"),
                            "option",
                            hg.If(hg.C("option.selected"),
                                  Tag(hg.C("option.label"))),
                        ),
                    )),
                hg.DIV(
                    hg.DIV(
                        hg.DIV(
                            hg.F(countselected),
                            Icon(
                                "close",
                                focusable="false",
                                size=15,
                                role="img",
                                onclick=
                                "clearMultiselect(this.parentElement.parentElement.parentElement)",
                            ),
                            role="button",
                            _class=
                            "bx--list-box__selection bx--list-box__selection--multi bx--tag--filter",
                            tabindex="0",
                            title="Clear all selected items",
                        ),
                        hg.INPUT(
                            id=searchfieldid,
                            _class="bx--text-input",
                            placeholder="Filter...",
                            onclick=
                            "this.parentElement.nextElementSibling.style.display = 'block'",
                            onkeyup=
                            "filterOptions(this.parentElement.parentElement)",
                        ),
                        hg.DIV(
                            Icon("chevron--down",
                                 size=16,
                                 role="img",
                                 focusable="false"),
                            _class="bx--list-box__menu-icon",
                            onclick=
                            "this.parentElement.nextElementSibling.style.display = this.parentElement.nextElementSibling.style.display == 'none' ? 'block' : 'none';",
                        ),
                        role="button",
                        _class="bx--list-box__field",
                        tabindex="0",
                        onload=
                        "window.addEventListener('click', (e) => {this.nextElementSibling.style.display = 'none'})",
                    ),
                    hg.FIELDSET(
                        hg.Iterator(
                            optgroups,
                            "optgroup",
                            hg.Iterator(
                                hg.C("optgroup.1"),
                                "option",
                                hg.DIV(
                                    hg.DIV(
                                        hg.DIV(
                                            hg.LABEL(
                                                hg.INPUT(
                                                    type="checkbox",
                                                    readonly=True,
                                                    _class="bx--checkbox",
                                                    value=hg.C("option.value"),
                                                    lazy_attributes=hg.C(
                                                        "option.attrs"),
                                                    onchange=
                                                    "updateMultiselect(this.closest('.bx--multi-select'))",
                                                    checked=hg.C(
                                                        "option.selected"),
                                                    name=hg.C("option.name"),
                                                ),
                                                hg.SPAN(
                                                    _class=
                                                    "bx--checkbox-appearance"),
                                                hg.SPAN(
                                                    hg.C("option.label"),
                                                    _class=
                                                    "bx--checkbox-label-text",
                                                ),
                                                title=hg.C("option.label"),
                                                _class="bx--checkbox-label",
                                            ),
                                            _class=
                                            "bx--form-item bx--checkbox-wrapper",
                                        ),
                                        _class=
                                        "bx--list-box__menu-item__option",
                                    ),
                                    _class="bx--list-box__menu-item",
                                ),
                            ),
                        ),
                        _class="bx--list-box__menu",
                        role="listbox",
                        style="display: none",
                    ),
                    _class=hg.BaseElement(
                        "bx--multi-select bx--list-box bx--multi-select--selected bx--combo-box bx--multi-select--filterable",
                        hg.If(
                            inputelement_attrs.get("disabled"),
                            " bx--list-box--disabled",
                        ),
                    ),
                    data_invalid=hg.If(getattr(errors, "condition", None),
                                       True),
                ),
            ),
            help_text,
            errors,
            **hg.merge_html_attrs(
                attributes,
                {
                    "onclick": "event.stopPropagation()",
                    "_class": "bx--list-box__wrapper",
                },
            ),
        )
示例#14
0
 def __init__(
     self,
     label=None,
     help_text=None,
     errors=None,
     inputelement_attrs=None,
     boundfield=None,
     inline=False,
     choices=None,  # for non-django-form select elements use this
     **attributes,
 ):
     inputelement_attrs = inputelement_attrs or {}
     select_wrapper = hg.DIV(
         hg.SELECT(
             hg.Iterator(
                 _optgroups_from_choices(
                     choices,
                     name=inputelement_attrs.get("name"),
                     value=inputelement_attrs.get("value"),
                 ) if choices else _gen_optgroup(boundfield),
                 "optgroup",
                 hg.If(
                     hg.C("optgroup.0"),
                     hg.OPTGROUP(
                         hg.Iterator(
                             hg.C("optgroup.1"),
                             "option",
                             hg.OPTION(
                                 hg.C("option.label"),
                                 _class="bx--select-option",
                                 value=hg.C("option.value"),
                                 lazy_attributes=hg.C("option.attrs"),
                             ),
                         ),
                         _class="bx--select-optgroup",
                         label=hg.C("optgroup.0"),
                     ),
                     hg.Iterator(
                         hg.C("optgroup.1"),
                         "option",
                         hg.OPTION(
                             hg.C("option.label"),
                             _class="bx--select-option",
                             value=hg.C("option.value"),
                             lazy_attributes=hg.C("option.attrs"),
                         ),
                     ),
                 ),
             ),
             lazy_attributes=_append_classes(
                 inputelement_attrs,
                 self.carbon_input_class,
                 hg.If(
                     getattr(errors, "condition", None),
                     self.carbon_input_error_class,
                 ),
             ),
         ),
         Icon(
             "chevron--down",
             size=16,
             _class="bx--select__arrow",
             aria_hidden="true",
         ),
         hg.If(
             getattr(errors, "condition", None),
             Icon(
                 "warning--filled",
                 size=16,
                 _class="bx--select__invalid-icon",
             ),
         ),
         _class="bx--select-input__wrapper",
         data_invalid=hg.If(getattr(errors, "condition", None), True),
     )
     super().__init__(
         label,
         hg.If(
             inline,
             hg.DIV(
                 select_wrapper,
                 errors,
                 _class="bx--select-input--inline__wrapper",
             ),
             select_wrapper,
         ),
         help_text,
         hg.If(inline, None, errors),  # not displayed if this is inline
         **hg.merge_html_attrs(
             attributes,
             {
                 "_class":
                 hg.BaseElement(
                     "bx--select",
                     hg.If(inline, " bx--select--inline"),
                     hg.If(getattr(errors, "condition", None),
                           " bx--select--invalid"),
                     hg.If(
                         inputelement_attrs.get("disabled"),
                         " bx--select--disabled",
                     ),
                 ),
             },
         ),
     )
示例#15
0
    def __init__(
        self,
        label: Any,
        body: Any,
        heading: Optional[Any] = None,
        link: Optional[Link] = None,
        button: Optional[Button] = None,
        icon: Union[Icon, str] = "information",
        menudirection: str = "bottom",
        **attributes,
    ):
        """
        Parameters
        ----------
        label : Any
            Label for a tooltip
        body : Any
            The content inside a tooltip
        heading : Any, optional
            A heading for a tooltip.
        link : Link, optional
            Bread's Link NamedTuple in case you want to bring users to a specific webpage.
        button : Button, optional
            Insert the Bread's Button onto the tooltip.
        icon : Icon, str, optional
            Specify an icon for the tooltip
            The default value is "information".
        menudirection : str
            Where should the tooltip appear besides the tooltip icon.
            It can be either top, left, right, or bottom.
            The default value is "bottom".
        """
        base_class = "bx--tooltip"

        footer_elements: typing.List[hg.BaseElement] = []
        if link:
            footer_elements.append(hg.A(link.label, href=link.href, _class="bx--link"))
        if button:
            footer_elements.append(button)

        base_id = hg.html_id(self, base_class + "-id")
        label_id = f"{base_id}-label"

        trigger_attributes = {
            "_class": base_class + "__trigger",
            "aria_controls": base_id,
            "aria_expanded": "false",
            "aria_haspopup": "true",
            "aria_labelledby": label_id,
            "data_tooltip_target": "#" + base_id,
            "data_tooltip_trigger": True,
        }

        tooltip_attributes = {
            "_class": base_class,
            "aria_hidden": "true",
            "data_floating_menu_direction": menudirection,
            "id": base_id,
        }

        tooltip_content_attributes = {
            "_class": base_class + "__content",
            "aria_describedby": base_id + "-body",
            "aria_labelledby": label_id,
            "role": "dialog",
            "tabindex": "-1",
        }

        tooltip_attributes = hg.merge_html_attrs(tooltip_attributes, attributes)
        icon = _get_icon(icon)

        super().__init__(
            # tooltip label
            hg.DIV(
                label,
                # cannot use bread's Button class because
                # only the class bx--tooltip__trigger can be used
                hg.DIV(
                    icon,
                    **trigger_attributes,
                ),
                id=label_id,
                _class=base_class + "__label",
            ),
            # the real tooltip goes here
            hg.DIV(
                hg.SPAN(_class=base_class + "__caret"),
                hg.DIV(
                    hg.If(
                        bool(heading),
                        hg.H4(
                            heading,
                            id=base_id + "-heading",
                            _class=base_class + "__heading",
                        ),
                    ),
                    hg.P(body, id=base_id + "-body"),
                    hg.If(
                        len(footer_elements) > 0,
                        hg.DIV(
                            *footer_elements,
                            _class=base_class + "__footer",
                        ),
                    ),
                    **tooltip_content_attributes,
                ),
                hg.SPAN(tabindex="0"),
                **tooltip_attributes,
            ),
        )