Skip to content

Commit 0e2d560

Browse files
[FIX][UI]: Make "+N more" badges clickable to expand full list in server details modal (#3511)
* fix(ui): make '+N more' badges clickable to expand full list in server details modal In the Server Details modal, the '+N more' summary badges for tools, resources, and prompts were static text. Clicking them now expands the compact view inline, replacing it with the full list for that section. Closes #3509 Signed-off-by: NAYANA.R <nayana.r7813@gmail.com> Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * test(ui): add differential tests for server detail badge click-to-expand 19 Vitest/JSDOM tests covering the new '+N more' badge expansion: - Tools, resources, and prompts badges render with correct count - cursor-pointer class present on all badges - Clicking a badge expands to show the full inline list - Expanded items show display names from window mappings with ID fallback - Sections with <= maxToShow items do not render a badge - Clicking one section badge does not affect other sections Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> * chore: update lockfiles, Rust plugin stubs, and manual test results Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> --------- Signed-off-by: NAYANA.R <nayana.r7813@gmail.com> Signed-off-by: Mihai Criveti <crivetimihai@gmail.com> Co-authored-by: Mihai Criveti <crivetimihai@gmail.com>
1 parent 0aef187 commit 0e2d560

File tree

9 files changed

+677
-20
lines changed

9 files changed

+677
-20
lines changed

mcpgateway/static/admin.js

Lines changed: 88 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6674,7 +6674,7 @@ async function viewServer(serverId) {
66746674
toolsList.appendChild(toolItem);
66756675
});
66766676

6677-
// If more than maxToShow, add a summary badge
6677+
// If more than maxToShow, add a summary badge (clickable to expand)
66786678
if (server.associatedTools.length > maxToShow) {
66796679
const moreItem = document.createElement("div");
66806680
moreItem.className = "flex items-center space-x-2";
@@ -6686,6 +6686,32 @@ async function viewServer(serverId) {
66866686
const remaining = server.associatedTools.length - maxToShow;
66876687
moreBadge.textContent = `+${remaining} more`;
66886688

6689+
// Expand inline to show full list when clicked
6690+
moreBadge.addEventListener("click", () => {
6691+
toolsList.innerHTML = "";
6692+
(server.associatedTools || []).forEach((toolId) => {
6693+
const toolItem = document.createElement("div");
6694+
toolItem.className = "flex items-center space-x-2";
6695+
6696+
const toolBadge = document.createElement("span");
6697+
toolBadge.className =
6698+
"inline-block bg-green-100 text-green-800 text-xs px-2 py-1 rounded-full dark:bg-green-900 dark:text-green-200";
6699+
toolBadge.textContent =
6700+
window.toolMapping && window.toolMapping[toolId]
6701+
? window.toolMapping[toolId]
6702+
: toolId;
6703+
6704+
const toolIdSpan = document.createElement("span");
6705+
toolIdSpan.className =
6706+
"text-xs text-gray-500 dark:text-gray-400";
6707+
toolIdSpan.textContent = `(${toolId})`;
6708+
6709+
toolItem.appendChild(toolBadge);
6710+
toolItem.appendChild(toolIdSpan);
6711+
toolsList.appendChild(toolItem);
6712+
});
6713+
});
6714+
66896715
moreItem.appendChild(moreBadge);
66906716
toolsList.appendChild(moreItem);
66916717
}
@@ -6740,7 +6766,7 @@ async function viewServer(serverId) {
67406766
resourcesList.appendChild(resourceItem);
67416767
});
67426768

6743-
// If more than maxToShow, add a summary badge
6769+
// If more than maxToShow, add a summary badge (clickable to expand)
67446770
if (server.associatedResources.length > maxToShow) {
67456771
const moreItem = document.createElement("div");
67466772
moreItem.className = "flex items-center space-x-2";
@@ -6753,6 +6779,38 @@ async function viewServer(serverId) {
67536779
server.associatedResources.length - maxToShow;
67546780
moreBadge.textContent = `+${remaining} more`;
67556781

6782+
moreBadge.addEventListener("click", () => {
6783+
resourcesList.innerHTML = "";
6784+
(server.associatedResources || []).forEach(
6785+
(resourceId) => {
6786+
const resourceItem =
6787+
document.createElement("div");
6788+
resourceItem.className =
6789+
"flex items-center space-x-2";
6790+
6791+
const resourceBadge =
6792+
document.createElement("span");
6793+
resourceBadge.className =
6794+
"inline-block bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded-full dark:bg-blue-900 dark:text-blue-200";
6795+
resourceBadge.textContent =
6796+
window.resourceMapping &&
6797+
window.resourceMapping[resourceId]
6798+
? window.resourceMapping[resourceId]
6799+
: `Resource ${resourceId}`;
6800+
6801+
const resourceIdSpan =
6802+
document.createElement("span");
6803+
resourceIdSpan.className =
6804+
"text-xs text-gray-500 dark:text-gray-400";
6805+
resourceIdSpan.textContent = `(${resourceId})`;
6806+
6807+
resourceItem.appendChild(resourceBadge);
6808+
resourceItem.appendChild(resourceIdSpan);
6809+
resourcesList.appendChild(resourceItem);
6810+
},
6811+
);
6812+
});
6813+
67566814
moreItem.appendChild(moreBadge);
67576815
resourcesList.appendChild(moreItem);
67586816
}
@@ -6806,7 +6864,7 @@ async function viewServer(serverId) {
68066864
promptsList.appendChild(promptItem);
68076865
});
68086866

