Skip to content

OU-1139: feat: allow to import and migrate a dashboard#779

Merged
openshift-merge-bot[bot] merged 1 commit intoopenshift:mainfrom
jgbernalp:allow-dashboard-import
Feb 25, 2026
Merged

OU-1139: feat: allow to import and migrate a dashboard#779
openshift-merge-bot[bot] merged 1 commit intoopenshift:mainfrom
jgbernalp:allow-dashboard-import

Conversation

@jgbernalp
Copy link
Contributor

@jgbernalp jgbernalp commented Feb 24, 2026

This PR adds the import button as part of a new menu which also includes the create option as default, following UX suggestions:

Screen.Recording.2026-02-24.at.13.00.03.mov

Summary by CodeRabbit

  • New Features

    • Dashboard import modal with editor + file upload, auto-detects format, supports Grafana→Perses migration, project selection, and import progress.
    • Dashboard actions menu with Create and Import; Create opens a dedicated modal with inline project/name form.
  • Bug Fixes / UX

    • Improved validation and clear feedback for invalid formats, file types, parsing/migration/import errors, and 5MB file-size limit.
  • Documentation

    • Expanded localization strings for project creation, import workflow, errors, and user guidance.

@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Feb 24, 2026
@openshift-ci-robot
Copy link

openshift-ci-robot commented Feb 24, 2026

@jgbernalp: This pull request references OU-1139 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the task to target the "4.22.0" version, but no target version was set.

Details

In response to this:

This PR adds the import button as part of a new menu which also includes the create option as default, following UX suggestions:

Screen.Recording.2026-02-24.at.13.00.03.mov

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci openshift-ci bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Feb 24, 2026
@openshift-ci-robot
Copy link

openshift-ci-robot commented Feb 24, 2026

@jgbernalp: This pull request references OU-1139 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the task to target the "4.22.0" version, but no target version was set.

Details

In response to this:

This PR adds the import button as part of a new menu which also includes the create option as default, following UX suggestions:

Screen.Recording.2026-02-24.at.13.00.03.mov

Summary by CodeRabbit

  • New Features

  • Added dashboard import functionality supporting JSON and YAML formats

  • Enabled Grafana-to-Perses dashboard migration with automatic format detection

  • Introduced file upload for dashboard imports with validation (max 5MB, file type checks)

  • Added step-by-step import workflow with project selection

  • Enhanced dashboard actions menu with Create and Import options

  • Documentation

  • Added comprehensive localization strings for project creation, import workflows, and error messages

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@coderabbitai
Copy link

coderabbitai bot commented Feb 24, 2026

📝 Walkthrough

Walkthrough

Adds a dashboard actions menu with Create and Import flows, converts the create dialog to a controlled component, introduces a large dashboard import modal (file/editor, parsing, Grafana→Perses migration), adds a migration API/hook, updates i18n keys, adds a code-editor dependency, and a data-test id.

Changes

Cohort / File(s) Summary
Localization
web/locales/en/plugin__monitoring-plugin.json
Added ~30+ i18n keys for project creation and a full dashboard import workflow (validation, parsing, migration, progress, success/error messages); removed/reordered some existing validation keys.
Dependencies
web/package.json
Added @patternfly/react-code-editor ^6.4.1 and upgraded @patternfly/react-core to ^6.4.1.
Dashboard actions & header
web/src/components/dashboards/perses/dashboard-actions-menu.tsx, web/src/components/dashboards/perses/dashboard-header.tsx
New DashboardActionsMenu component (Create / Import split-button); header now uses DashboardActionsMenu instead of embedding DashboardCreateDialog.
Create dialog (controlled)
web/src/components/dashboards/perses/dashboard-create-dialog.tsx
Refactored to controlled component with isOpen/onClose props, removed internal modal state and tooltip gating, added explicit form controls and inline alerts/error handling.
Import dialog
web/src/components/dashboards/perses/dashboard-import-dialog.tsx
New large import modal: editor + file upload (JSON/YAML), file size/type validation (5MB), format detection, Grafana→Perses migration flow, project typeahead selection, import mutation, navigation on success, and comprehensive error handling.
Migration API & hook
web/src/components/dashboards/perses/migrate-api.ts
Added migrateDashboard POST wrapper and useMigrateDashboard() react-query mutation with typed request/response.
Data test IDs
web/src/components/data-test.ts
Added persesDashboardDataTestIDs.importDashboardButtonToolbar = 'import-dashboard-button-list-page'.

Sequence Diagram

sequenceDiagram
    actor User
    participant UI as Dashboard UI
    participant Parser as Format Detector/Parser
    participant MigrationAPI as Migration Service
    participant PersesAPI as Perses API
    participant Router as Router

    User->>UI: Upload file or paste dashboard (JSON/YAML)
    UI->>Parser: Parse input & detect format
    alt Format valid
        Parser-->>UI: Detected Grafana or Perses
        alt Grafana detected
            UI->>MigrationAPI: POST migrate (Grafana -> Perses)
            MigrationAPI-->>UI: Migrated Perses dashboard
        else Perses detected
            Parser-->>UI: Parsed Perses dashboard
        end
        User->>UI: Select target project
        UI->>PersesAPI: POST import dashboard
        PersesAPI-->>UI: Import success (dashboard id)
        UI->>Router: Navigate to new dashboard
        UI-->>User: Show success alert
    else Invalid input / size / type
        Parser-->>UI: Error (invalid format / parsing / size / type)
        UI-->>User: Show error alert
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐇 I nudged JSON and YAML through the gate,
I hopped between Grafana and Perses' state,
A split-button click, an import delight,
Projects picked, dashboards take flight,
Hooray — new routes and strings look great! 🎉

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: allow to import and migrate a dashboard' clearly and accurately summarizes the main changes in the PR—adding dashboard import functionality with Grafana-to-Perses migration support.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/package.json`:
- Line 68: The project has a dependency mismatch: the added dependency
"@patternfly/react-code-editor" (entry "@patternfly/react-code-editor":
"^6.4.1") requires "@patternfly/react-core@^6.4.1" while the project currently
pins "@patternfly/react-core" to 6.2.x; fix by either updating the project's
"@patternfly/react-core" version in package.json to at least "6.4.1" (and run
install/lockfile update) or replace/pin the "@patternfly/react-code-editor"
entry to a 6.2.x compatible release so both packages share a compatible version
range. Ensure package.json reflects the chosen option and regenerate the
lockfile.

In `@web/src/components/dashboards/perses/dashboard-actions-menu.tsx`:
- Around line 70-73: The Import DropdownItem is missing the test hook; update
the DropdownItem rendering for the Import action (the element using DropdownItem
and onClick={handleImportClick}) to include the data-test attribute with
persesDashboardDataTestIDs.importDashboardButtonToolbar so E2E selectors can
target it; ensure the attribute name matches the test suite expectation (e.g.,
data-test or data-testid) consistent with other components.

