Repository: deephaven/deephaven-plugins (language: python)

Issue:
docs: ui.inline_alert
Blocked by implementation.


Discussion / hints:

Fix patch:
diff --git a/plugins/ui/docs/components/inline_alert.md b/plugins/ui/docs/components/inline_alert.md
new file mode 100644
index 000000000..e6fd17b72
--- /dev/null
+++ b/plugins/ui/docs/components/inline_alert.md
@@ -0,0 +1,61 @@
+# Inline Alert
+
+Inline alerts display non-modal messages related to objects in a view, often used for form validation to aggregate feedback for multiple fields.
+
+## Example
+
+For the inline alert component, both the `heading` and `content` props are required. 
+
+```python
+from deephaven import ui
+
+
+my_inline_alert_basic = ui.inline_alert(
+    heading="Payment Information",
+    content="Enter your billing address, shipping address, and payment method to complete your purchase.",
+)
+```
+
+
+## Variant
+
+The `variant` prop can set a variant to give inline alerts a semantic meaning.
+
+```python
+from deephaven import ui
+
+
+@ui.component
+def ui_inline_alert_variant_examples():
+    return [
+        ui.inline_alert(
+            heading="Accepted Payment Methods",
+            content="Only major credit cards are accepted for payment. Direct debit is currently unavailable.",
+            variant="info",
+        ),
+        ui.inline_alert(
+            heading="Purchase completed",
+            content="You'll get a confirmation email with your order details shortly.",
+            variant="positive",
+        ),
+        ui.inline_alert(
+            heading="Payment Information",
+            content="Enter your billing address, shipping address, and payment method to complete your purchase.",
+            variant="notice",
+        ),
+        ui.inline_alert(
+            heading="Payment Information",
+            content="Enter your billing address, shipping address, and payment method to complete your purchase.",
+            variant="negative",
+        ),
+    ]
+
+
+my_inline_alert_variant_examples = ui_inline_alert_variant_examples()
+```
+
+## API Reference
+
+```{eval-rst}
+.. dhautofunction:: deephaven.ui.inline_alert
+```
diff --git a/plugins/ui/docs/sidebar.json b/plugins/ui/docs/sidebar.json
index b8ff32ebb..d4ad06877 100644
--- a/plugins/ui/docs/sidebar.json
+++ b/plugins/ui/docs/sidebar.json
@@ -121,6 +121,10 @@
             "label": "image",
             "path": "components/image.md"
           },