6809-
// If more than maxToShow, add a summary badge
6867+
// If more than maxToShow, add a summary badge (clickable to expand)
68106868
if (server.associatedPrompts.length > maxToShow) {
68116869
const moreItem = document.createElement("div");
68126870
moreItem.className = "flex items-center space-x-2";
@@ -6819,6 +6877,33 @@ async function viewServer(serverId) {
68196877
server.associatedPrompts.length - maxToShow;
68206878
moreBadge.textContent = `+${remaining} more`;
68216879

6880+
moreBadge.addEventListener("click", () => {
6881+
promptsList.innerHTML = "";
6882+
(server.associatedPrompts || []).forEach((promptId) => {
6883+
const promptItem = document.createElement("div");
6884+
promptItem.className =
6885+
"flex items-center space-x-2";
6886+
6887+
const promptBadge = document.createElement("span");
6888+
promptBadge.className =
6889+
"inline-block bg-purple-100 text-purple-800 text-xs px-2 py-1 rounded-full dark:bg-purple-900 dark:text-purple-200";
6890+
promptBadge.textContent =
6891+
window.promptMapping &&
6892+
window.promptMapping[promptId]
6893+
? window.promptMapping[promptId]
6894+
: `Prompt ${promptId}`;
6895+
6896+
const promptIdSpan = document.createElement("span");
6897+
promptIdSpan.className =
6898+
"text-xs text-gray-500 dark:text-gray-400";
6899+
promptIdSpan.textContent = `(${promptId})`;
6900+
6901+
promptItem.appendChild(promptBadge);
6902+
promptItem.appendChild(promptIdSpan);
6903+
promptsList.appendChild(promptItem);
6904+
});
6905+
});
6906+
68226907
moreItem.appendChild(moreBadge);
68236908
promptsList.appendChild(moreItem);
68246909
}

package-lock.json

Lines changed: 27 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,8 @@
2929
"overrides": {
3030
"keyv": "5.1.0",
3131
"minimatch": ">=10.2.1"
32+
},
33+
"dependencies": {
34+
"alpinejs": "^3.15.8"
3235
}
3336
}

plugins_rust/encoded_exfil_detection/python/encoded_exfil_detection_rust/__init__.pyi

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,3 @@ __all__ = [
88
]
99

1010
def py_scan_container(container: typing.Any, config: typing.Any) -> tuple[builtins.int, typing.Any, list]: ...
11-

plugins_rust/pii_filter/python/pii_filter_rust/__init__.pyi

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,29 +11,29 @@ __all__ = [
1111
class PIIDetectorRust:
1212
r"""
1313
Main PII detector exposed to Python
14-
14+
1515
# Example (Python)
1616
```python
1717
from pii_filter import PIIDetectorRust
18-
18+
1919
config = {"detect_ssn": True, "detect_email": True}
2020
detector = PIIDetectorRust(config)
21-
21+
2222
text = "My SSN is 123-45-6789 and email is john@example.com"
2323
detections = detector.detect(text)
2424
print(detections) # {"ssn": [...], "email": [...]}
25-
25+
2626
masked = detector.mask(text, detections)
2727
print(masked) # "My SSN is [REDACTED] and email is [REDACTED]"
2828
```
2929
"""
3030
def __new__(cls, config: typing.Any) -> PIIDetectorRust:
3131
r"""
3232
Create a new PII detector
33-
33+
3434
# Arguments
3535
* `config` - Python dictionary or Pydantic model with configuration
36-
36+
3737
# Configuration Keys
3838
* `detect_ssn` (bool): Detect Social Security Numbers
3939
* `detect_credit_card` (bool): Detect credit card numbers
@@ -55,10 +55,10 @@ class PIIDetectorRust:
5555
def detect(self, text: builtins.str) -> typing.Any:
5656
r"""
5757
Detect PII in text
58-
58+
5959
# Arguments
6060
* `text` - Text to scan for PII
61-
61+
6262
# Returns
6363
Dictionary mapping PII type to list of detections:
6464
```python
@@ -75,23 +75,22 @@ class PIIDetectorRust:
7575
def mask(self, text: builtins.str, detections: typing.Any) -> builtins.str:
7676
r"""
7777
Mask detected PII in text
78-
78+
7979
# Arguments
8080
* `text` - Original text
8181
* `detections` - Detection results from detect()
82-
82+
8383
# Returns
8484
Masked text with PII replaced
8585
"""
8686
def process_nested(self, data: typing.Any, path: builtins.str) -> tuple[builtins.bool, typing.Any, typing.Any]:
8787
r"""
8888
Process nested data structures (dicts, lists, strings)
89-
89+
9090
# Arguments
9191
* `data` - Python object (dict, list, str, or other)
9292
* `path` - Current path in the structure (for logging)
93-
93+
9494
# Returns
9595
Tuple of (modified: bool, new_data: Any, detections: dict)
9696
"""
97-

plugins_rust/secrets_detection/python/secrets_detection_rust/__init__.pyi

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,3 @@ def py_scan_container(container: typing.Any, config: typing.Any) -> tuple[builti
1111
r"""
1212
Scan Python container for secrets using optimized type dispatch
1313
"""
14-

0 commit comments

Comments
 (0)