In `@web/src/components/dashboards/perses/dashboard-import-dialog.tsx`:
- Line 305: The Import button can stay enabled when a parse/validation error
exists because the guard only checks parsedDashboard, selectedProject and
isImporting; update the guard for canImport to also require no parseError so
stale/invalid parsed data cannot be imported. Locate the const canImport
declaration and change it to include && !parseError (ensuring parseError is in
scope), so the expression uses parsedDashboard && selectedProject &&
!isImporting && !parseError.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 57f0b76 and fa00fad.

⛔ Files ignored due to path filters (1)
  • web/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (8)
  • web/locales/en/plugin__monitoring-plugin.json
  • web/package.json
  • web/src/components/dashboards/perses/dashboard-actions-menu.tsx
  • web/src/components/dashboards/perses/dashboard-create-dialog.tsx
  • web/src/components/dashboards/perses/dashboard-header.tsx
  • web/src/components/dashboards/perses/dashboard-import-dialog.tsx
  • web/src/components/dashboards/perses/migrate-api.ts
  • web/src/components/data-test.ts

@jgbernalp
Copy link
Contributor Author

/cherry-pick release-coo-0.5

@openshift-cherrypick-robot

@jgbernalp: once the present PR merges, I will cherry-pick it on top of release-coo-0.5 in a new PR and assign it to you.

Details

In response to this:

/cherry-pick release-coo-0.5

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@jgbernalp jgbernalp force-pushed the allow-dashboard-import branch from fa00fad to 8537252 Compare February 24, 2026 16:57
@openshift-ci-robot
Copy link

openshift-ci-robot commented Feb 24, 2026

@jgbernalp: This pull request references OU-1139 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the task to target the "4.22.0" version, but no target version was set.

Details

In response to this:

This PR adds the import button as part of a new menu which also includes the create option as default, following UX suggestions:

Screen.Recording.2026-02-24.at.13.00.03.mov

Summary by CodeRabbit

  • New Features

  • Dashboard import modal with editor and file upload (JSON/YAML), format detection, Grafana→Perses migration, project selection, and import progress.

  • Dashboard actions menu adds Create and Import options; Create now opens a dedicated modal.

  • Bug Fixes / UX

  • Validation and user feedback for invalid formats, file types, and file size limits (5MB).

  • Documentation

  • Expanded localization strings for project creation, import flow, errors, and guidance.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@jgbernalp jgbernalp force-pushed the allow-dashboard-import branch from 8537252 to fc85fef Compare February 24, 2026 17:01
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

♻️ Duplicate comments (2)
web/src/components/dashboards/perses/dashboard-actions-menu.tsx (1)

70-73: data-test attribute missing on Import DropdownItem.

persesDashboardDataTestIDs.importDashboardButtonToolbar is defined but not applied to the Import DropdownItem, preventing E2E selectors from targeting it.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/dashboards/perses/dashboard-actions-menu.tsx` around lines
70 - 73, The Import DropdownItem is missing the E2E selector; add the data-test
attribute using the existing identifier
persesDashboardDataTestIDs.importDashboardButtonToolbar to the Import
<DropdownItem> (the one rendering {t('Import')} and using
onClick={handleImportClick}) so tests can target it; ensure the attribute is
added to the JSX element alongside the key and onClick attributes.
web/src/components/dashboards/perses/dashboard-import-dialog.tsx (1)

174-184: ⚠️ Potential issue | 🟡 Minor

Early MIME/size validation returns without clearing parsedDashboard — root cause of stale import guard.

When file-type or size validation fails, the function returns early after setting parseError but leaves any previously-set parsedDashboard intact. This means canImport (Line 305) can still evaluate to true despite a live parse error, allowing the user to import stale data from a prior successful parse. The previous review already flagged adding !parseError to canImport; clearing the parsed state on these early-exit paths fixes it at the source.

🐛 Proposed fix
 if (file.type && !ALLOWED_MIME_TYPES.includes(file.type)) {
   setParseError(
     t('Invalid file type. Please upload a JSON or YAML file (.json, .yaml, .yml)'),
   );
+  setParsedDashboard(undefined);
   return;
 }

 if (file.size > MAX_FILE_SIZE) {
   setParseError(t('File size exceeds maximum allowed size of 5MB'));
+  setParsedDashboard(undefined);
   return;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/dashboards/perses/dashboard-import-dialog.tsx` around
lines 174 - 184, The early-return validation branches set parseError but don’t
clear the existing parsed dashboard, leaving parsedDashboard stale and allowing
canImport to remain true; update the file-type and file-size failure paths in
the import handler to call setParsedDashboard(null) (or the appropriate empty
state) immediately before setParseError and return so parsedDashboard is cleared
whenever parseError is set, ensuring canImport correctly reflects the error
state; reference the setParseError and setParsedDashboard state setters and the
parsedDashboard/canImport logic in dashboard-import-dialog.tsx when applying the
change.
🧹 Nitpick comments (2)
web/src/components/dashboards/perses/dashboard-import-dialog.tsx (1)

246-274: Migration onError silently updates only inline state — no toast alert shown.

When Grafana migration fails, the onError callback (Lines 268–273) calls setFormErrors but omits addAlert. By contrast, every other failure path in this function (Lines 261–264, Lines 280–283) calls both. Users who have scrolled past the modal's inline error banner will miss migration failures entirely.