+          {
+            "label": "inline_alert",
+            "path": "components/inline_alert.md"
+          },
           {
             "label": "link",
             "path": "components/link.md"
diff --git a/plugins/ui/src/deephaven/ui/components/__init__.py b/plugins/ui/src/deephaven/ui/components/__init__.py
index 6761e71b4..bae21fd51 100644
--- a/plugins/ui/src/deephaven/ui/components/__init__.py
+++ b/plugins/ui/src/deephaven/ui/components/__init__.py
@@ -28,6 +28,7 @@
 from .icon import icon
 from .illustrated_message import illustrated_message
 from .image import image
+from .inline_alert import inline_alert
 from .item import item
 from .item_table_source import item_table_source
 from .link import link
@@ -97,6 +98,7 @@
     "item_table_source",
     "illustrated_message",
     "image",
+    "inline_alert",
     "link",
     "list_view",
     "list_action_group",
diff --git a/plugins/ui/src/deephaven/ui/components/inline_alert.py b/plugins/ui/src/deephaven/ui/components/inline_alert.py
new file mode 100644
index 000000000..4ec01483e
--- /dev/null
+++ b/plugins/ui/src/deephaven/ui/components/inline_alert.py
@@ -0,0 +1,159 @@
+from __future__ import annotations
+from typing import Any
+from .types import (
+    AlignSelf,
+    CSSProperties,
+    DimensionValue,
+    JustifySelf,
+    LayoutFlex,
+    Position,
+)
+from ..types import InlineAlertVariant
+from .basic import component_element
+from ..elements import Element
+
+
+def inline_alert(
+    heading: Any,
+    content: Any,
+    *,
+    variant: InlineAlertVariant | None = "neutral",
+    auto_focus: bool | None = None,
+    flex: LayoutFlex | None = None,
+    flex_grow: float | None = None,
+    flex_shrink: float | None = None,
+    flex_basis: DimensionValue | None = None,
+    align_self: AlignSelf | None = None,
+    justify_self: JustifySelf | None = None,
+    order: int | None = None,
+    grid_area: str | None = None,
+    grid_row: str | None = None,
+    grid_row_start: str | None = None,
+    grid_row_end: str | None = None,
+    grid_column: str | None = None,
+    grid_column_start: str | None = None,
+    grid_column_end: str | None = None,
+    margin: DimensionValue | None = None,
+    margin_top: DimensionValue | None = None,
+    margin_bottom: DimensionValue | None = None,
+    margin_start: DimensionValue | None = None,
+    margin_end: DimensionValue | None = None,
+    margin_x: DimensionValue | None = None,
+    margin_y: DimensionValue | None = None,
+    width: DimensionValue | None = None,
+    height: DimensionValue | None = None,
+    min_width: DimensionValue | None = None,
+    min_height: DimensionValue | None = None,
+    max_width: DimensionValue | None = None,
+    max_height: DimensionValue | None = None,
+    position: Position | None = None,
+    top: DimensionValue | None = None,
+    bottom: DimensionValue | None = None,
+    start: DimensionValue | None = None,
+    end: DimensionValue | None = None,
+    left: DimensionValue | None = None,
+    right: DimensionValue | None = None,
+    z_index: int | None = None,
+    is_hidden: bool | None = None,
+    id: str | None = None,
+    UNSAFE_class_name: str | None = None,
+    UNSAFE_style: CSSProperties | None = None,
+) -> Element:
+    """
+    Inline alerts display non-modal messages related to objects in a view.
+
+    Args:
+        heading: The heading of the Inline Alert.
+        content: The content of the Inline Alert.
+        variant: The visual style of the Inline Alert.
+        auto_focus: Whether to automatically focus the Inline Alert when it first renders.
+        flex: When used in a flex layout, specifies how the element will grow or shrink to fit the space available.
+        flex_grow: When used in a flex layout, specifies how the element will grow to fit the space available.
+        flex_shrink: When used in a flex layout, specifies how the element will shrink to fit the space available.
+        flex_basis: When used in a flex layout, specifies the initial main size of the element.
+        align_self: Overrides the alignItems property of a flex or grid container.
+        justify_self: Species how the element is justified inside a flex or grid container.
+        order: The layout order for the element within a flex or grid container.
+        grid_area: When used in a grid layout specifies, specifies the named grid area that the element should be placed in within the grid.
+        grid_row: When used in a grid layout, specifies the row the element should be placed in within the grid.
+        grid_column: When used in a grid layout, specifies the column the element should be placed in within the grid.
+        grid_row_start: When used in a grid layout, specifies the starting row to span within the grid.
+        grid_row_end: When used in a grid layout, specifies the ending row to span within the grid.
+        grid_column_start: When used in a grid layout, specifies the starting column to span within the grid.
+        grid_column_end: When used in a grid layout, specifies the ending column to span within the grid.
+        margin: The margin for all four sides of the element.
+        margin_top: The margin for the top side of the element.
+        margin_bottom: The margin for the bottom side of the element.
+        margin_start: The margin for the logical start side of the element, depending on layout direction.
+        margin_end: The margin for the logical end side of the element, depending on layout direction.
+        margin_x: The margin for the left and right sides of the element.
+        margin_y: The margin for the top and bottom sides of the element.
+        width: The width of the element.
+        min_width: The minimum width of the element.
+        max_width: The maximum width of the element.
+        height: The height of the element.
+        min_height: The minimum height of the element.
+        max_height: The maximum height of the element.
+        position: The position of the element.
+        top: The distance from the top of the containing element.
+        bottom: The distance from the bottom of the containing element.
+        left: The distance from the left of the containing element.
+        right: The distance from the right of the containing element.
+        start: The distance from the start of the containing element, depending on layout direction.
+        end: The distance from the end of the containing element, depending on layout direction.
+        z_index: The stack order of the element.
+        is_hidden: Whether the element is hidden.
+        id: The unique identifier of the element.
+        UNSAFE_class_name: A CSS class to apply to the element.
+        UNSAFE_style: A CSS style to apply to the element.
+
+    Returns:
+        The rendered inline alert element.
+
+    """
+    return component_element(
+        "InlineAlert",
+        heading=heading,
+        content=content,
+        variant=variant,
+        auto_focus=auto_focus,
+        flex=flex,
+        flex_grow=flex_grow,
+        flex_shrink=flex_shrink,
+        flex_basis=flex_basis,
+        align_self=align_self,
+        justify_self=justify_self,
+        order=order,
+        grid_area=grid_area,
+        grid_row=grid_row,
+        grid_row_start=grid_row_start,
+        grid_row_end=grid_row_end,
+        grid_column=grid_column,
+        grid_column_start=grid_column_start,
+        grid_column_end=grid_column_end,
+        margin=margin,
+        margin_top=margin_top,
+        margin_bottom=margin_bottom,
+        margin_start=margin_start,
+        margin_end=margin_end,
+        margin_x=margin_x,
+        margin_y=margin_y,
+        width=width,
+        height=height,
+        min_width=min_width,
+        min_height=min_height,
+        max_width=max_width,
+        max_height=max_height,
+        position=position,
+        top=top,
+        bottom=bottom,
+        start=start,
+        end=end,
+        left=left,
+        right=right,
+        z_index=z_index,
+        is_hidden=is_hidden,
+        id=id,
+        UNSAFE_class_name=UNSAFE_class_name,
+        UNSAFE_style=UNSAFE_style,
+    )
diff --git a/plugins/ui/src/deephaven/ui/types/types.py b/plugins/ui/src/deephaven/ui/types/types.py
index 1b8b9bd15..c8eeee38c 100644
--- a/plugins/ui/src/deephaven/ui/types/types.py
+++ b/plugins/ui/src/deephaven/ui/types/types.py
@@ -506,6 +506,7 @@ class SliderChange(TypedDict):
 ListViewOverflowMode = Literal["truncate", "wrap"]
 ActionGroupDensity = Literal["compact", "regular"]
 TabDensity = Literal["compact", "regular"]
+InlineAlertVariant = Literal["neutral", "info", "positive", "notice", "negative"]
 LinkVariant = Literal["primary", "secondary", "over_background"]
 BadgeVariant = Literal[
     "neutral",
diff --git a/plugins/ui/src/js/src/elements/InlineAlert.tsx b/plugins/ui/src/js/src/elements/InlineAlert.tsx
new file mode 100644
index 000000000..282e0b39d
--- /dev/null
+++ b/plugins/ui/src/js/src/elements/InlineAlert.tsx
@@ -0,0 +1,41 @@
+import React, { ReactNode } from 'react';
+import {
+  Content,
+  InlineAlert as DHCInlineAlert,
+  InlineAlertProps as DHCInlineAlertProps,
+  Heading,
+} from '@deephaven/components';
+import { isElementOfType } from '@deephaven/react-hooks';
+
+export type SerializedInlineAlertProps = Omit<
+  DHCInlineAlertProps,
+  'children'
+> & {
+  heading: ReactNode;
+  content: ReactNode;
+};
+
+export function InlineAlert(props: SerializedInlineAlertProps): JSX.Element {
+  const { heading, content, ...otherProps } = props;
+
+  return (
+    /* eslint-disable-next-line react/jsx-props-no-spreading */
+    <DHCInlineAlert {...otherProps}>
+      {heading != null &&
+        (isElementOfType(heading, Heading) ? (
+          heading
+        ) : (
+          <Heading>{heading}</Heading>
+        ))}
+      {content != null &&
+        (isElementOfType(content, Content) ? (
+          content
+        ) : (
+          <Content>{content}</Content>
+        ))}
+    </DHCInlineAlert>
+  );
+}
+
+InlineAlert.displayName = 'InlineAlert';
+export default InlineAlert;
diff --git a/plugins/ui/src/js/src/elements/index.ts b/plugins/ui/src/js/src/elements/index.ts
index 9f31738dc..173feb403 100644
--- a/plugins/ui/src/js/src/elements/index.ts
+++ b/plugins/ui/src/js/src/elements/index.ts
@@ -17,6 +17,7 @@ export * from './HTMLElementView';
 export * from './IconElementView';
 export * from './IllustratedMessage';
 export * from './Image';
+export * from './InlineAlert';
 export * from './ListView';
 export * from './Markdown';
 export * from './model';
diff --git a/plugins/ui/src/js/src/elements/model/ElementConstants.ts b/plugins/ui/src/js/src/elements/model/ElementConstants.ts
index 9aa6cd7bf..5815837c2 100644
--- a/plugins/ui/src/js/src/elements/model/ElementConstants.ts
+++ b/plugins/ui/src/js/src/elements/model/ElementConstants.ts
@@ -47,6 +47,7 @@ export const ELEMENT_NAME = {
   heading: uiComponentName('Heading'),
   illustratedMessage: uiComponentName('IllustratedMessage'),
   image: uiComponentName('Image'),
+  inlineAlert: uiComponentName('InlineAlert'),
   item: uiComponentName('Item'),
   listActionGroup: uiComponentName('ListActionGroup'),
   listActionMenu: uiComponentName('ListActionMenu'),
diff --git a/plugins/ui/src/js/src/widget/WidgetUtils.tsx b/plugins/ui/src/js/src/widget/WidgetUtils.tsx
index e29d3c1ea..e99f9e6a3 100644
--- a/plugins/ui/src/js/src/widget/WidgetUtils.tsx
+++ b/plugins/ui/src/js/src/widget/WidgetUtils.tsx
@@ -63,6 +63,7 @@ import {
   Grid,
   IllustratedMessage,
   Image,
+  InlineAlert,
   ListView,
   Markdown,
   Picker,
@@ -133,6 +134,7 @@ export const elementComponentMap = {
   [ELEMENT_NAME.heading]: Heading,
   [ELEMENT_NAME.illustratedMessage]: IllustratedMessage,
   [ELEMENT_NAME.image]: Image,
+  [ELEMENT_NAME.inlineAlert]: InlineAlert,
   [ELEMENT_NAME.item]: Item,
   [ELEMENT_NAME.link]: Link,
   [ELEMENT_NAME.listActionGroup]: ListActionGroup,


Test patch:
diff --git a/tests/app.d/ui_render_all.py b/tests/app.d/ui_render_all.py
index 99cdba25f..3b96ae6e4 100644
--- a/tests/app.d/ui_render_all.py
+++ b/tests/app.d/ui_render_all.py
@@ -48,6 +48,7 @@ def ui_components1():
         ui.action_button("Action Button"),
         ui.action_group("Aaa", "Bbb", "Ccc"),
         ui.action_menu("Aaa", "Bbb", "Ccc"),
+        ui.badge("Licensed", variant="positive"),
         ui.button_group(ui.button("One"), ui.button("Two")),
         ui.button("Button"),
         ui.calendar(value="2021-01-01"),
@@ -79,6 +80,14 @@ def ui_components2():
             ui.heading("Warning"),
             ui.content("This is a warning message."),
         ),
+        ui.inline_alert(
+            ui.heading("Purchase completed"),
+            ui.content(
+                "You'll get a confirmation email with your order details shortly."
+            ),
+            variant="positive",
+        ),
+        ui.link("Learn more about Deephaven", href="https://deephaven.io/"),
         ui.list_view(
             _item_table_source_with_action_group,
             aria_label="List View - List action group",
diff --git a/tests/ui.spec.ts-snapshots/UI-all-components-render-1-1-chromium-linux.png b/tests/ui.spec.ts-snapshots/UI-all-components-render-1-1-chromium-linux.png
index b25c82740..01eeab591 100644
Binary files a/tests/ui.spec.ts-snapshots/UI-all-components-render-1-1-chromium-linux.png and b/tests/ui.spec.ts-snapshots/UI-all-components-render-1-1-chromium-linux.png differ
diff --git a/tests/ui.spec.ts-snapshots/UI-all-components-render-1-1-firefox-linux.png b/tests/ui.spec.ts-snapshots/UI-all-components-render-1-1-firefox-linux.png
index 28d10f4f3..2b2ca3238 100644
Binary files a/tests/ui.spec.ts-snapshots/UI-all-components-render-1-1-firefox-linux.png and b/tests/ui.spec.ts-snapshots/UI-all-components-render-1-1-firefox-linux.png differ
diff --git a/tests/ui.spec.ts-snapshots/UI-all-components-render-1-1-webkit-linux.png b/tests/ui.spec.ts-snapshots/UI-all-components-render-1-1-webkit-linux.png
index e73a064b0..50e60dbb2 100644
Binary files a/tests/ui.spec.ts-snapshots/UI-all-components-render-1-1-webkit-linux.png and b/tests/ui.spec.ts-snapshots/UI-all-components-render-1-1-webkit-linux.png differ
diff --git a/tests/ui.spec.ts-snapshots/UI-all-components-render-2-1-chromium-linux.png b/tests/ui.spec.ts-snapshots/UI-all-components-render-2-1-chromium-linux.png
index 47e150d27..92492a7d7 100644
Binary files a/tests/ui.spec.ts-snapshots/UI-all-components-render-2-1-chromium-linux.png and b/tests/ui.spec.ts-snapshots/UI-all-components-render-2-1-chromium-linux.png differ
diff --git a/tests/ui.spec.ts-snapshots/UI-all-components-render-2-1-firefox-linux.png b/tests/ui.spec.ts-snapshots/UI-all-components-render-2-1-firefox-linux.png
index a39d8aff0..55e199088 100644
Binary files a/tests/ui.spec.ts-snapshots/UI-all-components-render-2-1-firefox-linux.png and b/tests/ui.spec.ts-snapshots/UI-all-components-render-2-1-firefox-linux.png differ
diff --git a/tests/ui.spec.ts-snapshots/UI-all-components-render-2-1-webkit-linux.png b/tests/ui.spec.ts-snapshots/UI-all-components-render-2-1-webkit-linux.png
index b408899c5..14da881b1 100644
Binary files a/tests/ui.spec.ts-snapshots/UI-all-components-render-2-1-webkit-linux.png and b/tests/ui.spec.ts-snapshots/UI-all-components-render-2-1-webkit-linux.png differ