feat: add tooltip field to workspace app that renders as markdown (#19651)

In this pull request we're adding an optional `tooltip` field. The
`tooltip` field is a string field (with markdown support) that will be
used to display tooltips on hover over app buttons in a workspace
dashboard.

Tooltip screenshot

<img width="816" height="275" alt="Screenshot 2025-08-29 at 4 11 56 PM"
src="https://github.com/user-attachments/assets/52c736a1-f632-465b-89a0-35ca99bd367b"
/>

Tooltip video


https://github.com/user-attachments/assets/21806337-accc-4acf-b8c6-450c031d98f1

Issue: https://github.com/coder/coder/issues/18431
Related provider PR:
https://github.com/coder/terraform-provider-coder/pull/435

### Changes

- Added migration to add `tooltip` column to `workspace_apps` table
- Updated queries to get/set the new `tooltip` column
- Updated frontend to render tooltip as markdown (primary tool tip takes
precedence over template tooltip)

### Testing

- Added storybook test for `Applink` markdown rendering
This commit is contained in:
Rafael Rodriguez
2025-09-10 11:01:54 -05:00
committed by GitHub
parent 4cd0ada0bb
commit e53bc247e9
29 changed files with 495 additions and 388 deletions
+1
View File
@@ -562,6 +562,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaceagents/{workspaceagent} \
],
"subdomain": true,
"subdomain_name": "string",
"tooltip": "string",
"url": "string"
}
],
+8
View File
@@ -122,6 +122,7 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam
],
"subdomain": true,
"subdomain_name": "string",
"tooltip": "string",
"url": "string"
}
],
@@ -361,6 +362,7 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild} \
],
"subdomain": true,
"subdomain_name": "string",
"tooltip": "string",
"url": "string"
}
],
@@ -718,6 +720,7 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/res
],
"subdomain": true,
"subdomain_name": "string",
"tooltip": "string",
"url": "string"
}
],
@@ -861,6 +864,7 @@ Status Code **200**
| `»»»» workspace_id` | string(uuid) | false | | |
| `»»» subdomain` | boolean | false | | Subdomain denotes whether the app should be accessed via a path on the `coder server` or via a hostname-based dev URL. If this is set to true and there is no app wildcard configured on the server, the app will not be accessible in the UI. |
| `»»» subdomain_name` | string | false | | Subdomain name is the application domain exposed on the `coder server`. |
| `»»» tooltip` | string | false | | Tooltip is an optional markdown supported field that is displayed when hovering over workspace apps in the UI. |
| `»»» url` | string | false | | URL is the address being proxied to inside the workspace. If external is specified, this will be opened on the client. |
| `»» architecture` | string | false | | |
| `»» connection_timeout_seconds` | integer | false | | |
@@ -1089,6 +1093,7 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/sta
],
"subdomain": true,
"subdomain_name": "string",
"tooltip": "string",
"url": "string"
}
],
@@ -1401,6 +1406,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace}/builds \
],
"subdomain": true,
"subdomain_name": "string",
"tooltip": "string",
"url": "string"
}
],
@@ -1606,6 +1612,7 @@ Status Code **200**
| `»»»»» workspace_id` | string(uuid) | false | | |
| `»»»» subdomain` | boolean | false | | Subdomain denotes whether the app should be accessed via a path on the `coder server` or via a hostname-based dev URL. If this is set to true and there is no app wildcard configured on the server, the app will not be accessible in the UI. |
| `»»»» subdomain_name` | string | false | | Subdomain name is the application domain exposed on the `coder server`. |
| `»»»» tooltip` | string | false | | Tooltip is an optional markdown supported field that is displayed when hovering over workspace apps in the UI. |
| `»»»» url` | string | false | | URL is the address being proxied to inside the workspace. If external is specified, this will be opened on the client. |
| `»»» architecture` | string | false | | |
| `»»» connection_timeout_seconds` | integer | false | | |
@@ -1896,6 +1903,7 @@ curl -X POST http://coder-server:8080/api/v2/workspaces/{workspace}/builds \
],
"subdomain": true,
"subdomain_name": "string",
"tooltip": "string",
"url": "string"
}
],
+7
View File
@@ -8985,6 +8985,7 @@ If the schedule is empty, the user will be updated to use the default schedule.|
],
"subdomain": true,
"subdomain_name": "string",
"tooltip": "string",
"url": "string"
}
],
@@ -9249,6 +9250,7 @@ If the schedule is empty, the user will be updated to use the default schedule.|
],
"subdomain": true,
"subdomain_name": "string",
"tooltip": "string",
"url": "string"
}
],
@@ -9917,6 +9919,7 @@ If the schedule is empty, the user will be updated to use the default schedule.|
],
"subdomain": true,
"subdomain_name": "string",
"tooltip": "string",
"url": "string"
}
```
@@ -9940,6 +9943,7 @@ If the schedule is empty, the user will be updated to use the default schedule.|
| `statuses` | array of [codersdk.WorkspaceAppStatus](#codersdkworkspaceappstatus) | false | | Statuses is a list of statuses for the app. |
| `subdomain` | boolean | false | | Subdomain denotes whether the app should be accessed via a path on the `coder server` or via a hostname-based dev URL. If this is set to true and there is no app wildcard configured on the server, the app will not be accessible in the UI. |
| `subdomain_name` | string | false | | Subdomain name is the application domain exposed on the `coder server`. |
| `tooltip` | string | false | | Tooltip is an optional markdown supported field that is displayed when hovering over workspace apps in the UI. |
| `url` | string | false | | URL is the address being proxied to inside the workspace. If external is specified, this will be opened on the client. |
#### Enumerated Values
@@ -10148,6 +10152,7 @@ If the schedule is empty, the user will be updated to use the default schedule.|
],
"subdomain": true,
"subdomain_name": "string",
"tooltip": "string",
"url": "string"
}
],
@@ -10629,6 +10634,7 @@ If the schedule is empty, the user will be updated to use the default schedule.|
],
"subdomain": true,
"subdomain_name": "string",
"tooltip": "string",
"url": "string"
}
],
@@ -10965,6 +10971,7 @@ If the schedule is empty, the user will be updated to use the default schedule.|
"statuses": [],
"subdomain": true,
"subdomain_name": "string",
"tooltip": "string",
"url": "string"
}
],
+4
View File
@@ -2404,6 +2404,7 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/d
],
"subdomain": true,
"subdomain_name": "string",
"tooltip": "string",
"url": "string"
}
],
@@ -2547,6 +2548,7 @@ Status Code **200**
| `»»»» workspace_id` | string(uuid) | false | | |
| `»»» subdomain` | boolean | false | | Subdomain denotes whether the app should be accessed via a path on the `coder server` or via a hostname-based dev URL. If this is set to true and there is no app wildcard configured on the server, the app will not be accessible in the UI. |
| `»»» subdomain_name` | string | false | | Subdomain name is the application domain exposed on the `coder server`. |
| `»»» tooltip` | string | false | | Tooltip is an optional markdown supported field that is displayed when hovering over workspace apps in the UI. |
| `»»» url` | string | false | | URL is the address being proxied to inside the workspace. If external is specified, this will be opened on the client. |
| `»» architecture` | string | false | | |
| `»» connection_timeout_seconds` | integer | false | | |
@@ -3090,6 +3092,7 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/r
],
"subdomain": true,
"subdomain_name": "string",
"tooltip": "string",
"url": "string"
}
],
@@ -3233,6 +3236,7 @@ Status Code **200**
| `»»»» workspace_id` | string(uuid) | false | | |
| `»»» subdomain` | boolean | false | | Subdomain denotes whether the app should be accessed via a path on the `coder server` or via a hostname-based dev URL. If this is set to true and there is no app wildcard configured on the server, the app will not be accessible in the UI. |
| `»»» subdomain_name` | string | false | | Subdomain name is the application domain exposed on the `coder server`. |
| `»»» tooltip` | string | false | | Tooltip is an optional markdown supported field that is displayed when hovering over workspace apps in the UI. |
| `»»» url` | string | false | | URL is the address being proxied to inside the workspace. If external is specified, this will be opened on the client. |
| `»» architecture` | string | false | | |
| `»» connection_timeout_seconds` | integer | false | | |
+6
View File
@@ -177,6 +177,7 @@ of the template will be used.
],
"subdomain": true,
"subdomain_name": "string",
"tooltip": "string",
"url": "string"
}
],
@@ -466,6 +467,7 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam
],
"subdomain": true,
"subdomain_name": "string",
"tooltip": "string",
"url": "string"
}
],
@@ -780,6 +782,7 @@ of the template will be used.
],
"subdomain": true,
"subdomain_name": "string",
"tooltip": "string",
"url": "string"
}
],
@@ -1055,6 +1058,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaces \
"statuses": [],
"subdomain": true,
"subdomain_name": "string",
"tooltip": "string",
"url": "string"
}
],
@@ -1345,6 +1349,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace} \
],
"subdomain": true,
"subdomain_name": "string",
"tooltip": "string",
"url": "string"
}
],
@@ -1867,6 +1872,7 @@ curl -X PUT http://coder-server:8080/api/v2/workspaces/{workspace}/dormant \
],
"subdomain": true,
"subdomain_name": "string",
"tooltip": "string",
"url": "string"
}
],