|
| 1 | +--- |
| 2 | +slug: /use-cases/observability/clickstack/dashboards/row-click-drilldowns |
| 3 | +title: 'Table row-click drilldowns' |
| 4 | +sidebar_label: 'Row-click drilldowns' |
| 5 | +pagination_prev: null |
| 6 | +pagination_next: null |
| 7 | +description: 'Configure table tile row clicks to drill into Search or another dashboard, carrying the clicked row values through as filters' |
| 8 | +doc_type: 'guide' |
| 9 | +keywords: ['clickstack', 'dashboards', 'drilldown', 'table', 'row click', 'observability'] |
| 10 | +--- |
| 11 | + |
| 12 | +import Image from '@theme/IdealImage'; |
| 13 | + |
| 14 | +A table tile can be configured with a row-click action. When a viewer clicks a row, ClickStack opens either the [Search](/use-cases/observability/clickstack/search) page or another dashboard, carrying the values from the clicked row through as filters. |
| 15 | + |
| 16 | +This is how you build sequenced workflows across dashboards. An overview table with one row per service answers "which service is unhealthy?", and a row click opens a focused search or a per-service dashboard that answers "what is happening inside it?". The viewer moves from the aggregate to the detail in one click, without rebuilding the query by hand. |
| 17 | + |
| 18 | +Row-click actions apply to table tiles only. They are distinct from the chart [drilldown to search](/use-cases/observability/clickstack/dashboards#drilldown-to-search), which opens a context menu when you click a point on a line or bar chart. |
| 19 | + |
| 20 | +## The two actions {#the-two-actions} |
| 21 | + |
| 22 | +A row-click action opens one of two destinations: |
| 23 | + |
| 24 | +- **Search** opens the Search page for a log or trace source. Metric and session sources are not searchable, so they cannot be search targets. |
| 25 | +- **Dashboard** opens another dashboard owned by your team. |
| 26 | + |
| 27 | +For both, you template a set of filters (and, optionally, a global `WHERE` condition) from the clicked row's columns. The rendered filters are applied at the destination, scoping it to the row you clicked. |
| 28 | + |
| 29 | +## Configure a row-click action {#configure} |
| 30 | + |
| 31 | +Row-click actions are configured from the tile editor. There is no separate page for them: you set the action on the table tile itself. |
| 32 | + |
| 33 | +<VerticalStepper headerLevel="h3"> |
| 34 | + |
| 35 | +### Open a table tile for editing {#open-table-tile} |
| 36 | + |
| 37 | +Add a new tile or edit an existing one, and set the visualization type to **Table**. A **Row Click Action** button appears in the editor toolbar, next to **Display Settings**. The button is shown for table tiles only, and its label reflects the current action: `Row Click Action: Default`, `Row Click Action: Search`, or `Row Click Action: Dashboard`. |
| 38 | + |
| 39 | +### Open the row-click action drawer {#open-drawer} |
| 40 | + |
| 41 | +Click **Row Click Action** to open the drawer. A segmented control at the top selects the action: |
| 42 | + |
| 43 | +- **Default** keeps the built-in behavior: clicking a row opens the Search page, filtered by the row's group-by column values and the selected time range. This is the action used when no custom action is set. |
| 44 | +- **Search** and **Dashboard** configure a custom drilldown, described below. |
| 45 | + |
| 46 | +{/* SCREENSHOT: row-click-drawer - Row Click Action drawer in Dashboard mode, populated with the Service Detail target and a ServiceName filter */} |
| 47 | + |
| 48 | +### Choose the destination {#choose-destination} |
| 49 | + |
| 50 | +Select **Search** or **Dashboard**, then choose where the click lands: |
| 51 | + |
| 52 | +- For **Search**, choose a **Source**. Only log and trace sources are listed; metric and session sources are filtered out because the Search page cannot render them. |
| 53 | +- For **Dashboard**, choose a **Dashboard** from your team's dashboards. |
| 54 | +- In either mode you can instead choose **Template** and enter a [Handlebars](https://handlebarsjs.com/) template that is matched by name to an available source or dashboard. Use a constant name to always resolve the same target (for example `Service Detail`), or reference a row column to pick the target per row (for example `Errors-{{ServiceName}}`). |
| 55 | + |
| 56 | +### Add filters {#add-filters} |
| 57 | + |
| 58 | +Under **Filters**, click **Add filter** and provide: |
| 59 | + |
| 60 | +- an **Expression**: a column or expression on the destination, for example `ServiceName`. |
| 61 | +- a **Template**: a Handlebars template for its value, for example `{{ServiceName}}`. |
| 62 | + |
| 63 | +Templates reference the clicked row's columns by name with `{{columnName}}`. When the row is clicked, each filter renders to an `expression IN (value)` condition on the destination. Filters that share an expression are merged into a single condition. |
| 64 | + |
| 65 | +When you choose a destination dashboard, ClickStack pre-fills one empty filter for each filter that dashboard already declares, so you only need to fill in the templates. |
| 66 | + |
| 67 | +### Set a global WHERE condition (optional) {#set-where} |
| 68 | + |
| 69 | +The **WHERE** field takes a Handlebars template rendered into the destination's global filter, in addition to the per-filter conditions above. Set the query language to SQL or Lucene so the destination parses it correctly. For example, an SQL template `ServiceName = '{{ServiceName}}'` scopes the destination to the clicked row's service. |
| 70 | + |
| 71 | +### Apply {#apply} |
| 72 | + |
| 73 | +Click **Apply**. ClickStack validates the templates and reports any with invalid syntax. Save the dashboard to persist the action. |
| 74 | + |
| 75 | +</VerticalStepper> |
| 76 | + |
| 77 | +## How the destination and filters resolve {#how-it-resolves} |
| 78 | + |
| 79 | +When a viewer clicks a row, ClickStack resolves the action against that row: |
| 80 | + |
| 81 | +- **Destination.** Choosing a specific source or dashboard pins it by ID. A **Template** target renders against the clicked row and is then matched by name against your team's sources or dashboards. A name must be unique to resolve: if two sources or two dashboards share the rendered name, the link cannot resolve to one of them. An empty rendered name, or a name with no match, also fails to resolve. |
| 82 | +- **Filters.** Each filter template renders against the row and becomes an `expression IN (value)` condition at the destination. A `Search` action opens `/search` scoped to the chosen source; a `Dashboard` action opens that dashboard. The clicked row's time range carries over in both cases. |
| 83 | + |
| 84 | +:::note Dashboard targets need a matching filter declared |
| 85 | +A filter carried into a dashboard only takes effect if the destination dashboard declares a top-level [custom filter](/use-cases/observability/clickstack/dashboards#custom-filters) whose expression matches the filter's **Expression**. If no declared filter matches, that value is dropped at click time and the destination opens unfiltered for that expression. This is why the dashboard mode pre-fills the destination's declared filters: match the expression, and the destination dropdown auto-populates with the clicked row's value. |
| 86 | +::: |
| 87 | + |
| 88 | +## Example: a service inventory that drills into a service detail dashboard {#example} |
| 89 | + |
| 90 | +A common pairing is a service inventory (one row per service) that drills into a per-service detail dashboard. Clicking a service row opens the detail dashboard scoped to that service. |
| 91 | + |
| 92 | +<VerticalStepper headerLevel="h3"> |
| 93 | + |
| 94 | +### Build the detail dashboard {#example-detail} |
| 95 | + |
| 96 | +Create a dashboard named `Service Detail` and add a [custom filter](/use-cases/observability/clickstack/dashboards#custom-filters) with the expression `ServiceName` on your traces source. The dashboard-level filter re-scopes every tile to a single service, so the tiles themselves do not hardcode a service in their queries. Add the per-service tiles you want, such as request and error counts, latency percentiles, a span-duration heatmap, and top error messages. |
| 97 | + |
| 98 | +Save this dashboard first. A template target is resolved by name at click time, so the detail dashboard must exist when a viewer clicks. If you later rename it, update the row-click action to match. |
| 99 | + |
| 100 | +### Build the inventory table {#example-inventory} |
| 101 | + |
| 102 | +On an overview dashboard, add a **Table** tile on your traces source grouped by `ServiceName`, with the columns you want per service. For example: |
| 103 | + |
| 104 | +- `Requests`: count of spans. |
| 105 | +- `Errors`: count of spans where `StatusCode` is `STATUS_CODE_ERROR`. |
| 106 | +- `P95 Duration`: the 95th percentile of `Duration`. |
| 107 | + |
| 108 | +Order by `Requests` descending so the busiest services sort to the top. |
| 109 | + |
| 110 | +### Wire up the row click {#example-wire} |
| 111 | + |
| 112 | +On the table tile, open **Row Click Action**, select **Dashboard**, and choose **Template** with the value `Service Detail`. Add one filter: |
| 113 | + |
| 114 | +- **Expression**: `ServiceName` |
| 115 | +- **Template**: `{{ServiceName}}` |
| 116 | + |
| 117 | +Click **Apply** and save. The expression `ServiceName` matches the custom filter declared on the `Service Detail` dashboard, so the clicked row's service flows straight into that filter. |
| 118 | + |
| 119 | +### Click a row {#example-click} |
| 120 | + |
| 121 | +Hovering a row reveals a link affordance on the right edge of the table, with a hint describing the action (`Open dashboard "Service Detail"`). Clicking the row opens the `Service Detail` dashboard scoped to that service, with the `ServiceName` filter set to the clicked value. |
| 122 | + |
| 123 | +{/* SCREENSHOT: row-click-drilldown - Service Detail dashboard opened after a row click, with the ServiceName filter populated to the clicked service */} |
| 124 | + |
| 125 | +</VerticalStepper> |
| 126 | + |
| 127 | +To drill into Search instead, set the action to **Search**, choose your traces source, and add the same `ServiceName` filter with template `{{ServiceName}}`. Clicking a row then opens the Search page scoped to that source and filtered to the row's service. |
| 128 | + |
| 129 | +## Validation and limitations {#validation} |
| 130 | + |
| 131 | +- **Table tiles only.** The **Row Click Action** button appears for table tiles, both the chart-builder table and a [SQL-based](/use-cases/observability/clickstack/dashboards/sql-visualizations) table. Other tile types do not have a row-click action. |
| 132 | +- **Search targets must be log or trace sources.** Metric and session sources are not offered, and are rejected if set through the API. |
| 133 | +- **Template names must be unique.** A template target resolves by name, so two sources or two dashboards with the same name cannot be used as a template target. |
| 134 | +- **Dashboard targets need a matching declared filter** for a carried value to take effect (see the note above). |
| 135 | +- **Map-attribute group-by needs a SQL alias.** A chart-builder group-by on a map attribute such as `SpanAttributes['http.route']` produces a result column whose name is the raw expression, which cannot be referenced as `{{...}}` in a template. To drill down from such a column, author the tile as a [SQL-based](/use-cases/observability/clickstack/dashboards/sql-visualizations) table and give the column an explicit `AS` alias, then reference that alias in the template. A group-by on a plain column such as `ServiceName` needs no alias. |
0 commit comments