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 125ff7147..d914518af 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 */ + + {heading != null && + (isElementOfType(heading, Heading) ? ( + heading + ) : ( + {heading} + ))} + {content != null && + (isElementOfType(content, Content) ? ( + content + ) : ( + {content} + ))} + + ); +} + +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 014a1c6e4..84a57000a 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, @@ -136,6 +137,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, 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