🔧 Proposed fix
-          onError: (error) => {
-            const errorMessage =
-              getErrorMessage(error) || t('Migration failed. Please try again.');
-            setFormErrors({ general: errorMessage });
-          },
+          onError: (error) => {
+            const errorMessage =
+              getErrorMessage(error) || t('Migration failed. Please try again.');
+            addAlert(
+              t('Error importing dashboard: {{error}}', { error: errorMessage }),
+              'danger',
+            );
+            setFormErrors({ general: errorMessage });
+          },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/dashboards/perses/dashboard-import-dialog.tsx` around
lines 246 - 274, The migration onError handler for migrateMutation.mutate
currently only calls setFormErrors and so fails to surface failures to users;
update the onError callback in migrateMutation.mutate to also call addAlert with
the composed error message (use getErrorMessage(error) || t('Migration failed.
Please try again.')) — mirror the pattern used in the import onSuccess catch and
other error paths so both addAlert(...) and setFormErrors({ general:
errorMessage }) are invoked in the onError handler.
web/src/components/dashboards/perses/migrate-api.ts (1)

24-27: Error context discarded on migration failure.

new Error(Failed to migrate dashboard: ${message}) replaces the original error, dropping any HTTP status, response body details, or structured error data that consoleFetchJSON would have included. Callers only see the generic message string.

Consider re-throwing or appending the original error as a cause to preserve debuggability:

♻️ Proposed fix
-    throw new Error(`Failed to migrate dashboard: ${message}`);
+    throw new Error(`Failed to migrate dashboard: ${message}`, { cause: error });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/dashboards/perses/migrate-api.ts` around lines 24 - 27,
The catch block that currently does "const message = error instanceof Error ?
error.message : 'Unknown error'; throw new Error(`Failed to migrate dashboard:
${message}`);" discards the original error details; update it to preserve the
original error as the cause (e.g., throw new Error(`Failed to migrate dashboard:
${message}`, { cause: error })) or attach the original error object to the
thrown error (e.g., set a .cause or .originalError property) so callers can
access HTTP status/response/body from consoleFetchJSON; keep the existing
message but pass the original error through using the Error constructor's cause
option or by re-throwing the original error after augmenting it.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/src/components/dashboards/perses/dashboard-actions-menu.tsx`:
- Around line 1-17: The file uses React namespace types (e.g.,
React.FunctionComponent in DashboardActionsMenu and
React.Ref<MenuToggleElement>) but doesn't import the React namespace; add the
missing import by inserting "import React from 'react'" at the top of the file
so React is in scope for those type references (match the project's pattern used
in files like dashboard-header.tsx).

In `@web/src/components/dashboards/perses/dashboard-create-dialog.tsx`:
- Around line 159-166: The Modal's aria-labelledby points to an id that doesn't
exist; update the Modal and ModalHeader to share a real id via ModalHeader's
labelId prop. Concretely, create a single id string (e.g. "modal-with-dropdown"
or "modal-with-dropdown-title"), set the Modal's aria-labelledby to that id, and
pass the same id into the ModalHeader via its labelId prop (and ensure the
header's title uses that id through the labelId mechanism). Modify the Modal
usage and the ModalHeader invocation in dashboard-create-dialog.tsx so they
reference the same id instead of leaving aria-labelledby pointing to a
non-existent element.

In `@web/src/components/dashboards/perses/dashboard-import-dialog.tsx`:
- Around line 307-315: The Modal's aria-labelledby points to a missing id
("import-dashboard-modal") so screen readers can't find the title; fix by giving
the ModalHeader the matching id or removing aria-labelledby. Update the JSX
where Modal and ModalHeader are used (the Modal element with props
isOpen/onClose/onEscapePress and the ModalHeader component) so that ModalHeader
receives id="import-dashboard-modal" (or remove aria-labelledby from Modal to
let PatternFly handle it) to ensure the label reference resolves.
- Around line 364-374: The editor language is hardcoded to JSON
(language={Language.json}) causing wrong highlighting for YAML uploads; update
the component to derive the language dynamically (e.g., from the uploaded file
name or content) and pass that instead of Language.json. Add a small helper like
determineLanguageFromFilename(filename) (or content-sniffing) and a state
variable (e.g., editorLanguage) that gets set when the file is uploaded or
dashboardInput changes, then replace language={Language.json} with
language={editorLanguage} and ensure the upload handler or
handleDashboardInputChange sets editorLanguage appropriately for .yaml/.yml =>
Language.yaml and .json => Language.json (fall back to a default).
- Line 1: The file uses React.FunctionComponent<DashboardImportDialogProps> but
doesn't import the React namespace; add an import for React (e.g., import React
from 'react';) at the top of the file alongside the existing imports so the
React identifier is defined for the React.FunctionComponent type used in the
DashboardImportDialog component declaration.

---

Duplicate comments:
In `@web/src/components/dashboards/perses/dashboard-actions-menu.tsx`:
- Around line 70-73: The Import DropdownItem is missing the E2E selector; add
the data-test attribute using the existing identifier
persesDashboardDataTestIDs.importDashboardButtonToolbar to the Import
<DropdownItem> (the one rendering {t('Import')} and using
onClick={handleImportClick}) so tests can target it; ensure the attribute is
added to the JSX element alongside the key and onClick attributes.

In `@web/src/components/dashboards/perses/dashboard-import-dialog.tsx`:
- Around line 174-184: The early-return validation branches set parseError but
don’t clear the existing parsed dashboard, leaving parsedDashboard stale and
allowing canImport to remain true; update the file-type and file-size failure
paths in the import handler to call setParsedDashboard(null) (or the appropriate
empty state) immediately before setParseError and return so parsedDashboard is
cleared whenever parseError is set, ensuring canImport correctly reflects the
error state; reference the setParseError and setParsedDashboard state setters
and the parsedDashboard/canImport logic in dashboard-import-dialog.tsx when
applying the change.

---

Nitpick comments:
In `@web/src/components/dashboards/perses/dashboard-import-dialog.tsx`:
- Around line 246-274: The migration onError handler for migrateMutation.mutate
currently only calls setFormErrors and so fails to surface failures to users;
update the onError callback in migrateMutation.mutate to also call addAlert with
the composed error message (use getErrorMessage(error) || t('Migration failed.
Please try again.')) — mirror the pattern used in the import onSuccess catch and
other error paths so both addAlert(...) and setFormErrors({ general:
errorMessage }) are invoked in the onError handler.

In `@web/src/components/dashboards/perses/migrate-api.ts`:
- Around line 24-27: The catch block that currently does "const message = error
instanceof Error ? error.message : 'Unknown error'; throw new Error(`Failed to
migrate dashboard: ${message}`);" discards the original error details; update it
to preserve the original error as the cause (e.g., throw new Error(`Failed to
migrate dashboard: ${message}`, { cause: error })) or attach the original error
object to the thrown error (e.g., set a .cause or .originalError property) so
callers can access HTTP status/response/body from consoleFetchJSON; keep the
existing message but pass the original error through using the Error
constructor's cause option or by re-throwing the original error after augmenting
it.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between fa00fad and 8537252.

⛔ Files ignored due to path filters (1)
  • web/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (8)
  • web/locales/en/plugin__monitoring-plugin.json
  • web/package.json
  • web/src/components/dashboards/perses/dashboard-actions-menu.tsx
  • web/src/components/dashboards/perses/dashboard-create-dialog.tsx
  • web/src/components/dashboards/perses/dashboard-header.tsx
  • web/src/components/dashboards/perses/dashboard-import-dialog.tsx
  • web/src/components/dashboards/perses/migrate-api.ts
  • web/src/components/data-test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • web/package.json

@openshift-ci-robot
Copy link

openshift-ci-robot commented Feb 24, 2026

@jgbernalp: This pull request references OU-1139 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the task to target the "4.22.0" version, but no target version was set.

Details

In response to this:

This PR adds the import button as part of a new menu which also includes the create option as default, following UX suggestions:

Screen.Recording.2026-02-24.at.13.00.03.mov

Summary by CodeRabbit

  • New Features

  • Dashboard import modal (editor + file upload), auto-detects format, supports Grafana→Perses migration, project selection, and import progress.

  • Dashboard actions menu adds Create and Import; Create opens a dedicated modal with inline form.

  • Bug Fixes / UX

  • Validation and clear feedback for invalid formats, file types, parsing/migration/import errors, and 5MB file size limit.

  • Documentation

  • Expanded localization strings for project creation, import flow, errors, and user guidance.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
web/src/components/dashboards/perses/dashboard-create-dialog.tsx (2)

97-97: ⚠️ Potential issue | 🟡 Minor

Dashboard name duplicate error bypasses i18n.

Line 97 uses a template literal instead of t(). A matching locale key already exists: "Dashboard name '{{dashboardName}}' already exists in '{{projectName}}' project!" — use it with selectedProject as projectName.

🌐 Proposed fix
-        setFormErrors({
-          dashboardName: `Dashboard name "${dashboardName}" already exists in this project`,
-        });
+        setFormErrors({
+          dashboardName: t(
+            "Dashboard name '{{dashboardName}}' already exists in '{{projectName}}' project!",
+            { dashboardName, projectName: selectedProject },
+          ),
+        });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/dashboards/perses/dashboard-create-dialog.tsx` at line 97,
Replace the hard-coded template string that constructs the duplicate name error
with a call to the i18n translator; in the component
(dashboard-create-dialog.tsx) where you currently build dashboardName:
`Dashboard name "${dashboardName}" already exists in this project`, use the
existing locale key "Dashboard name '{{dashboardName}}' already exists in
'{{projectName}}' project!" via t(...) and pass { dashboardName, projectName:
selectedProject } so the message is localized and includes the project name.

