Skip to content

Commit

Permalink
Alert pane: Allows providing contextual feedback messages for typical…
Browse files Browse the repository at this point in the history
… user actions (#1181)
  • Loading branch information
MarcSkovMadsen committed Jun 17, 2020
1 parent ef17e4d commit 626ac27
Show file tree
Hide file tree
Showing 5 changed files with 369 additions and 0 deletions.
130 changes: 130 additions & 0 deletions examples/reference/panes/Alert.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import panel as pn\n",
"pn.extension()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Alert Pane\n",
"\n",
"The ``Alert`` pane allows providing **contextual feedback messages** for typical user actions with the handful of available and flexible alert messages.\n",
"\n",
"It's heavily inspired by the [Bootstrap Alert](https://getbootstrap.com/docs/4.0/components/alerts/).\n",
"\n",
"#### Parameters:\n",
"\n",
"For layout and styling related parameters see the [customization user guide](../../user_guide/Customization.ipynb).\n",
"\n",
"* **``text``** (str): The contextual feedback message.\n",
"* **``alert_type``** (str): The type of Alert and one of `primary`, `secondary`, `success`, `danger`, `warning`, `info`, `light`, `dark`.\n",
"\n",
"___"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"alert_primary=pn.pane.Alert(text=f\"\"\"\\\n",
" This is a **primary** alert with [an example link](https://panel.holoviz.org/).\n",
" Give it a click if you like.\"\"\", alert_type=\"primary\")\n",
"alert_secondary=pn.pane.Alert(text=f\"\"\"\\\n",
" This is a **secondary** alert with [an example link](https://panel.holoviz.org/).\n",
" Give it a click if you like.\"\"\", alert_type=\"secondary\")\n",
"alert_success=pn.pane.Alert(text=f\"\"\"\\\n",
" This is a **success** alert with [an example link](https://panel.holoviz.org/).\n",
" Give it a click if you like.\"\"\", alert_type=\"success\")\n",
"alert_danger=pn.pane.Alert(text=f\"\"\"\\\n",
" This is a **danger** alert with [an example link](https://panel.holoviz.org/).\n",
" Give it a click if you like.\"\"\", alert_type=\"danger\")\n",
"alert_warning=pn.pane.Alert(text=f\"\"\"\\\n",
" This is a **warning** alert with [an example link](https://panel.holoviz.org/).\n",
" Give it a click if you like.\"\"\", alert_type=\"warning\")\n",
"alert_info=pn.pane.Alert(text=f\"\"\"\\\n",
" This is a **info** alert with [an example link](https://panel.holoviz.org/).\n",
" Give it a click if you like.\"\"\", alert_type=\"info\")\n",
"alert_light=pn.pane.Alert(text=f\"\"\"\\\n",
" This is a **light** alert with [an example link](https://panel.holoviz.org/).\n",
" Give it a click if you like.\"\"\", alert_type=\"light\")\n",
"alert_dark=pn.pane.Alert(text=f\"\"\"\\\n",
" This is a **dark** alert with [an example link](https://panel.holoviz.org/).\n",
" Give it a click if you like.\"\"\", alert_type=\"dark\")\n",
"\n",
"pn.Column(\n",
" alert_primary,\n",
" alert_secondary,\n",
" alert_success,\n",
" alert_danger,\n",
" alert_warning,\n",
" alert_info,\n",
" alert_light,\n",
" alert_dark,\n",
" sizing_mode=\"stretch_width\",\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Additional Content"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"text = \"\"\"\\\n",
"#### Well done!\n",
"\n",
"Aww yeah, you successfully read this important alert message. This example text is going to run a bit longer so that you can see how spacing within an alert works with this kind of content.\n",
"<hr>\n",
"\n",
"Did you notice the use of the divider?\n",
"\"\"\"\n",
"pn.pane.Alert(text=text,alert_type=\"success\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "panel",
"language": "python",
"name": "panel"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.4"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
136 changes: 136 additions & 0 deletions panel/_styles/alerts.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
.bk.alert {
padding: 0.75rem 1.25rem;
border: 1px solid transparent;
border-radius: 0.25rem;
/* Don't set margin because that will not render correctly! */
/* margin-bottom: 1rem; */
margin-top: 15px;
margin-bottom: 15px;
}
.bk.alert a {
color: rgb(11, 46, 19); /* #002752; */
font-weight: 700;
text-decoration: rgb(11, 46, 19);
text-decoration-color: rgb(11, 46, 19);
text-decoration-line: none;
text-decoration-style: solid;
text-decoration-thickness: auto;
}
.bk.alert a:hover {
color: rgb(11, 46, 19);
font-weight: 700;
text-decoration: underline;
}

.bk.alert-primary {
color: #004085;
background-color: #cce5ff;
border-color: #b8daff;
}
.bk.alert-primary hr {
border-top-color: #9fcdff;
}

.bk.alert-secondary {
color: #383d41;
background-color: #e2e3e5;
border-color: #d6d8db;
}
.bk.alert-secondary hr {
border-top-color: #c8cbcf;
}

.bk.alert-success {
color: #155724;
background-color: #d4edda;
border-color: #c3e6cb;
}

.bk.alert-success hr {
border-top-color: #b1dfbb;
}

.bk.alert-info {
color: #0c5460;
background-color: #d1ecf1;
border-color: #bee5eb;
}
.bk.alert-info hr {
border-top-color: #abdde5;
}

.bk.alert-warning {
color: #856404;
background-color: #fff3cd;
border-color: #ffeeba;
}

.bk.alert-warning hr {
border-top-color: #ffe8a1;
}

.bk.alert-danger {
color: #721c24;
background-color: #f8d7da;
border-color: #f5c6cb;
}
.bk.alert-danger hr {
border-top-color: #f1b0b7;
}

.bk.alert-light {
color: #818182;
background-color: #fefefe;
border-color: #fdfdfe;
}
.bk.alert-light hr {
border-top-color: #ececf6;
}

.bk.alert-dark {
color: #1b1e21;
background-color: #d6d8d9;
border-color: #c6c8ca;
}
.bk.alert-dark hr {
border-top-color: #b9bbbe;
}


/* adjfæl */

.bk.alert-primary a {
color: #002752;
}

.bk.alert-secondary a {
color: #202326;
}


.bk.alert-success a {
color: #0b2e13;
}


.bk.alert-info a {
color: #062c33;
}


.bk.alert-warning a {
color: #533f03;
}


.bk.alert-danger a {
color: #491217;
}

.bk.alert-light a {
color: #686868;
}

.bk.alert-dark a {
color: #040505;
}
1 change: 1 addition & 0 deletions panel/pane/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from __future__ import absolute_import, division, unicode_literals

from .ace import Ace # noqa
from .alert import Alert
from .base import PaneBase, Pane, panel # noqa
from .equation import LaTeX # noqa
from .deckgl import DeckGL # noqa
Expand Down
51 changes: 51 additions & 0 deletions panel/pane/alert.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""Bootstrap inspired Alerts
See https://getbootstrap.com/docs/4.0/components/alerts/
"""
import param

from panel.pane.markup import Markdown

ALERT_TYPES = ["primary", "secondary", "success", "danger", "warning", "info", "light", "dark"]


class Alert(Markdown):
"""
An Alert that renders Markdown
- CSS Styling is done via the classes `alert` and `alert-TYPE`, where TYPE is the alert_type.
- sizing_mode is set to `stretch_width` by default
"""

alert_type = param.ObjectSelector("primary", objects=ALERT_TYPES)

def __init__(self, text: str, **kwargs):
"""An Primary Alert that renders Markdown
- CSS Styling is done via the classes `alert` and `alert-primary`.
- sizing_mode is set to `stretch_width` by default
Arguments:
text {str} -- Some MarkDown text
"""
if "margin" not in kwargs:
kwargs["margin"] = (0, 0, 25, 0)
if "sizing_mode" not in kwargs:
kwargs["sizing_mode"] = "stretch_width"

super().__init__(text, **kwargs)

self._set_css_classes()

@param.depends("alert_type", watch=True)
def _set_css_classes(self):
css_classes = []
if self.css_classes:
for class_ in self.css_classes:
if class_ != "alert" and not class_.startswith("alert-"):
css_classes.append(class_)

css_classes.append("alert")
css_classes.append(f"alert-{self.alert_type}")

self.css_classes = css_classes
51 changes: 51 additions & 0 deletions panel/tests/pane/test_alert.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""In this module we test the functionality of the alerts"""
import pytest

import panel as pn
from panel.pane import Alert
from panel.pane.alert import ALERT_TYPES


def test_constructor():
"""Test that an Alert can be instantiated"""
alert = Alert(text="This is some text")
# pylint: disable=no-member
assert set(alert.css_classes) == {"alert", f"alert-{Alert.param.alert_type.default}"}
# pylint: enable=no-member


@pytest.mark.parametrize(["alert_type"], [(alert_type,) for alert_type in ALERT_TYPES])
def test_alert_type_change(alert_type):
"""Test that an alert can change alert_type"""
alert = Alert(text="This is some text")

alert.alert_type = alert_type
assert set(alert.css_classes) == {"alert", f"alert-{alert_type}"}

def test_existing_css_classes():
"""Test that an alert can change alert_type"""
alert = Alert(text="This is some text", css_classes=["important"])
assert set(alert.css_classes) == {"alert", f"alert-{Alert.param.alert_type.default}", "important"}

alert.alert_type="info"
assert set(alert.css_classes) == {"alert", f"alert-info", "important"}


def test_all_view():
"""Test that we can construct and view all Alerts"""
alerts = []
for alert_type in ALERT_TYPES:
text = f"""\
This is a **{alert_type}** alert with [an example link](https://panel.holoviz.org/).
Give it a click if you like."""
alert = Alert(text=text, alert_type=alert_type)
alerts.append(alert)

assert "alert" in alert.css_classes
assert f"alert-{alert_type}" in alert.css_classes

return pn.Column(*alerts, sizing_mode="stretch_width")


if __name__.startswith("bk"):
test_all_view().servable()

0 comments on commit 626ac27

Please sign in to comment.