ConditionViewer

GithubStorybook

The ConditionViewer components render a read-only summary of a set of rules — one boxed sentence per rule, nested under the operator that combines them.

Importing

import {
	ConditionViewerItem,
	ConditionViewerGroup,
	ConditionViewerTerm,
	ConditionViewerDetailList,
	ConditionViewerDetail,
} from '@customerio/pluma-components/react';

Usage

Give each ConditionViewerItem a marker with icon and a sentence as its children. Wrap the parts that carry meaning in ConditionViewerTerm and leave the connecting words plain — the emphasis is what a reader scans for.

Loading editor

A sentence does not have to start with a field — put the emphasis wherever the meaning is.

Loading editor

Grouping

ConditionViewerGroup combines conditions under one operator. Its label is the descriptor; Pluma lays it out so an inline Badge lines up with the words either side of it, but the wording is entirely yours. The outermost group has no surface or border of its own — pass isNested to every group below it.

Loading editor

isCollapsible renders the toggle and manages the open state. Pass isOpen and onToggle instead if the state lives in your application; defaultOpen seeds the uncontrolled case and defaults to open.

Sub-conditions

When one condition is refined by others, put a ConditionViewerDetailList in its details.

Write the lead-in ("and matches all:") at the end of the item's own sentence, so it reads as one phrase on one line with the chips below it. Don't put it on the list's label for a single list — that would push it onto a line of its own and break the sentence in two.

Loading editor

label is for the second and subsequent lists, where an item is refined by more than one differently-described set and only the first lead-in can live in the sentence. Those labels necessarily start their own line, because the chips above them are a block.

Loading editor

Conditional slots in Ember

Glimmer won't let a named block be wrapped in {{#if}}, and writing the block unconditionally isn't the same thing — has-block is true even for a block that renders nothing, so an empty <:actions> still produces its container. Rather than spelling out one invocation per combination, write the blocks once and drive them with hasIconBlock, hasDetailsBlock, hasActionsBlock (and hasLabelBlock on a group or detail list).

Each is a three-state override: leave it undefined and the slot renders whenever there's something in it; set it explicitly and it wins. React reads the same props, so a shared wrapper behaves identically in both.

<PlumaConditionViewerItem
	@icon="trigger-event"
	@hasDetailsBlock={{this.hasFilters}}
	@hasActionsBlock={{this.isUnresolvable}}
>
	<:default><PlumaConditionViewerTerm>{{this.eventName}}</PlumaConditionViewerTerm> performed</:default>
	<:details>…</:details>
	<:actions>…</:actions>
</PlumaConditionViewerItem>

Values with more detail behind them

A value can carry a tooltip — a formatted date whose full timestamp and timezone only matter on demand, a truncated list of options, an entity whose full name is too long for the sentence. A chip's overflow stays visible, so a tooltip anchored inside one is not clipped by it.

Mark the trigger with isUnderlined so a reader can tell there is more to see; FormattedDateTime exposes the same thing as isTooltipTriggerUnderlined. Wrap the value in a ConditionViewerTerm either way, so it keeps the emphasis every other value has.

Loading editor

The same applies to a value in the item's own sentence — nothing about the sentence stops a term being a tooltip trigger.

Flagging a condition

actions renders at the trailing edge, aligned with the marker. Use it for what sits beside a condition rather than inside its sentence — an unresolvable reference, a cost indicator. iconColorVariant recolors the marker for the same purpose; leave it alone otherwise, so a set of conditions reads as one.

Loading editor

Inline conditions

By default a condition fills the available width. isInline makes it hug its content instead, for a condition shown alongside other content rather than as a list.

Loading editor

API

Content rendered at the trailing edge of the condition — a warning tooltip, a cost indicator. Aligned with the marker, not centered, so it stays put as the sentence wraps.

Content rendered below the sentence, for the sub-conditions a single condition is refined by. Holds one or more ConditionViewerDetailLists.

Forces the actions area on or off regardless of what's passed. See hasIconBlock.

Forces the details area on or off regardless of what's passed. See hasIconBlock.

Forces the marker on or off regardless of what's passed. Leave it undefined and the marker renders whenever there's something to put in it.

Mostly for Ember, where a named block is either written or not: rather than spelling out one invocation per combination, write the block once and drive it from here.

The icon rendered in the condition's marker, naming the kind of thing the condition is about.

Default:'purple'

The marker's color. Use it to signal that a condition needs attention (for example an unresolvable reference); leave it alone otherwise so conditions read as one set.

Arbitrary content rendered inside the marker, for the rare marker that is neither an icon nor an image. Takes precedence over icon and iconSrc.

An image source rendered in the marker instead of an icon, for markers that can't be expressed as a Pluma icon.

Default:false

Whether the condition hugs its content instead of filling the available width. Use it for a condition rendered alongside other content rather than as part of a full-width list.

Content rendered at the trailing edge of the header row — typically the way into editing the group.

Default:true

Whether a collapsible group starts open. Ignored when isOpen is provided (controlled mode).

Forces the header's actions area on or off regardless of what's passed. See hasLabelBlock.

Forces the descriptor on or off regardless of what's passed. Leave it undefined and it renders whenever there's something to put in it. Mostly for Ember, where a named block is either written or not.

Default:false

Whether Pluma renders a collapse toggle before the label. It controls the toggle only — a group can still be driven open and closed through isOpen from a control of your own without it.

Default:false

Whether the group hugs its content instead of filling the available width.

Default:false

Whether the group is nested inside another group. Adds the surface, border and padding that separate a nested group from its siblings; the outermost group should leave this off so it has no surface or border of its own.

Controls whether the group is open. When set, the group is controlled: its open state is driven entirely by this prop and the consumer is responsible for updating it in response to onToggle (or from a control of their own). Honored with or without isCollapsible. Leave undefined for uncontrolled use.

The descriptor for how the group's conditions combine — for example Matches <Badge>all</Badge> of 4 conditions. Laid out so an inline Badge lines up with the words either side of it. When set, it also names the group for assistive technology and the collapse toggle.

Called whenever the group is opened or closed, with the new state and the event that triggered the toggle.

Forces the label on or off regardless of what's passed. Leave it undefined and it renders whenever there's something to put in it. Mostly for Ember, where a named block is either written or not.

The lead-in above the chips — for example and matches all relationship attributes:. Inherits the sentence's typography so it reads as a continuation of it.

Only for the second and subsequent lists on an item. A single list's lead-in belongs at the end of the item's own sentence, where it reads as one phrase on one line; setting it here would put it on a line of its own and break the sentence in two.