132-132: ⚠️ Potential issue | 🟡 Minor

Success and error alerts on Lines 132 and 143 are not translated.

Both addAlert calls use hardcoded English template literals while the import dialog equivalents correctly use t(). These need both t() wrappers and corresponding locale keys added (e.g., "Dashboard \"{{name}}\" created successfully" and "Error creating dashboard: {{error}}").

🌐 Proposed fix
-      addAlert(`Dashboard "${dashboardName}" created successfully`, 'success');
+      addAlert(t('Dashboard "{{name}}" created successfully', { name: dashboardName }), 'success');
-      addAlert(`Error creating dashboard: ${errorMessage}`, 'danger');
+      addAlert(t('Error creating dashboard: {{error}}', { error: errorMessage }), 'danger');
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/dashboards/perses/dashboard-create-dialog.tsx` at line
132, Replace the hardcoded English template literals passed to addAlert with
translated strings using t() and interpolation: wrap the success message (where
addAlert(`Dashboard "${dashboardName}" created successfully`, 'success') is
called) as t('dashboard.created_success', { name: dashboardName }) and wrap the
error alert (the addAlert call on failure) as t('dashboard.create_error', {
error: errorMessageOrErr }); update the locale files with keys
"dashboard.created_success": "Dashboard \"{{name}}\" created successfully" and
"dashboard.create_error": "Error creating dashboard: {{error}}". Ensure you
reference the exact variables used in the function (dashboardName and the caught
error variable) when passing interpolation values to t().
♻️ Duplicate comments (6)
web/src/components/dashboards/perses/dashboard-import-dialog.tsx (4)

1-65: ⚠️ Potential issue | 🔴 Critical

Add missing React import for React.FunctionComponent.

Line 65 uses React.FunctionComponent<DashboardImportDialogProps> but React is never imported. With "jsx": "react-jsx", the JSX transform is automatic, but namespace-qualified type references like React.FunctionComponent still require the React identifier to be in scope. This is a TypeScript compilation error.

🔧 Proposed fix
+import React from 'react';
 import { useMemo, useState, ChangeEvent } from 'react';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/dashboards/perses/dashboard-import-dialog.tsx` around
lines 1 - 65, The file references React.FunctionComponent in the
DashboardImportDialog declaration but never imports the React identifier,
causing a TypeScript error; fix by adding a top-level import for React (so React
is in scope) or change the component type to use the standalone
FunctionComponent/FC type from 'react' and import that instead (e.g., import
React, { FunctionComponent } from 'react' or import { FunctionComponent } from
'react' and use FunctionComponent<DashboardImportDialogProps> on
DashboardImportDialog).

364-374: CodeEditor language is hardcoded to Language.json regardless of uploaded file type.

Valid YAML content uploaded via the file picker will still receive JSON syntax highlighting, producing misleading red markers. Derive the language from the filename state already available in this component.

♻️ Proposed fix
+  const editorLanguage =
+    filename.endsWith('.yaml') || filename.endsWith('.yml') ? Language.yaml : Language.json;
+
   <CodeEditor
     id="import-dashboard-code-editor"
     code={dashboardInput}
     onChange={handleDashboardInputChange}
-    language={Language.json}
+    language={editorLanguage}
     height="300px"
     isLineNumbersVisible
     isDarkTheme={theme === 'dark'}
   />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/dashboards/perses/dashboard-import-dialog.tsx` around
lines 364 - 374, The CodeEditor is always using Language.json causing YAML
uploads to be highlighted incorrectly; change the language prop on the
CodeEditor (component instance with id "import-dashboard-code-editor") to be
derived from the component's filename state instead of hardcoding Language.json:
implement a small mapping from filename extension (e.g., .yaml/.yml ->
Language.yaml, .json -> Language.json) or a utility like
getLanguageFromFilename(filename) and pass that result to the language prop,
leaving dashboardInput and handleDashboardInputChange unchanged so uploads pick
the correct syntax highlighting.

308-315: ⚠️ Potential issue | 🟡 Minor

aria-labelledby="import-dashboard-modal" references a non-existent element ID.

The PF6 Modal prop documentation states that aria-labelledby "should include the ModalHeader labelId." Since ModalHeader does not have an id prop and no element in the render tree carries id="import-dashboard-modal", screen readers cannot resolve this reference. Use ModalHeader's labelId prop to generate the matching ID.

♿ Proposed fix
-      aria-labelledby="import-dashboard-modal"
+      aria-labelledby="import-dashboard-modal-title"
     >
-      <ModalHeader title={t('Import Dashboard')} />
+      <ModalHeader title={t('Import Dashboard')} labelId="import-dashboard-modal-title" />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/dashboards/perses/dashboard-import-dialog.tsx` around
lines 308 - 315, The Modal's aria-labelledby references "import-dashboard-modal"
which doesn't exist; update the ModalHeader usage to supply a matching labelId
and use that labelId value for the Modal's aria-labelledby prop (e.g., generate
a constant id like labelId = 'import-dashboard-modal' and pass it to ModalHeader
via its labelId prop, then set Modal aria-labelledby={labelId}); modify the JSX
around Modal and ModalHeader to wire the same unique id string to both
components so screen readers can resolve the label.

