diff --git a/branca/element.py b/branca/element.py index 853ca16..504e7cc 100644 --- a/branca/element.py +++ b/branca/element.py @@ -328,7 +328,8 @@ class Figure(Element): A percentage defining the aspect ratio of the Figure. It will be ignored if height is not None. title : str, default None - Figure title. + Figure title. Also used as the ``title`` attribute (accessible + name) of the iframe when the Figure is displayed in a notebook. figsize : tuple of two int, default None If you're a matplotlib addict, you can overwrite width and height. Values will be converted into pixels in using 60 dpi. @@ -412,24 +413,37 @@ def render(self, **kwargs) -> str: def _repr_html_(self, **kwargs) -> str: """Displays the Figure in a Jupyter notebook.""" html = escape(self.render(**kwargs)) + # Give the iframe an accessible name when a title is set, so it is + # not flagged by "frames must have an accessible name" audits. + title_attr = f' title="{escape(self.title)}"' if self.title else "" if self.height is None: iframe = ( '
' '
' # noqa 'Make this Notebook Trusted to load map: File -> Trust Notebook' # noqa - '" "
" - ).format(html=html, width=self.width, ratio=self.ratio) + ).format( + html=html, + width=self.width, + ratio=self.ratio, + title_attr=title_attr, + ) else: iframe = ( - '" - ).format(html=html, width=self.width, height=self.height) + ).format( + html=html, + width=self.width, + height=self.height, + title_attr=title_attr, + ) return iframe def add_subplot(self, x: int, y: int, n: int, margin: float = 0.05) -> "Div": @@ -640,6 +654,10 @@ class IFrame(Element): height. Values will be converted into pixels in using 60 dpi. For example figsize=(10, 5) will result in width="600px", height="300px". + title : str, default None + Value for the iframe's ``title`` attribute, used as the frame's + accessible name. Set it to satisfy accessibility audits that + require every frame to have an accessible name. """ def __init__( @@ -649,10 +667,12 @@ def __init__( height: Optional[str] = None, ratio: str = "60%", figsize: Optional[Tuple[int, int]] = None, + title: Optional[str] = None, ): super().__init__() self._name = "IFrame" + self.title = title self.width = width self.height = height self.ratio = ratio @@ -671,21 +691,32 @@ def render(self, **kwargs) -> str: html = "data:text/html;charset=utf-8;base64," + base64.b64encode( html.encode("utf8"), ).decode("utf8") + title_attr = f' title="{escape(self.title)}"' if self.title else "" if self.height is None: iframe = ( '
' '
' # noqa - '" "
" - ).format(html=html, width=self.width, ratio=self.ratio) + ).format( + html=html, + width=self.width, + ratio=self.ratio, + title_attr=title_attr, + ) else: iframe = ( - '' - ).format(html=html, width=self.width, height=self.height) + ).format( + html=html, + width=self.width, + height=self.height, + title_attr=title_attr, + ) return iframe diff --git a/tests/test_element.py b/tests/test_element.py new file mode 100644 index 0000000..934703a --- /dev/null +++ b/tests/test_element.py @@ -0,0 +1,76 @@ +""" +Tests for branca.element +------------------------ +""" + +from html.parser import HTMLParser + +import branca.element as elem + + +class _IframeAttrs(HTMLParser): + """Collect the attributes of every