Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Alert pane: Allows providing contextual feedback messages for typical user actions #1181

Merged
merged 5 commits into from
Jun 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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()