151-160: Show only the relevant parse error, not both JSON and YAML errors concatenated.

When input fails both parsers, the combined "JSON: ..., YAML: ..." detail string gives users two low-level error messages with no clear actionable path. Since the input can be sniffed (starts with { → JSON, otherwise YAML), only the relevant parser's error should be surfaced.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/dashboards/perses/dashboard-import-dialog.tsx` around
lines 151 - 160, The catch block currently concatenates both jsonError and
yamlError messages; change it to surface only the relevant parser error:
determine which parser was expected by sniffing the trimmed input (e.g., if
trimmedInput.startsWith('{') treat as JSON else YAML), then pick only
jsonError.message or yamlError.message accordingly; keep the existing calls to
setParsedDashboard(undefined) and setParseError but build details from the
chosen error (fallback to the other error only if the chosen one is absent)
using the same user-facing prefix text.
web/src/components/dashboards/perses/dashboard-create-dialog.tsx (1)

159-167: ⚠️ Potential issue | 🟡 Minor

aria-labelledby="modal-with-dropdown" references a non-existent element ID.

PF6 Modal's aria-labelledby "should include the ModalHeader labelId." No element in the rendered output has id="modal-with-dropdown", so screen readers cannot resolve the reference.

♿ Proposed fix
-      aria-labelledby="modal-with-dropdown"
+      aria-labelledby="create-dashboard-modal-title"
     >
-      <ModalHeader title={t('Create Dashboard')} />
+      <ModalHeader title={t('Create Dashboard')} labelId="create-dashboard-modal-title" />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/dashboards/perses/dashboard-create-dialog.tsx` around
lines 159 - 167, The Modal currently sets aria-labelledby="modal-with-dropdown"
which doesn't match any rendered element; update the Modal/ModalHeader usage so
the Modal's aria-labelledby references the ModalHeader's actual label id (use
the ModalHeader's labelId prop value or pass a matching id down), e.g. ensure
Modal aria-labelledby equals the ModalHeader's labelId string (locate the Modal
component and the ModalHeader call in dashboard-create-dialog.tsx around
isOpen/handleClose and change the attribute to the ModalHeader's labelId or add
a matching id prop to ModalHeader).
web/src/components/dashboards/perses/dashboard-actions-menu.tsx (1)

1-17: ⚠️ Potential issue | 🔴 Critical

Add missing React import for React.FunctionComponent and React.Ref.

Line 17 uses React.FunctionComponent and Line 49 uses React.Ref<MenuToggleElement>, but the React namespace is never imported. This is a TypeScript compilation error; the "react-jsx" transform only covers JSX syntax, not namespace type references.

🔧 Proposed fix
+import React from 'react';
 import { useState } from 'react';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/dashboards/perses/dashboard-actions-menu.tsx` around lines
1 - 17, The file references React namespace types (React.FunctionComponent and
React.Ref<MenuToggleElement>) but does not import the React namespace; add an
explicit import for the React namespace (e.g. insert "import React from
'react';" at the top) or alternatively replace the namespace types with named
imports (import { FunctionComponent, Ref } from 'react' and use
FunctionComponent and Ref<MenuToggleElement>) so that DashboardActionsMenu and
the React.Ref<MenuToggleElement> usage compile correctly.
🧹 Nitpick comments (1)
web/src/components/dashboards/perses/dashboard-create-dialog.tsx (1)

113-115: error?.message on unknown-typed catch variable; use a shared getErrorMessage helper.

Lines 115 and 142 access .message directly on unknown-typed catch variables (projectError, error). This is a TypeScript error under useUnknownInCatchVariables (enabled by strict mode). The import dialog already defines a correct getErrorMessage(error) utility — extract it to a shared utils module and import it here instead of duplicating the pattern.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/dashboards/perses/dashboard-create-dialog.tsx` around
lines 113 - 115, The catch blocks in dashboard-create-dialog.tsx access .message
on unknown-typed variables (projectError and error); extract the existing
getErrorMessage helper from the import dialog into a shared utils module (e.g.,
utils/errors or similar), export it, then import that shared getErrorMessage
into dashboard-create-dialog.tsx and replace direct property access with calls
to getErrorMessage(projectError) and getErrorMessage(error) (also update any
other occurrences in this file such as the handler around line 142) to satisfy
TypeScript's useUnknownInCatchVariables rule.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/src/components/dashboards/perses/dashboard-import-dialog.tsx`:
- Around line 249-274: The migrateMutation.onError handler currently only calls
setFormErrors and does not surface a toast; update the failure path so the user
sees a toast by either switching the call site to await
migrateMutation.mutateAsync inside a try/catch and calling addAlert(...) in the
catch, or by adding an addAlert(...) invocation inside the existing onError
callback in migrateMutation.mutate (use the same error message logic as the
onSuccess catch: const errorMessage = getErrorMessage(error) || t('Migration
failed. Please try again.') and call addAlert(t('Error migrating dashboard:
{{error}}', { error: errorMessage }), 'danger') before setFormErrors({ general:
errorMessage })). Ensure you reference migrateMutation.mutate/mutateAsync,
onError, addAlert, setFormErrors and getErrorMessage when making the change.

---

Outside diff comments:
In `@web/src/components/dashboards/perses/dashboard-create-dialog.tsx`:
- Line 97: Replace the hard-coded template string that constructs the duplicate
name error with a call to the i18n translator; in the component
(dashboard-create-dialog.tsx) where you currently build dashboardName:
`Dashboard name "${dashboardName}" already exists in this project`, use the
existing locale key "Dashboard name '{{dashboardName}}' already exists in
'{{projectName}}' project!" via t(...) and pass { dashboardName, projectName:
selectedProject } so the message is localized and includes the project name.
- Line 132: Replace the hardcoded English template literals passed to addAlert
with translated strings using t() and interpolation: wrap the success message
(where addAlert(`Dashboard "${dashboardName}" created successfully`, 'success')
is called) as t('dashboard.created_success', { name: dashboardName }) and wrap
the error alert (the addAlert call on failure) as t('dashboard.create_error', {
error: errorMessageOrErr }); update the locale files with keys
"dashboard.created_success": "Dashboard \"{{name}}\" created successfully" and
"dashboard.create_error": "Error creating dashboard: {{error}}". Ensure you
reference the exact variables used in the function (dashboardName and the caught
error variable) when passing interpolation values to t().

---

Duplicate comments:
In `@web/src/components/dashboards/perses/dashboard-actions-menu.tsx`:
- Around line 1-17: The file references React namespace types
(React.FunctionComponent and React.Ref<MenuToggleElement>) but does not import
the React namespace; add an explicit import for the React namespace (e.g. insert
"import React from 'react';" at the top) or alternatively replace the namespace
types with named imports (import { FunctionComponent, Ref } from 'react' and use
FunctionComponent and Ref<MenuToggleElement>) so that DashboardActionsMenu and
the React.Ref<MenuToggleElement> usage compile correctly.

In `@web/src/components/dashboards/perses/dashboard-create-dialog.tsx`:
- Around line 159-167: The Modal currently sets
aria-labelledby="modal-with-dropdown" which doesn't match any rendered element;
update the Modal/ModalHeader usage so the Modal's aria-labelledby references the
ModalHeader's actual label id (use the ModalHeader's labelId prop value or pass
a matching id down), e.g. ensure Modal aria-labelledby equals the ModalHeader's
labelId string (locate the Modal component and the ModalHeader call in
dashboard-create-dialog.tsx around isOpen/handleClose and change the attribute
to the ModalHeader's labelId or add a matching id prop to ModalHeader).

In `@web/src/components/dashboards/perses/dashboard-import-dialog.tsx`:
- Around line 1-65: The file references React.FunctionComponent in the
DashboardImportDialog declaration but never imports the React identifier,
causing a TypeScript error; fix by adding a top-level import for React (so React
is in scope) or change the component type to use the standalone
FunctionComponent/FC type from 'react' and import that instead (e.g., import
React, { FunctionComponent } from 'react' or import { FunctionComponent } from
'react' and use FunctionComponent<DashboardImportDialogProps> on
DashboardImportDialog).
- Around line 364-374: The CodeEditor is always using Language.json causing YAML
uploads to be highlighted incorrectly; change the language prop on the
CodeEditor (component instance with id "import-dashboard-code-editor") to be
derived from the component's filename state instead of hardcoding Language.json:
implement a small mapping from filename extension (e.g., .yaml/.yml ->
Language.yaml, .json -> Language.json) or a utility like
getLanguageFromFilename(filename) and pass that result to the language prop,
leaving dashboardInput and handleDashboardInputChange unchanged so uploads pick
the correct syntax highlighting.
- Around line 308-315: The Modal's aria-labelledby references
"import-dashboard-modal" which doesn't exist; update the ModalHeader usage to
supply a matching labelId and use that labelId value for the Modal's
aria-labelledby prop (e.g., generate a constant id like labelId =
'import-dashboard-modal' and pass it to ModalHeader via its labelId prop, then
set Modal aria-labelledby={labelId}); modify the JSX around Modal and
ModalHeader to wire the same unique id string to both components so screen
readers can resolve the label.
- Around line 151-160: The catch block currently concatenates both jsonError and
yamlError messages; change it to surface only the relevant parser error:
determine which parser was expected by sniffing the trimmed input (e.g., if
trimmedInput.startsWith('{') treat as JSON else YAML), then pick only
jsonError.message or yamlError.message accordingly; keep the existing calls to
setParsedDashboard(undefined) and setParseError but build details from the
chosen error (fallback to the other error only if the chosen one is absent)
using the same user-facing prefix text.

---

Nitpick comments:
In `@web/src/components/dashboards/perses/dashboard-create-dialog.tsx`:
- Around line 113-115: The catch blocks in dashboard-create-dialog.tsx access
.message on unknown-typed variables (projectError and error); extract the
existing getErrorMessage helper from the import dialog into a shared utils
module (e.g., utils/errors or similar), export it, then import that shared
getErrorMessage into dashboard-create-dialog.tsx and replace direct property
access with calls to getErrorMessage(projectError) and getErrorMessage(error)
(also update any other occurrences in this file such as the handler around line
142) to satisfy TypeScript's useUnknownInCatchVariables rule.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 8537252 and fc85fef.

⛔ Files ignored due to path filters (1)
  • web/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (8)
  • web/locales/en/plugin__monitoring-plugin.json
  • web/package.json
  • web/src/components/dashboards/perses/dashboard-actions-menu.tsx
  • web/src/components/dashboards/perses/dashboard-create-dialog.tsx
  • web/src/components/dashboards/perses/dashboard-header.tsx
  • web/src/components/dashboards/perses/dashboard-import-dialog.tsx
  • web/src/components/dashboards/perses/migrate-api.ts
  • web/src/components/data-test.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • web/src/components/dashboards/perses/migrate-api.ts
  • web/package.json
  • web/src/components/dashboards/perses/dashboard-header.tsx

@jgbernalp jgbernalp force-pushed the allow-dashboard-import branch from fc85fef to 2b45216 Compare February 24, 2026 17:27
Signed-off-by: Gabriel Bernal <gbernal@redhat.com>
@jgbernalp jgbernalp force-pushed the allow-dashboard-import branch from 2b45216 to 8d6fef7 Compare February 24, 2026 17:49
@openshift-ci-robot
Copy link

openshift-ci-robot commented Feb 24, 2026

@jgbernalp: This pull request references OU-1139 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the task to target the "4.22.0" version, but no target version was set.

Details

In response to this:

This PR adds the import button as part of a new menu which also includes the create option as default, following UX suggestions:

Screen.Recording.2026-02-24.at.13.00.03.mov

Summary by CodeRabbit

  • New Features

  • Dashboard import modal with editor + file upload, auto-detects format, supports Grafana→Perses migration, project selection, and import progress.

  • Dashboard actions menu with Create and Import; Create opens a dedicated modal with inline project/name form.

  • Bug Fixes / UX

  • Improved validation and clear feedback for invalid formats, file types, parsing/migration/import errors, and 5MB file-size limit.

  • Documentation

  • Expanded localization strings for project creation, import workflow, errors, and user guidance.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (6)
web/src/components/dashboards/perses/dashboard-create-dialog.tsx (2)

159-167: ⚠️ Potential issue | 🟡 Minor

Ensure aria-labelledby points to an actual title element.

aria-labelledby="modal-with-dropdown" doesn’t match any element id in the modal. Wire it to the header’s id (or remove aria-labelledby and rely on default labeling).

♿ Suggested fix (verify PatternFly ModalHeader props)
-    <Modal
-      variant={ModalVariant.small}
-      isOpen={isOpen}
-      onClose={handleClose}
-      onEscapePress={handleClose}
-      aria-labelledby="modal-with-dropdown"
-    >
-      <ModalHeader title={t('Create Dashboard')} />
+    <Modal
+      variant={ModalVariant.small}
+      isOpen={isOpen}
+      onClose={handleClose}
+      onEscapePress={handleClose}
+      aria-labelledby="create-dashboard-modal-title"
+    >
+      <ModalHeader title={t('Create Dashboard')} labelId="create-dashboard-modal-title" />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/dashboards/perses/dashboard-create-dialog.tsx` around
lines 159 - 167, The Modal's aria-labelledby value ("modal-with-dropdown") does
not match any element id; update the ModalHeader used in
dashboard-create-dialog.tsx so its rendered title element has
id="modal-with-dropdown" (or remove the aria-labelledby prop and rely on
Modal/ModalHeader default labeling) — locate the Modal and ModalHeader
components in this file and either add the matching id to ModalHeader or delete
the aria-labelledby attribute from the Modal to satisfy accessibility
requirements.

32-40: ⚠️ Potential issue | 🟠 Major

Import React (or type-only equivalents) for React.FunctionComponent.

React.FunctionComponent requires the React namespace unless it’s globally declared; otherwise TS will fail.

🔧 Suggested fix
-import { useMemo, useState } from 'react';
+import React, { useMemo, useState } from 'react';
#!/bin/bash
# Check whether React is globally declared/allowed
rg -n "allowUmdGlobalAccess|jsx" web/tsconfig.json
rg -n "export as namespace React|namespace React|declare global" web/src -g'*.d.ts'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/dashboards/perses/dashboard-create-dialog.tsx` around
lines 32 - 40, The component is using the React namespace type
React.FunctionComponent but React isn't imported; either add an import to
satisfy the namespace (e.g. import React from 'react') or switch to a type-only
import and explicit type (e.g. import type { FunctionComponent } from 'react'
and annotate DashboardCreateDialog as
FunctionComponent<DashboardCreateDialogProps>); update the file so
DashboardCreateDialog and DashboardCreateDialogProps compile without relying on
a global React declaration.
web/src/components/dashboards/perses/dashboard-actions-menu.tsx (1)

1-49: ⚠️ Potential issue | 🟠 Major

Import React (or type-only equivalents) for React namespace types.

React.FunctionComponent and React.Ref<MenuToggleElement> require the React namespace unless it’s globally declared; otherwise this is a TS error.

🔧 Suggested fix
-import { useState } from 'react';
+import React, { useState } from 'react';
#!/bin/bash
# Check whether React is globally declared/allowed
rg -n "allowUmdGlobalAccess|jsx" web/tsconfig.json
rg -n "export as namespace React|namespace React|declare global" web/src -g'*.d.ts'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/dashboards/perses/dashboard-actions-menu.tsx` around lines
1 - 49, The TS build error comes from using React namespace types
(React.FunctionComponent and React.Ref<MenuToggleElement>) without importing
React; update the top of the DashboardActionsMenu file to import React (or
import type { Ref } from 'react' and use FunctionComponent from 'react' or use
type-only imports) so the symbols used in DashboardActionsMenu and the toggle
callback signature (React.Ref<MenuToggleElement>) resolve; ensure you either add
a default import like "import React from 'react'" or add appropriate type-only
imports to satisfy the compiler.
web/src/components/dashboards/perses/dashboard-import-dialog.tsx (3)

23-66: ⚠️ Potential issue | 🟠 Major

Import React (or type-only equivalents) for React.FunctionComponent.

React.FunctionComponent needs the React namespace unless it’s globally declared; otherwise TS will fail.

🔧 Suggested fix
-import { ChangeEvent, useMemo, useState } from 'react';
+import React, { ChangeEvent, useMemo, useState } from 'react';
#!/bin/bash
# Check whether React is globally declared/allowed
rg -n "allowUmdGlobalAccess|jsx" web/tsconfig.json
rg -n "export as namespace React|namespace React|declare global" web/src -g'*.d.ts'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/dashboards/perses/dashboard-import-dialog.tsx` around
lines 23 - 66, The file uses React.FunctionComponent for the
DashboardImportDialog type but never imports React, which breaks TypeScript
unless React's global namespace is declared; fix by adding an import: either
import React from 'react' (to provide the React namespace) or import the type
explicitly (import type { FunctionComponent } from 'react') and update the
component signature to use FunctionComponent<DashboardImportDialogProps> or
React.FC<DashboardImportDialogProps>; update the top imports accordingly where
DashboardImportDialog is declared to ensure the type is resolved.

307-315: ⚠️ Potential issue | 🟡 Minor

Ensure aria-labelledby points to an actual title element.

aria-labelledby="import-dashboard-modal" doesn’t match any element id in the modal. Wire it to the header’s id (or remove aria-labelledby and rely on default labeling).

♿ Suggested fix (verify PatternFly ModalHeader props)
-    <Modal
-      variant={ModalVariant.large}
-      isOpen={isOpen}
-      onClose={handleClose}
-      onEscapePress={handleClose}
-      aria-labelledby="import-dashboard-modal"
-    >
-      <ModalHeader title={t('Import Dashboard')} />
+    <Modal
+      variant={ModalVariant.large}
+      isOpen={isOpen}
+      onClose={handleClose}
+      onEscapePress={handleClose}
+      aria-labelledby="import-dashboard-modal-title"
+    >
+      <ModalHeader title={t('Import Dashboard')} labelId="import-dashboard-modal-title" />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/dashboards/perses/dashboard-import-dialog.tsx` around
lines 307 - 315, The Modal's aria-labelledby="import-dashboard-modal" does not
match any element id; either give the ModalHeader a matching id (e.g. add
id="import-dashboard-modal" to the ModalHeader component) or remove the
aria-labelledby prop so the Modal uses its default labeling. Update the
ModalHeader (component name: ModalHeader) to include the id that matches the
Modal's aria-labelledby value, or delete the aria-labelledby attribute on the
Modal component to resolve the mismatch.

246-273: ⚠️ Potential issue | 🟡 Minor

Surface Grafana migration failures via toast for consistency.

Other failure paths call addAlert, but migrate onError only sets inline errors, so users may miss it.

🔧 Suggested fix
             onError: (error) => {
               const errorMessage =
                 getErrorMessage(error) || t('Migration failed. Please try again.');
+              addAlert(
+                t('Error importing dashboard: {{error}}', { error: errorMessage }),
+                'danger',
+              );
               setFormErrors({ general: errorMessage });
             },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/dashboards/perses/dashboard-import-dialog.tsx` around
lines 246 - 273, The Grafana migration failure path only calls setFormErrors in
migrateMutation.mutate onError, so users don't get a toast; update the onError
handler for migrateMutation.mutate (invoked when currentParsedDashboard.kind ===
'grafana') to also call addAlert with a message built from
getErrorMessage(error) || t('Migration failed. Please try again.') (same style
used elsewhere) and keep the existing setFormErrors({ general: errorMessage })
behavior so both inline and toast notifications are shown.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@web/src/components/dashboards/perses/dashboard-actions-menu.tsx`:
- Around line 1-49: The TS build error comes from using React namespace types
(React.FunctionComponent and React.Ref<MenuToggleElement>) without importing
React; update the top of the DashboardActionsMenu file to import React (or
import type { Ref } from 'react' and use FunctionComponent from 'react' or use
type-only imports) so the symbols used in DashboardActionsMenu and the toggle
callback signature (React.Ref<MenuToggleElement>) resolve; ensure you either add
a default import like "import React from 'react'" or add appropriate type-only
imports to satisfy the compiler.

In `@web/src/components/dashboards/perses/dashboard-create-dialog.tsx`:
- Around line 159-167: The Modal's aria-labelledby value ("modal-with-dropdown")
does not match any element id; update the ModalHeader used in
dashboard-create-dialog.tsx so its rendered title element has
id="modal-with-dropdown" (or remove the aria-labelledby prop and rely on
Modal/ModalHeader default labeling) — locate the Modal and ModalHeader
components in this file and either add the matching id to ModalHeader or delete
the aria-labelledby attribute from the Modal to satisfy accessibility
requirements.
- Around line 32-40: The component is using the React namespace type
React.FunctionComponent but React isn't imported; either add an import to
satisfy the namespace (e.g. import React from 'react') or switch to a type-only
import and explicit type (e.g. import type { FunctionComponent } from 'react'
and annotate DashboardCreateDialog as
FunctionComponent<DashboardCreateDialogProps>); update the file so
DashboardCreateDialog and DashboardCreateDialogProps compile without relying on
a global React declaration.

In `@web/src/components/dashboards/perses/dashboard-import-dialog.tsx`:
- Around line 23-66: The file uses React.FunctionComponent for the
DashboardImportDialog type but never imports React, which breaks TypeScript
unless React's global namespace is declared; fix by adding an import: either
import React from 'react' (to provide the React namespace) or import the type
explicitly (import type { FunctionComponent } from 'react') and update the
component signature to use FunctionComponent<DashboardImportDialogProps> or
React.FC<DashboardImportDialogProps>; update the top imports accordingly where
DashboardImportDialog is declared to ensure the type is resolved.
- Around line 307-315: The Modal's aria-labelledby="import-dashboard-modal" does
not match any element id; either give the ModalHeader a matching id (e.g. add
id="import-dashboard-modal" to the ModalHeader component) or remove the
aria-labelledby prop so the Modal uses its default labeling. Update the
ModalHeader (component name: ModalHeader) to include the id that matches the
Modal's aria-labelledby value, or delete the aria-labelledby attribute on the
Modal component to resolve the mismatch.
- Around line 246-273: The Grafana migration failure path only calls
setFormErrors in migrateMutation.mutate onError, so users don't get a toast;
update the onError handler for migrateMutation.mutate (invoked when
currentParsedDashboard.kind === 'grafana') to also call addAlert with a message
built from getErrorMessage(error) || t('Migration failed. Please try again.')
(same style used elsewhere) and keep the existing setFormErrors({ general:
errorMessage }) behavior so both inline and toast notifications are shown.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between fc85fef and 8d6fef7.

⛔ Files ignored due to path filters (1)
  • web/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (8)
  • web/locales/en/plugin__monitoring-plugin.json
  • web/package.json
  • web/src/components/dashboards/perses/dashboard-actions-menu.tsx
  • web/src/components/dashboards/perses/dashboard-create-dialog.tsx
  • web/src/components/dashboards/perses/dashboard-header.tsx
  • web/src/components/dashboards/perses/dashboard-import-dialog.tsx
  • web/src/components/dashboards/perses/migrate-api.ts
  • web/src/components/data-test.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • web/src/components/dashboards/perses/migrate-api.ts
  • web/src/components/dashboards/perses/dashboard-header.tsx
  • web/src/components/data-test.ts

@etmurasaki
Copy link
Contributor

/label qe-approved

@openshift-ci openshift-ci bot added the qe-approved Signifies that QE has signed off on this PR label Feb 24, 2026
@openshift-ci-robot
Copy link

openshift-ci-robot commented Feb 24, 2026

@jgbernalp: This pull request references OU-1139 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the task to target the "4.22.0" version, but no target version was set.

Details

In response to this:

This PR adds the import button as part of a new menu which also includes the create option as default, following UX suggestions:

Screen.Recording.2026-02-24.at.13.00.03.mov

Summary by CodeRabbit

  • New Features

  • Dashboard import modal with editor + file upload, auto-detects format, supports Grafana→Perses migration, project selection, and import progress.

  • Dashboard actions menu with Create and Import; Create opens a dedicated modal with inline project/name form.

  • Bug Fixes / UX

  • Improved validation and clear feedback for invalid formats, file types, parsing/migration/import errors, and 5MB file-size limit.

  • Documentation

  • Expanded localization strings for project creation, import workflow, errors, and user guidance.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@etmurasaki
Copy link
Contributor

/test ?

@PeterYurkovich
Copy link
Contributor

/lgtm

@jgbernalp
Copy link
Contributor Author

/test e2e-monitoring

@openshift-ci openshift-ci bot added the lgtm Indicates that a PR is ready to be merged. label Feb 24, 2026
@openshift-ci
Copy link
Contributor

openshift-ci bot commented Feb 24, 2026

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: jgbernalp, PeterYurkovich

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:
  • OWNERS [PeterYurkovich,jgbernalp]

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@PeterYurkovich
Copy link
Contributor

/retest

@jgbernalp
Copy link
Contributor Author

/test e2e-monitoring

@jgbernalp
Copy link
Contributor Author

/override ci/prow/e2e-aws-ovn

@openshift-ci
Copy link
Contributor

openshift-ci bot commented Feb 25, 2026

@jgbernalp: Overrode contexts on behalf of jgbernalp: ci/prow/e2e-aws-ovn

Details

In response to this:

/override ci/prow/e2e-aws-ovn

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@openshift-ci
Copy link
Contributor

openshift-ci bot commented Feb 25, 2026

@jgbernalp: all tests passed!

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

@openshift-merge-bot openshift-merge-bot bot merged commit 2ce787b into openshift:main Feb 25, 2026
10 checks passed
@openshift-cherrypick-robot

@jgbernalp: #779 failed to apply on top of branch "release-coo-0.5":

Applying: feat: allow to import and migrate a dashboard
Using index info to reconstruct a base tree...
M	web/package-lock.json
M	web/package.json
Falling back to patching base and 3-way merge...
Auto-merging web/package.json
Auto-merging web/package-lock.json
CONFLICT (content): Merge conflict in web/package-lock.json
error: Failed to merge in the changes.
hint: Use 'git am --show-current-patch=diff' to see the failed patch
hint: When you have resolved this problem, run "git am --continue".
hint: If you prefer to skip this patch, run "git am --skip" instead.
hint: To restore the original branch and stop patching, run "git am --abort".
hint: Disable this message with "git config set advice.mergeConflict false"
Patch failed at 0001 feat: allow to import and migrate a dashboard

Details

In response to this:

/cherry-pick release-coo-0.5

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. lgtm Indicates that a PR is ready to be merged. qe-approved Signifies that QE has signed off on this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants