From 1ef5837835728deecc88a7ca41e7298116c7dec4 Mon Sep 17 00:00:00 2001 From: Gabriel Dugny Date: Sat, 11 Jul 2026 12:16:11 +0200 Subject: [PATCH 01/23] refactor(jira-dashboard): migrate MUI primitives to Backstage UI Swap direct @mui/material layout/typography primitives for their @backstage/ui equivalents: Typography -> Text, Stack -> Flex, Box -> Flex, Unstable_Grid2 -> Grid. Replace the @mui/styles makeStyles rule with a CSS module, dropping @mui/styles entirely. Divider and Chip stay on MUI (no BUI equivalent in 0.16), as do the @backstage/core-components (InfoCard, Table, Avatar, Link). Co-Authored-By: Claude Opus 4.8 (1M context) # Conflicts: # yarn.lock --- plugins/jira-dashboard/package.json | 2 +- .../JiraDashboardContent/JiraGrid.tsx | 24 ++---- .../JiraProjectCard.module.css | 4 + .../JiraProjectCard/JiraProjectCard.tsx | 24 +++--- .../JiraProjectCard/ProjectInfoLabel.tsx | 14 ++-- .../src/components/JiraTable/JiraTable.tsx | 18 ++-- .../JiraTable/cells/AssigneeCell.tsx | 26 +++--- .../src/components/JiraTable/columns.tsx | 20 ++--- yarn.lock | 82 +++++++++---------- 9 files changed, 96 insertions(+), 118 deletions(-) create mode 100644 plugins/jira-dashboard/src/components/JiraProjectCard/JiraProjectCard.module.css diff --git a/plugins/jira-dashboard/package.json b/plugins/jira-dashboard/package.json index bb58e874..15a5fe1f 100644 --- a/plugins/jira-dashboard/package.json +++ b/plugins/jira-dashboard/package.json @@ -52,8 +52,8 @@ "@backstage/frontend-plugin-api": "^0.17.3", "@backstage/plugin-catalog-react": "^3.2.0", "@backstage/theme": "^0.7.3", + "@backstage/ui": "^0.16.0", "@mui/material": "^5.15.7", - "@mui/styles": "^5.15.7", "lodash": "^4.18.1", "luxon": "^3.5.0", "react-use": "^17.2.4" diff --git a/plugins/jira-dashboard/src/components/JiraDashboardContent/JiraGrid.tsx b/plugins/jira-dashboard/src/components/JiraDashboardContent/JiraGrid.tsx index d4246b67..4879dc2b 100644 --- a/plugins/jira-dashboard/src/components/JiraDashboardContent/JiraGrid.tsx +++ b/plugins/jira-dashboard/src/components/JiraDashboardContent/JiraGrid.tsx @@ -1,4 +1,4 @@ -import Grid from '@mui/material/Grid'; +import { Grid } from '@backstage/ui'; import { JiraProjectCard } from '../JiraProjectCard'; import { JiraTable } from '../JiraTable'; import { @@ -22,24 +22,18 @@ export const JiraGrid = ({ tableOptions, }: JiraGridProps) => { return ( - - + - + {tableData.map((value: JiraDataResponse) => ( - - + ))} - + ); }; diff --git a/plugins/jira-dashboard/src/components/JiraProjectCard/JiraProjectCard.module.css b/plugins/jira-dashboard/src/components/JiraProjectCard/JiraProjectCard.module.css new file mode 100644 index 00000000..494a99f9 --- /dev/null +++ b/plugins/jira-dashboard/src/components/JiraProjectCard/JiraProjectCard.module.css @@ -0,0 +1,4 @@ +/* Disable default paragraph margins inside the markdown description. */ +.markdown p { + margin: 0; +} diff --git a/plugins/jira-dashboard/src/components/JiraProjectCard/JiraProjectCard.tsx b/plugins/jira-dashboard/src/components/JiraProjectCard/JiraProjectCard.tsx index 69a42e1b..a6c97c85 100644 --- a/plugins/jira-dashboard/src/components/JiraProjectCard/JiraProjectCard.tsx +++ b/plugins/jira-dashboard/src/components/JiraProjectCard/JiraProjectCard.tsx @@ -1,11 +1,15 @@ import Divider from '@mui/material/Divider'; -import Stack from '@mui/material/Stack'; -import Typography from '@mui/material/Typography'; -import { Avatar, InfoCard } from '@backstage/core-components'; -import { LinkButton } from '@backstage/core-components'; +import { Flex, Text } from '@backstage/ui'; +import { + Avatar, + InfoCard, + MarkdownContent, + LinkButton, +} from '@backstage/core-components'; import { Project } from '@axis-backstage/plugin-jira-dashboard-common'; import { ProjectInfoLabel } from './ProjectInfoLabel'; import { getProjectUrl } from '../../lib'; +import styles from './JiraProjectCard.module.css'; type JiraProjectCardProps = { project: Project; @@ -14,7 +18,7 @@ type JiraProjectCardProps = { export const JiraProjectCard = ({ project }: JiraProjectCardProps) => { return ( - + { }} /> - + {project.name} | {project.projectTypeKey ?? ''} - - + + - + {project.projectCategory?.name && ( { value={project?.lead?.displayName || project?.lead?.key} /> )} - + { return ( - theme.palette.text.disabled }} - > + {label} - - + + {value} - + ); }; diff --git a/plugins/jira-dashboard/src/components/JiraTable/JiraTable.tsx b/plugins/jira-dashboard/src/components/JiraTable/JiraTable.tsx index fd1e59b2..6984795a 100644 --- a/plugins/jira-dashboard/src/components/JiraTable/JiraTable.tsx +++ b/plugins/jira-dashboard/src/components/JiraTable/JiraTable.tsx @@ -1,5 +1,5 @@ import { CSSProperties } from 'react'; -import Typography from '@mui/material/Typography'; +import { Flex, Text } from '@backstage/ui'; import { Issue, JiraDataResponse, @@ -74,9 +74,9 @@ export const JiraTable = ({ } } let title = ( - + {`${capitalize(tableContent.name)} (${nbrOfIssues})`} - + ); if (project && tableContent.query) { @@ -114,9 +114,9 @@ export const JiraTable = ({ }} filters={filters} emptyContent={ - - No issues found  - + + No issues found  + } data={tableContent.issues || []} columns={tableColumns} @@ -137,9 +137,9 @@ export const JiraTable = ({ }} filters={filters} emptyContent={ - - No issues found  - + + No issues found  + } data={tableContent.issues || []} columns={tableColumns} diff --git a/plugins/jira-dashboard/src/components/JiraTable/cells/AssigneeCell.tsx b/plugins/jira-dashboard/src/components/JiraTable/cells/AssigneeCell.tsx index 64acd9c3..d450e664 100644 --- a/plugins/jira-dashboard/src/components/JiraTable/cells/AssigneeCell.tsx +++ b/plugins/jira-dashboard/src/components/JiraTable/cells/AssigneeCell.tsx @@ -1,6 +1,5 @@ import { Avatar, Link } from '@backstage/core-components'; -import Typography from '@mui/material/Typography'; -import Stack from '@mui/material/Stack'; +import { Flex, Text } from '@backstage/ui'; import { Issue } from '@axis-backstage/plugin-jira-dashboard-common'; import { EntityPeekAheadPopover } from '@backstage/plugin-catalog-react'; import { stringifyEntityRef } from '@backstage/catalog-model'; @@ -24,33 +23,28 @@ export const AssigneeCell = ({ assignee }: Props) => { const name = assignee.name || assignee.displayName; if (!name || name.toLowerCase() === 'unassigned') { return ( - - Unassigned - + Unassigned + ); } const avatar = ( - + - + {assignee.displayName || assignee.name} - - + + ); if (assignee.name) { diff --git a/plugins/jira-dashboard/src/components/JiraTable/columns.tsx b/plugins/jira-dashboard/src/components/JiraTable/columns.tsx index 4d32028e..337db8e1 100644 --- a/plugins/jira-dashboard/src/components/JiraTable/columns.tsx +++ b/plugins/jira-dashboard/src/components/JiraTable/columns.tsx @@ -1,9 +1,8 @@ import { Link, TableColumn } from '@backstage/core-components'; import { Issue } from '@axis-backstage/plugin-jira-dashboard-common'; -import Typography from '@mui/material/Typography'; +import { Flex, Text } from '@backstage/ui'; import { getIssueUrl } from '../../lib'; import Chip from '@mui/material/Chip'; -import Box from '@mui/material/Box'; import { DateTime } from 'luxon'; import { AssigneeCell } from './cells/AssigneeCell'; @@ -22,19 +21,14 @@ export const columnKey: TableColumn = { } return ( - + {issue.fields?.issuetype.name} {issue.key} - + ); }, @@ -147,13 +141,9 @@ export const columnUpdated: TableColumn = { render: (issue: Partial) => { if (issue.fields?.updated) { return ( - theme.palette.text.secondary }} - color="divider" - variant="body2" - > + {DateTime.fromISO(issue.fields.updated).toFormat('dd/MMM/yy')} - + ); } return null; diff --git a/yarn.lock b/yarn.lock index 329d9ad8..ba51123f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1949,8 +1949,8 @@ __metadata: "@backstage/plugin-catalog-react": "npm:^3.2.0" "@backstage/test-utils": "npm:^1.7.20" "@backstage/theme": "npm:^0.7.3" + "@backstage/ui": "npm:^0.16.0" "@mui/material": "npm:^5.15.7" - "@mui/styles": "npm:^5.15.7" "@testing-library/dom": "npm:^10.0.0" "@testing-library/jest-dom": "npm:^6.0.0" "@testing-library/react": "npm:^16.0.0" @@ -5177,6 +5177,33 @@ __metadata: languageName: node linkType: hard +"@backstage/ui@npm:^0.16.0": + version: 0.16.0 + resolution: "@backstage/ui@npm:0.16.0" + dependencies: + "@backstage/version-bridge": "npm:^1.0.12" + "@braintree/sanitize-url": "npm:^7.1.2" + "@internationalized/date": "npm:^3.12.0" + "@remixicon/react": "npm:>=4.6.0 <4.9.0" + "@tanstack/react-table": "npm:^8.21.3" + clsx: "npm:^2.1.1" + marked: "npm:^15.0.12" + react-aria: "npm:~3.48.0" + react-aria-components: "npm:~1.17.0" + react-stately: "npm:~3.46.0" + use-sync-external-store: "npm:^1.4.0" + peerDependencies: + "@types/react": ^18.0.0 + react: ^18.0.0 + react-dom: ^18.0.0 + react-router-dom: ^6.30.2 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/7444482fc6d5866682e4fa69ea9a6e5c6e561b2bfc71e3f7397f651c78ccbd83fe09b0aa0010ae70a91bf377b36082c2636cf77891b6d88e6572c9acbca57758 + languageName: node + linkType: hard + "@backstage/ui@npm:^0.17.0": version: 0.17.0 resolution: "@backstage/ui@npm:0.17.0" @@ -7969,7 +7996,7 @@ __metadata: languageName: node linkType: hard -"@mui/private-theming@npm:^5.15.20, @mui/private-theming@npm:^5.15.7": +"@mui/private-theming@npm:^5.15.20": version: 5.15.20 resolution: "@mui/private-theming@npm:5.15.20" dependencies: @@ -8007,37 +8034,6 @@ __metadata: languageName: node linkType: hard -"@mui/styles@npm:^5.15.7": - version: 5.15.7 - resolution: "@mui/styles@npm:5.15.7" - dependencies: - "@babel/runtime": "npm:^7.23.9" - "@emotion/hash": "npm:^0.9.1" - "@mui/private-theming": "npm:^5.15.7" - "@mui/types": "npm:^7.2.13" - "@mui/utils": "npm:^5.15.7" - clsx: "npm:^2.1.0" - csstype: "npm:^3.1.2" - hoist-non-react-statics: "npm:^3.3.2" - jss: "npm:^10.10.0" - jss-plugin-camel-case: "npm:^10.10.0" - jss-plugin-default-unit: "npm:^10.10.0" - jss-plugin-global: "npm:^10.10.0" - jss-plugin-nested: "npm:^10.10.0" - jss-plugin-props-sort: "npm:^10.10.0" - jss-plugin-rule-value-function: "npm:^10.10.0" - jss-plugin-vendor-prefixer: "npm:^10.10.0" - prop-types: "npm:^15.8.1" - peerDependencies: - "@types/react": ^17.0.0 || ^18.0.0 - react: ^17.0.0 - peerDependenciesMeta: - "@types/react": - optional: true - checksum: 10c0/6da21d2ca3ecb0496ca6bb3fcf0e58d23612b8bf71d919e18cbd6cb1a9fd6e2b19fcf183d40d2398f0014705b223520df13e8287ccc001b69b6c62a6bcc256ea - languageName: node - linkType: hard - "@mui/system@npm:^5.15.20": version: 5.15.20 resolution: "@mui/system@npm:5.15.20" @@ -8066,7 +8062,7 @@ __metadata: languageName: node linkType: hard -"@mui/types@npm:^7.2.13, @mui/types@npm:^7.2.14": +"@mui/types@npm:^7.2.14": version: 7.2.14 resolution: "@mui/types@npm:7.2.14" peerDependencies: @@ -8078,7 +8074,7 @@ __metadata: languageName: node linkType: hard -"@mui/utils@npm:^5.14.15, @mui/utils@npm:^5.15.14, @mui/utils@npm:^5.15.20, @mui/utils@npm:^5.15.7": +"@mui/utils@npm:^5.14.15, @mui/utils@npm:^5.15.14, @mui/utils@npm:^5.15.20": version: 5.15.20 resolution: "@mui/utils@npm:5.15.20" dependencies: @@ -23281,7 +23277,7 @@ __metadata: languageName: node linkType: hard -"jss-plugin-camel-case@npm:^10.10.0, jss-plugin-camel-case@npm:^10.5.1": +"jss-plugin-camel-case@npm:^10.5.1": version: 10.10.0 resolution: "jss-plugin-camel-case@npm:10.10.0" dependencies: @@ -23292,7 +23288,7 @@ __metadata: languageName: node linkType: hard -"jss-plugin-default-unit@npm:^10.10.0, jss-plugin-default-unit@npm:^10.5.1": +"jss-plugin-default-unit@npm:^10.5.1": version: 10.10.0 resolution: "jss-plugin-default-unit@npm:10.10.0" dependencies: @@ -23302,7 +23298,7 @@ __metadata: languageName: node linkType: hard -"jss-plugin-global@npm:^10.10.0, jss-plugin-global@npm:^10.5.1": +"jss-plugin-global@npm:^10.5.1": version: 10.10.0 resolution: "jss-plugin-global@npm:10.10.0" dependencies: @@ -23312,7 +23308,7 @@ __metadata: languageName: node linkType: hard -"jss-plugin-nested@npm:^10.10.0, jss-plugin-nested@npm:^10.5.1": +"jss-plugin-nested@npm:^10.5.1": version: 10.10.0 resolution: "jss-plugin-nested@npm:10.10.0" dependencies: @@ -23323,7 +23319,7 @@ __metadata: languageName: node linkType: hard -"jss-plugin-props-sort@npm:^10.10.0, jss-plugin-props-sort@npm:^10.5.1": +"jss-plugin-props-sort@npm:^10.5.1": version: 10.10.0 resolution: "jss-plugin-props-sort@npm:10.10.0" dependencies: @@ -23333,7 +23329,7 @@ __metadata: languageName: node linkType: hard -"jss-plugin-rule-value-function@npm:^10.10.0, jss-plugin-rule-value-function@npm:^10.5.1": +"jss-plugin-rule-value-function@npm:^10.5.1": version: 10.10.0 resolution: "jss-plugin-rule-value-function@npm:10.10.0" dependencies: @@ -23344,7 +23340,7 @@ __metadata: languageName: node linkType: hard -"jss-plugin-vendor-prefixer@npm:^10.10.0, jss-plugin-vendor-prefixer@npm:^10.5.1": +"jss-plugin-vendor-prefixer@npm:^10.5.1": version: 10.10.0 resolution: "jss-plugin-vendor-prefixer@npm:10.10.0" dependencies: @@ -23355,7 +23351,7 @@ __metadata: languageName: node linkType: hard -"jss@npm:10.10.0, jss@npm:^10.10.0, jss@npm:^10.5.1, jss@npm:~10.10.0": +"jss@npm:10.10.0, jss@npm:^10.5.1, jss@npm:~10.10.0": version: 10.10.0 resolution: "jss@npm:10.10.0" dependencies: From 59c8e7f666f5525e92560517526dcd994b71d470 Mon Sep 17 00:00:00 2001 From: Gabriel Dugny Date: Sat, 11 Jul 2026 12:37:24 +0200 Subject: [PATCH 02/23] refactor(jira-dashboard): migrate Tabs, Chip, and LinkButton to Backstage UI Replace three more core-components/MUI usages with @backstage/ui: - TabbedCard/CardTab -> Card + Tabs/TabList/Tab/TabPanel - Chip -> Badge (non-interactive label) - LinkButton -> ButtonLink columns.tsx is now free of @mui/material. Only Divider remains on MUI (no BUI equivalent in 0.16). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../JiraDashboardContent.tsx | 50 +++++++++++++------ .../JiraProjectCard/JiraProjectCard.tsx | 17 ++----- .../src/components/JiraTable/columns.tsx | 13 +---- 3 files changed, 42 insertions(+), 38 deletions(-) diff --git a/plugins/jira-dashboard/src/components/JiraDashboardContent/JiraDashboardContent.tsx b/plugins/jira-dashboard/src/components/JiraDashboardContent/JiraDashboardContent.tsx index 49610372..dd212654 100644 --- a/plugins/jira-dashboard/src/components/JiraDashboardContent/JiraDashboardContent.tsx +++ b/plugins/jira-dashboard/src/components/JiraDashboardContent/JiraDashboardContent.tsx @@ -3,9 +3,17 @@ import { ResponseErrorPanel, TableFilter, TableOptions, - TabbedCard, - CardTab, } from '@backstage/core-components'; +import { + Card, + CardBody, + CardHeader, + Tab, + TabList, + TabPanel, + Tabs, + Text, +} from '@backstage/ui'; import { useApi } from '@backstage/core-plugin-api'; import { useEntity } from '@backstage/plugin-catalog-react'; import { stringifyEntityRef } from '@backstage/catalog-model'; @@ -44,18 +52,32 @@ export const JiraDashboardContent = (props?: { : []; return projects.length > 1 ? ( - - {projects.map(project => ( - - - - ))} - + + + Jira Projects + + + + + {projects.map(project => ( + + {project.name} + + ))} + + {projects.map(project => ( + + + + ))} + + + ) : ( { /> )} - + Go to project - + ); }; diff --git a/plugins/jira-dashboard/src/components/JiraTable/columns.tsx b/plugins/jira-dashboard/src/components/JiraTable/columns.tsx index 337db8e1..347162dc 100644 --- a/plugins/jira-dashboard/src/components/JiraTable/columns.tsx +++ b/plugins/jira-dashboard/src/components/JiraTable/columns.tsx @@ -1,8 +1,7 @@ import { Link, TableColumn } from '@backstage/core-components'; import { Issue } from '@axis-backstage/plugin-jira-dashboard-common'; -import { Flex, Text } from '@backstage/ui'; +import { Badge, Flex, Text } from '@backstage/ui'; import { getIssueUrl } from '../../lib'; -import Chip from '@mui/material/Chip'; import { DateTime } from 'luxon'; import { AssigneeCell } from './cells/AssigneeCell'; @@ -100,15 +99,7 @@ export const columnStatus: TableColumn = { } return ( - + {issue.fields?.status.name} ); }, From 4a6f0cac654dceeb42d11ae34998b789af68335c Mon Sep 17 00:00:00 2001 From: Gabriel Dugny Date: Sat, 11 Jul 2026 12:58:39 +0200 Subject: [PATCH 03/23] refactor(jira-dashboard): migrate InfoCard to BUI EntityInfoCard Replace core-components InfoCard with @backstage/plugin-catalog-react's EntityInfoCard (itself built on @backstage/ui Card). fullHeight is baked into EntityInfoCard, so the variant prop is dropped. In JiraTable the title switches to as="span" so it nests validly inside EntityInfoCard's header; the always-undefined headerStyle is dropped. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../JiraProjectCard/JiraProjectCard.tsx | 7 ++++--- .../src/components/JiraTable/JiraTable.tsx | 15 +++++++-------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/plugins/jira-dashboard/src/components/JiraProjectCard/JiraProjectCard.tsx b/plugins/jira-dashboard/src/components/JiraProjectCard/JiraProjectCard.tsx index 7997119f..caf121df 100644 --- a/plugins/jira-dashboard/src/components/JiraProjectCard/JiraProjectCard.tsx +++ b/plugins/jira-dashboard/src/components/JiraProjectCard/JiraProjectCard.tsx @@ -1,6 +1,7 @@ import Divider from '@mui/material/Divider'; import { ButtonLink, Flex, Text } from '@backstage/ui'; -import { Avatar, InfoCard, MarkdownContent } from '@backstage/core-components'; +import { Avatar, MarkdownContent } from '@backstage/core-components'; +import { EntityInfoCard } from '@backstage/plugin-catalog-react'; import { Project } from '@axis-backstage/plugin-jira-dashboard-common'; import { ProjectInfoLabel } from './ProjectInfoLabel'; import { getProjectUrl } from '../../lib'; @@ -12,7 +13,7 @@ type JiraProjectCardProps = { export const JiraProjectCard = ({ project }: JiraProjectCardProps) => { return ( - + { Go to project - + ); }; diff --git a/plugins/jira-dashboard/src/components/JiraTable/JiraTable.tsx b/plugins/jira-dashboard/src/components/JiraTable/JiraTable.tsx index 6984795a..0857c159 100644 --- a/plugins/jira-dashboard/src/components/JiraTable/JiraTable.tsx +++ b/plugins/jira-dashboard/src/components/JiraTable/JiraTable.tsx @@ -7,13 +7,13 @@ import { } from '@axis-backstage/plugin-jira-dashboard-common'; import { ErrorPanel, - InfoCard, Link, Table, TableColumn, TableFilter, TableOptions, } from '@backstage/core-components'; +import { EntityInfoCard } from '@backstage/plugin-catalog-react'; import { capitalize } from 'lodash'; import { columns } from './columns'; import { getJiraBaseUrl, transformAssignees } from '../../lib'; @@ -26,10 +26,9 @@ type Props = { tableColumns?: TableColumn[]; tableStyle?: TableComponentProps['style']; /** - * CSS styles to apply to the top-most element, being the table component or - * the wrapping InfoCard component if filters are shown. - * If no filters are shown, the style prop is merged with the tableStyle prop - * and used in the table component. + * CSS styles merged into the table component's style (after tableStyle). + * Only applied when filters are not shown; with filters the table is wrapped + * in an EntityInfoCard and this is ignored. */ style?: CSSProperties; showFilters?: TableFilter[] | boolean; @@ -74,7 +73,7 @@ export const JiraTable = ({ } } let title = ( - + {`${capitalize(tableContent.name)} (${nbrOfIssues})`} ); @@ -104,7 +103,7 @@ export const JiraTable = ({ if (showFilters) { return ( - + options={{ paging: false, @@ -122,7 +121,7 @@ export const JiraTable = ({ columns={tableColumns} style={baseTableStyle} /> - + ); } From b111d438fa98ddaa818243ff01e50ef50fa0b46b Mon Sep 17 00:00:00 2001 From: Gabriel Dugny Date: Sat, 11 Jul 2026 13:00:24 +0200 Subject: [PATCH 04/23] refactor(jira-dashboard): migrate JiraUserIssuesCard to EntityInfoCard Replace core-components InfoCard with EntityInfoCard, rendering the deepLink as a footerActions ButtonLink (variant tertiary) per the upstream pattern (see catalog-graph CatalogGraphCard). fullHeight is built in. Drops BottomLinkProps.onClick forwarding: react-aria ButtonLink's onClick uses a broader FocusableElement event type, incompatible with BottomLinkProps' HTMLAnchorElement signature, and no in-repo caller uses it. The href (the essential deep-link behavior) is preserved. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../JiraUserIssuesCard/JiraUserIssuesCard.tsx | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/plugins/jira-dashboard/src/components/JiraUserIssuesCard/JiraUserIssuesCard.tsx b/plugins/jira-dashboard/src/components/JiraUserIssuesCard/JiraUserIssuesCard.tsx index 9ff93094..2ac15336 100644 --- a/plugins/jira-dashboard/src/components/JiraUserIssuesCard/JiraUserIssuesCard.tsx +++ b/plugins/jira-dashboard/src/components/JiraUserIssuesCard/JiraUserIssuesCard.tsx @@ -1,8 +1,6 @@ -import { - BottomLinkProps, - InfoCard, - TableOptions, -} from '@backstage/core-components'; +import { BottomLinkProps, TableOptions } from '@backstage/core-components'; +import { ButtonLink } from '@backstage/ui'; +import { EntityInfoCard } from '@backstage/plugin-catalog-react'; import { JiraUserIssuesTable, @@ -44,13 +42,22 @@ export const JiraUserIssuesCard = ({ filterName, }: JiraUserIssuesCardProps) => { return ( - + + {bottomLinkProps.title} + + ) + } + > - + ); }; From 8901958c18f53ae6d8c29fedbd7619d4014df7cd Mon Sep 17 00:00:00 2001 From: Gabriel Dugny Date: Sat, 11 Jul 2026 13:02:46 +0200 Subject: [PATCH 05/23] refactor(jira-dashboard): migrate Link to BUI Link Swap core-components Link for @backstage/ui Link across the table cells, assignee cell, and table title: to -> href, and the title's variant="h5" -> variant="title-small" (matching the adjacent non-link title). Internal catalog hrefs still client-navigate via the app's react-aria router; external Jira hrefs render plain anchors. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../src/components/JiraTable/JiraTable.tsx | 9 ++++---- .../JiraTable/cells/AssigneeCell.tsx | 6 +++--- .../src/components/JiraTable/columns.tsx | 21 +++++++++++++------ 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/plugins/jira-dashboard/src/components/JiraTable/JiraTable.tsx b/plugins/jira-dashboard/src/components/JiraTable/JiraTable.tsx index 0857c159..d9e392be 100644 --- a/plugins/jira-dashboard/src/components/JiraTable/JiraTable.tsx +++ b/plugins/jira-dashboard/src/components/JiraTable/JiraTable.tsx @@ -1,5 +1,5 @@ import { CSSProperties } from 'react'; -import { Flex, Text } from '@backstage/ui'; +import { Flex, Link, Text } from '@backstage/ui'; import { Issue, JiraDataResponse, @@ -7,7 +7,6 @@ import { } from '@axis-backstage/plugin-jira-dashboard-common'; import { ErrorPanel, - Link, Table, TableColumn, TableFilter, @@ -81,8 +80,10 @@ export const JiraTable = ({ if (project && tableContent.query) { title = ( {`${capitalize(tableContent.name)} (${nbrOfIssues})`} diff --git a/plugins/jira-dashboard/src/components/JiraTable/cells/AssigneeCell.tsx b/plugins/jira-dashboard/src/components/JiraTable/cells/AssigneeCell.tsx index d450e664..dabd7460 100644 --- a/plugins/jira-dashboard/src/components/JiraTable/cells/AssigneeCell.tsx +++ b/plugins/jira-dashboard/src/components/JiraTable/cells/AssigneeCell.tsx @@ -1,5 +1,5 @@ -import { Avatar, Link } from '@backstage/core-components'; -import { Flex, Text } from '@backstage/ui'; +import { Avatar } from '@backstage/core-components'; +import { Flex, Link, Text } from '@backstage/ui'; import { Issue } from '@axis-backstage/plugin-jira-dashboard-common'; import { EntityPeekAheadPopover } from '@backstage/plugin-catalog-react'; import { stringifyEntityRef } from '@backstage/catalog-model'; @@ -57,7 +57,7 @@ export const AssigneeCell = ({ assignee }: Props) => { return ( {avatar} diff --git a/plugins/jira-dashboard/src/components/JiraTable/columns.tsx b/plugins/jira-dashboard/src/components/JiraTable/columns.tsx index 347162dc..bfc89574 100644 --- a/plugins/jira-dashboard/src/components/JiraTable/columns.tsx +++ b/plugins/jira-dashboard/src/components/JiraTable/columns.tsx @@ -1,6 +1,6 @@ -import { Link, TableColumn } from '@backstage/core-components'; +import { TableColumn } from '@backstage/core-components'; import { Issue } from '@axis-backstage/plugin-jira-dashboard-common'; -import { Badge, Flex, Text } from '@backstage/ui'; +import { Badge, Flex, Link, Text } from '@backstage/ui'; import { getIssueUrl } from '../../lib'; import { DateTime } from 'luxon'; import { AssigneeCell } from './cells/AssigneeCell'; @@ -19,7 +19,10 @@ export const columnKey: TableColumn = { return null; } return ( - + = { return ( {issue.fields?.summary} @@ -73,7 +76,10 @@ export const columnPriority: TableColumn = { return null; } return ( - + {issue.fields?.priority?.name} = { return null; } return ( - + {issue.fields?.status.name} ); From f5a64dee280e8e1b01c82d727cc34ee1e1da5479 Mon Sep 17 00:00:00 2001 From: Gabriel Dugny Date: Sat, 11 Jul 2026 13:06:16 +0200 Subject: [PATCH 06/23] style(jira-dashboard): make BUI links standalone (no resting underline) Set standalone on the migrated BUI links (issue-key/summary/priority/ status cells, assignee link, table-title link) so they drop the default underline at rest and only underline on hover. Co-Authored-By: Claude Opus 4.8 (1M context) --- plugins/jira-dashboard/src/components/JiraTable/JiraTable.tsx | 1 + .../src/components/JiraTable/cells/AssigneeCell.tsx | 1 + plugins/jira-dashboard/src/components/JiraTable/columns.tsx | 4 ++++ 3 files changed, 6 insertions(+) diff --git a/plugins/jira-dashboard/src/components/JiraTable/JiraTable.tsx b/plugins/jira-dashboard/src/components/JiraTable/JiraTable.tsx index d9e392be..bfda3778 100644 --- a/plugins/jira-dashboard/src/components/JiraTable/JiraTable.tsx +++ b/plugins/jira-dashboard/src/components/JiraTable/JiraTable.tsx @@ -85,6 +85,7 @@ export const JiraTable = ({ }`} variant="title-small" data-testid="table-header" + standalone > {`${capitalize(tableContent.name)} (${nbrOfIssues})`} diff --git a/plugins/jira-dashboard/src/components/JiraTable/cells/AssigneeCell.tsx b/plugins/jira-dashboard/src/components/JiraTable/cells/AssigneeCell.tsx index dabd7460..3889c03a 100644 --- a/plugins/jira-dashboard/src/components/JiraTable/cells/AssigneeCell.tsx +++ b/plugins/jira-dashboard/src/components/JiraTable/cells/AssigneeCell.tsx @@ -58,6 +58,7 @@ export const AssigneeCell = ({ assignee }: Props) => { {avatar} diff --git a/plugins/jira-dashboard/src/components/JiraTable/columns.tsx b/plugins/jira-dashboard/src/components/JiraTable/columns.tsx index bfc89574..3e717b3d 100644 --- a/plugins/jira-dashboard/src/components/JiraTable/columns.tsx +++ b/plugins/jira-dashboard/src/components/JiraTable/columns.tsx @@ -22,6 +22,7 @@ export const columnKey: TableColumn = { = { style={{ lineHeight: 1.5 }} href={getIssueUrl(issue.self, issue.key)} title="Go to issue in Jira" + standalone > {issue.fields?.summary} @@ -79,6 +81,7 @@ export const columnPriority: TableColumn = { {issue.fields?.priority?.name} = { {issue.fields?.status.name} From 2882324c34bbf3ab6688d52e97be231b0bfec98b Mon Sep 17 00:00:00 2001 From: Gabriel Dugny Date: Sat, 11 Jul 2026 13:06:41 +0200 Subject: [PATCH 07/23] chore(jira-dashboard): expand dev app to cover all cards and states Rework the dev app into navigable pages so the migrated components can be reviewed visually: multi-project (tabbed), single-project, and Jira Cloud dashboards; JiraTable with filters / without / empty; JiraProjectCard full vs minimal; JiraUserIssuesCard with and without a deep-link footer plus the standalone table. A single mock API dispatches per entity ref so all dashboard variants coexist. Co-Authored-By: Claude Opus 4.8 (1M context) --- plugins/jira-dashboard/dev/index.tsx | 216 ++++++++++++++++++++++++--- 1 file changed, 195 insertions(+), 21 deletions(-) diff --git a/plugins/jira-dashboard/dev/index.tsx b/plugins/jira-dashboard/dev/index.tsx index 3939e331..b0af60ec 100644 --- a/plugins/jira-dashboard/dev/index.tsx +++ b/plugins/jira-dashboard/dev/index.tsx @@ -1,37 +1,211 @@ +import { ReactNode } from 'react'; import { createDevApp } from '@backstage/dev-utils'; import { EntityJiraDashboardContent, jiraDashboardPlugin } from '../src'; -import { jiraDashboardApiRef } from '../src/api'; -import { Page, Content } from '@backstage/core-components'; +import { JiraDashboardApi, jiraDashboardApiRef } from '../src/api'; +import { JiraDashboardContent } from '../src/components/JiraDashboardContent'; +import { JiraProjectCard } from '../src/components/JiraProjectCard'; +import { JiraTable } from '../src/components/JiraTable'; +import { JiraUserIssuesCard } from '../src/components/JiraUserIssuesCard'; +import { JiraUserIssuesTable } from '../src/components/JiraUserIssuesTable'; +import { Content, Page } from '@backstage/core-components'; import { EntityProvider } from '@backstage/plugin-catalog-react'; -import mockEntity from './__fixtures__/entity.json'; -import mockJiraResponse from './__fixtures__/jiraResponse.json'; -/* Use to test with Jira Cloud +import { Entity, stringifyEntityRef } from '@backstage/catalog-model'; +import { Flex, Text } from '@backstage/ui'; +import { + Issue, + JiraDataResponse, + JiraResponse, + Project, +} from '@axis-backstage/plugin-jira-dashboard-common'; +import multiProjectResponse from './__fixtures__/jiraResponse.json'; +import singleProjectResponse from './__fixtures__/singleProjectResponse.json'; +import cloudResponse from './__fixtures__/jiraCloudResponse.json'; +import multiProjectEntity from './__fixtures__/entity.json'; -import mockJiraCloudResponse from './__fixtures__/jiraCloudResponse.json'; -*/ +// One entity per dashboard variant. The mock API returns a different fixture +// per entity ref, so all three dashboards can coexist in the same dev app. +const singleProjectEntity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'single-project-service', + description: 'Entity backed by a single Jira Server project', + annotations: { 'jira.com/project-key': 'BS' }, + }, + spec: { lifecycle: 'production', type: 'service', owner: 'user:guest' }, +}; + +const cloudProjectEntity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'jenkins-meeting-actions', + description: 'Entity backed by a Jira Cloud project', + annotations: { 'jira.com/project-key': 'MEETING' }, + }, + spec: { lifecycle: 'production', type: 'service', owner: 'user:guest' }, +}; + +// Fixtures are intentionally partial vs the full Jira schema, hence the casts. +const responsesByEntityRef: Record = { + [stringifyEntityRef(multiProjectEntity as Entity)]: + multiProjectResponse as unknown as JiraResponse, + [stringifyEntityRef(singleProjectEntity)]: + singleProjectResponse as unknown as JiraResponse, + [stringifyEntityRef(cloudProjectEntity)]: + cloudResponse as unknown as JiraResponse, +}; + +const userIssues = (singleProjectResponse.data[0].issues ?? + []) as unknown as Issue[]; + +const mockJiraDashboardApi: JiraDashboardApi = { + getJiraResponseByEntity: async entityRef => + responsesByEntityRef[entityRef] ?? + (multiProjectResponse as unknown as JiraResponse), + getLoggedInUserIssues: async () => userIssues, + getProjectAvatar: async () => undefined, +}; + +// Standalone fixtures for the component-in-isolation pages. +const sampleProject = singleProjectResponse.project as Project; +const sampleDataGroup = singleProjectResponse + .data[0] as unknown as JiraDataResponse; +const emptyDataGroup: JiraDataResponse = { + name: 'No open issues', + type: 'filter', + query: 'resolution = Unresolved ORDER BY updated DESC', + issues: [], +}; +const minimalProject: Project = { + name: 'Minimal Project', + key: 'MIN', + description: '', + avatarUrls: { + '48x48': 'https://api.dicebear.com/6.x/open-peeps/svg?seed=Min', + }, + projectTypeKey: '', + lead: { key: '', displayName: '' }, + self: 'https://jira.com/project/999', +}; + +function DevPage(props: { children: ReactNode }) { + return ( + + {props.children} + + ); +} + +function Section(props: { title: string; children: ReactNode }) { + return ( + + {props.title} + {props.children} + + ); +} createDevApp() .registerPlugin(jiraDashboardPlugin) .registerApi({ api: jiraDashboardApiRef, deps: {}, - factory: () => - ({ - getJiraResponseByEntity: async () => mockJiraResponse, - getProjectAvatar: async () => mockJiraResponse, - } as any), + factory: () => mockJiraDashboardApi, }) .addPage({ + title: 'Dashboard: Multi-project', + path: '/jira-dashboard', element: ( - - - - - - - + + + + + + ), + }) + .addPage({ + title: 'Dashboard: Single project', + path: '/jira-dashboard-single', + element: ( + + + + + + ), + }) + .addPage({ + title: 'Dashboard: Jira Cloud', + path: '/jira-dashboard-cloud', + element: ( + + + + + + ), + }) + .addPage({ + title: 'JiraTable states', + path: '/jira-table', + element: ( + +
+ +
+
+ +
+
+ +
+
+ ), + }) + .addPage({ + title: 'JiraProjectCard', + path: '/jira-project-card', + element: ( + +
+ +
+
+ +
+
+ ), + }) + .addPage({ + title: 'JiraUserIssuesCard', + path: '/jira-user-issues', + element: ( + +
+ +
+
+ +
+
+ +
+
), - title: 'Root Page', - path: '/jira-dashboard', }) .render(); From d47a8912a43a02e28f25ec5f576ea960eb1d78b6 Mon Sep 17 00:00:00 2001 From: Gabriel Dugny Date: Sat, 11 Jul 2026 13:15:39 +0200 Subject: [PATCH 08/23] refactor(jira-dashboard): move JiraProjectCard avatar/title into card header Put the project avatar + name in the EntityInfoCard title (BUI Avatar, size small, decorative) following the upstream org-card pattern (UserProfileCard/GroupProfileCard). Removes the MUI Divider: BUI has no divider component, and the header/body split is the intended separation, consistent with the plugin's other EntityInfoCard cards. This drops the last @mui/material import from shipped code. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../JiraProjectCard/JiraProjectCard.tsx | 37 +++++++++---------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/plugins/jira-dashboard/src/components/JiraProjectCard/JiraProjectCard.tsx b/plugins/jira-dashboard/src/components/JiraProjectCard/JiraProjectCard.tsx index caf121df..2b269681 100644 --- a/plugins/jira-dashboard/src/components/JiraProjectCard/JiraProjectCard.tsx +++ b/plugins/jira-dashboard/src/components/JiraProjectCard/JiraProjectCard.tsx @@ -1,6 +1,5 @@ -import Divider from '@mui/material/Divider'; -import { ButtonLink, Flex, Text } from '@backstage/ui'; -import { Avatar, MarkdownContent } from '@backstage/core-components'; +import { Avatar, ButtonLink, Flex, Text } from '@backstage/ui'; +import { MarkdownContent } from '@backstage/core-components'; import { EntityInfoCard } from '@backstage/plugin-catalog-react'; import { Project } from '@axis-backstage/plugin-jira-dashboard-common'; import { ProjectInfoLabel } from './ProjectInfoLabel'; @@ -13,22 +12,22 @@ type JiraProjectCardProps = { export const JiraProjectCard = ({ project }: JiraProjectCardProps) => { return ( - - - - - - {project.name} | {project.projectTypeKey ?? ''} - - - - + + + + {`${project.name} | ${project.projectTypeKey ?? ''}`} + + + } + > + {project.projectCategory?.name && ( Date: Sat, 11 Jul 2026 13:17:43 +0200 Subject: [PATCH 09/23] chore(jira-dashboard): drop @mui/material dependency The migration removed all @mui/material usage from shipped code; the only remaining reference was a non-load-bearing MUI ThemeProvider wrapper in AssigneeCell.test.tsx. Simplify that render helper to MemoryRouter only and remove @mui/material from the plugin. The plugin is now MUI-free (only Divider had kept it; that moved to the card header). Co-Authored-By: Claude Opus 4.8 (1M context) --- plugins/jira-dashboard/package.json | 1 - .../src/components/JiraTable/cells/AssigneeCell.test.tsx | 7 +------ yarn.lock | 1 - 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/plugins/jira-dashboard/package.json b/plugins/jira-dashboard/package.json index 15a5fe1f..730f6d21 100644 --- a/plugins/jira-dashboard/package.json +++ b/plugins/jira-dashboard/package.json @@ -53,7 +53,6 @@ "@backstage/plugin-catalog-react": "^3.2.0", "@backstage/theme": "^0.7.3", "@backstage/ui": "^0.16.0", - "@mui/material": "^5.15.7", "lodash": "^4.18.1", "luxon": "^3.5.0", "react-use": "^17.2.4" diff --git a/plugins/jira-dashboard/src/components/JiraTable/cells/AssigneeCell.test.tsx b/plugins/jira-dashboard/src/components/JiraTable/cells/AssigneeCell.test.tsx index c3280bf4..39361990 100644 --- a/plugins/jira-dashboard/src/components/JiraTable/cells/AssigneeCell.test.tsx +++ b/plugins/jira-dashboard/src/components/JiraTable/cells/AssigneeCell.test.tsx @@ -1,6 +1,5 @@ import { render, screen } from '@testing-library/react'; import { AssigneeCell } from './AssigneeCell'; -import { ThemeProvider, createTheme } from '@mui/material/styles'; import { MemoryRouter } from 'react-router-dom'; import { Issue } from '@axis-backstage/plugin-jira-dashboard-common'; @@ -11,11 +10,7 @@ jest.mock('@backstage/plugin-catalog-react', () => ({ })); const renderWithProviders = (ui: React.ReactElement) => - render( - - {ui} - , - ); + render({ui}); describe('AssigneeCell', () => { const baseAssignee: Issue['fields']['assignee'] = { diff --git a/yarn.lock b/yarn.lock index ba51123f..de5c1d62 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1950,7 +1950,6 @@ __metadata: "@backstage/test-utils": "npm:^1.7.20" "@backstage/theme": "npm:^0.7.3" "@backstage/ui": "npm:^0.16.0" - "@mui/material": "npm:^5.15.7" "@testing-library/dom": "npm:^10.0.0" "@testing-library/jest-dom": "npm:^6.0.0" "@testing-library/react": "npm:^16.0.0" From f7b2e409d79d498bf7bf0f58b27063ec5f90beeb Mon Sep 17 00:00:00 2001 From: Gabriel Dugny Date: Sat, 11 Jul 2026 13:26:09 +0200 Subject: [PATCH 10/23] fix(jira-dashboard): drop title separator when project has no type The project card title rendered "Name | " with a trailing separator when projectTypeKey was empty. Only append " | " when a type is present. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../src/components/JiraProjectCard/JiraProjectCard.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/jira-dashboard/src/components/JiraProjectCard/JiraProjectCard.tsx b/plugins/jira-dashboard/src/components/JiraProjectCard/JiraProjectCard.tsx index 2b269681..47da0c72 100644 --- a/plugins/jira-dashboard/src/components/JiraProjectCard/JiraProjectCard.tsx +++ b/plugins/jira-dashboard/src/components/JiraProjectCard/JiraProjectCard.tsx @@ -22,7 +22,9 @@ export const JiraProjectCard = ({ project }: JiraProjectCardProps) => { src={project.avatarUrls['48x48'] || ''} /> - {`${project.name} | ${project.projectTypeKey ?? ''}`} + {project.projectTypeKey + ? `${project.name} | ${project.projectTypeKey}` + : project.name} } From e620cc56521d48e6ccf308ba5ee786c476a0d8c5 Mon Sep 17 00:00:00 2001 From: Gabriel Dugny Date: Sat, 11 Jul 2026 13:56:02 +0200 Subject: [PATCH 11/23] fix(jira-dashboard): open external Jira links in a new tab The Link/ButtonLink migration to @backstage/ui dropped the automatic target="_blank" rel="noopener" that core-components applied to external (http/https) links, so every outbound Jira link (issue cells, JQL header link, "Go to project", user-issues footer) navigated the current tab and dropped the user out of Backstage. Restore new-tab behavior explicitly. The internal catalog link in AssigneeCell stays in-tab (SPA nav). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../src/components/JiraProjectCard/JiraProjectCard.tsx | 7 ++++++- .../jira-dashboard/src/components/JiraTable/JiraTable.tsx | 2 ++ .../jira-dashboard/src/components/JiraTable/columns.tsx | 8 ++++++++ .../components/JiraUserIssuesCard/JiraUserIssuesCard.tsx | 7 ++++++- 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/plugins/jira-dashboard/src/components/JiraProjectCard/JiraProjectCard.tsx b/plugins/jira-dashboard/src/components/JiraProjectCard/JiraProjectCard.tsx index 47da0c72..1addfdf3 100644 --- a/plugins/jira-dashboard/src/components/JiraProjectCard/JiraProjectCard.tsx +++ b/plugins/jira-dashboard/src/components/JiraProjectCard/JiraProjectCard.tsx @@ -47,7 +47,12 @@ export const JiraProjectCard = ({ project }: JiraProjectCardProps) => { /> )}
- + Go to project diff --git a/plugins/jira-dashboard/src/components/JiraTable/JiraTable.tsx b/plugins/jira-dashboard/src/components/JiraTable/JiraTable.tsx index bfda3778..031839ef 100644 --- a/plugins/jira-dashboard/src/components/JiraTable/JiraTable.tsx +++ b/plugins/jira-dashboard/src/components/JiraTable/JiraTable.tsx @@ -86,6 +86,8 @@ export const JiraTable = ({ variant="title-small" data-testid="table-header" standalone + target="_blank" + rel="noopener noreferrer" > {`${capitalize(tableContent.name)} (${nbrOfIssues})`} diff --git a/plugins/jira-dashboard/src/components/JiraTable/columns.tsx b/plugins/jira-dashboard/src/components/JiraTable/columns.tsx index 3e717b3d..6851c203 100644 --- a/plugins/jira-dashboard/src/components/JiraTable/columns.tsx +++ b/plugins/jira-dashboard/src/components/JiraTable/columns.tsx @@ -23,6 +23,8 @@ export const columnKey: TableColumn = { href={getIssueUrl(issue.self, issue.key)} title="Go to issue in Jira" standalone + target="_blank" + rel="noopener noreferrer" > = { href={getIssueUrl(issue.self, issue.key)} title="Go to issue in Jira" standalone + target="_blank" + rel="noopener noreferrer" > {issue.fields?.summary} @@ -82,6 +86,8 @@ export const columnPriority: TableColumn = { href={getIssueUrl(issue.self, issue.key)} title="Go to issue in Jira" standalone + target="_blank" + rel="noopener noreferrer" > {issue.fields?.priority?.name} = { href={getIssueUrl(issue.self, issue.key)} title="Go to issue in Jira" standalone + target="_blank" + rel="noopener noreferrer" > {issue.fields?.status.name} diff --git a/plugins/jira-dashboard/src/components/JiraUserIssuesCard/JiraUserIssuesCard.tsx b/plugins/jira-dashboard/src/components/JiraUserIssuesCard/JiraUserIssuesCard.tsx index 2ac15336..846b96b4 100644 --- a/plugins/jira-dashboard/src/components/JiraUserIssuesCard/JiraUserIssuesCard.tsx +++ b/plugins/jira-dashboard/src/components/JiraUserIssuesCard/JiraUserIssuesCard.tsx @@ -46,7 +46,12 @@ export const JiraUserIssuesCard = ({ title={title} footerActions={ bottomLinkProps && ( - + {bottomLinkProps.title} ) From be3817d6bb25f2d4fb24603348b26f6096b7a205 Mon Sep 17 00:00:00 2001 From: Gabriel Dugny Date: Sat, 11 Jul 2026 13:56:53 +0200 Subject: [PATCH 12/23] fix(jira-dashboard): restore JiraProjectCard title weight and avatar size The title text was wrapped in a bare that defaulted to body-medium (14px regular), overriding EntityInfoCard's

styling, so the project title rendered smaller/lighter than its sibling cards. Set it to title-x-small bold to match. Bump the header avatar from small (24px) to large (40px); the original was 48px and small was too faint beside the title. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../src/components/JiraProjectCard/JiraProjectCard.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/jira-dashboard/src/components/JiraProjectCard/JiraProjectCard.tsx b/plugins/jira-dashboard/src/components/JiraProjectCard/JiraProjectCard.tsx index 1addfdf3..48b7f86f 100644 --- a/plugins/jira-dashboard/src/components/JiraProjectCard/JiraProjectCard.tsx +++ b/plugins/jira-dashboard/src/components/JiraProjectCard/JiraProjectCard.tsx @@ -16,12 +16,12 @@ export const JiraProjectCard = ({ project }: JiraProjectCardProps) => { title={ - + {project.projectTypeKey ? `${project.name} | ${project.projectTypeKey}` : project.name} From 7e5a11d3dfbb1f4d0e792cdca28891f3bd073cb5 Mon Sep 17 00:00:00 2001 From: Gabriel Dugny Date: Sat, 11 Jul 2026 14:29:41 +0200 Subject: [PATCH 13/23] style(jira-dashboard): match tabbed card header to EntityInfoCard title The hand-rolled CardHeader for the "Jira Projects" tabbed card used title-small regular; align it with EntityInfoCard's title typography (Text as="h3" variant="title-x-small" weight="bold") so custom card headers read consistently with the entity cards. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../components/JiraDashboardContent/JiraDashboardContent.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/jira-dashboard/src/components/JiraDashboardContent/JiraDashboardContent.tsx b/plugins/jira-dashboard/src/components/JiraDashboardContent/JiraDashboardContent.tsx index dd212654..224ff21f 100644 --- a/plugins/jira-dashboard/src/components/JiraDashboardContent/JiraDashboardContent.tsx +++ b/plugins/jira-dashboard/src/components/JiraDashboardContent/JiraDashboardContent.tsx @@ -54,7 +54,9 @@ export const JiraDashboardContent = (props?: { return projects.length > 1 ? ( - Jira Projects + + Jira Projects + From f14caf55758556efaa5d34329237db40642c40ba Mon Sep 17 00:00:00 2001 From: Gabriel Dugny Date: Sat, 11 Jul 2026 14:33:49 +0200 Subject: [PATCH 14/23] refactor(jira-dashboard): use EntityInfoCard for the tabbed projects card Replace the hand-rolled Card + CardHeader + CardBody + Text header with EntityInfoCard title="Jira Projects". Same titled-card surface with the title typography matched by construction, consistent with every other card. Drops the untested tabbed-card data-testid. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../JiraDashboardContent.tsx | 58 +++++++------------ 1 file changed, 21 insertions(+), 37 deletions(-) diff --git a/plugins/jira-dashboard/src/components/JiraDashboardContent/JiraDashboardContent.tsx b/plugins/jira-dashboard/src/components/JiraDashboardContent/JiraDashboardContent.tsx index 224ff21f..da0fec14 100644 --- a/plugins/jira-dashboard/src/components/JiraDashboardContent/JiraDashboardContent.tsx +++ b/plugins/jira-dashboard/src/components/JiraDashboardContent/JiraDashboardContent.tsx @@ -4,18 +4,9 @@ import { TableFilter, TableOptions, } from '@backstage/core-components'; -import { - Card, - CardBody, - CardHeader, - Tab, - TabList, - TabPanel, - Tabs, - Text, -} from '@backstage/ui'; +import { Tab, TabList, TabPanel, Tabs } from '@backstage/ui'; import { useApi } from '@backstage/core-plugin-api'; -import { useEntity } from '@backstage/plugin-catalog-react'; +import { EntityInfoCard, useEntity } from '@backstage/plugin-catalog-react'; import { stringifyEntityRef } from '@backstage/catalog-model'; import { jiraDashboardApiRef } from '../../api'; import { useJira } from '../../hooks/useJira'; @@ -52,34 +43,27 @@ export const JiraDashboardContent = (props?: { : []; return projects.length > 1 ? ( - - - - Jira Projects - - - - - - {projects.map(project => ( - - {project.name} - - ))} - + + + {projects.map(project => ( - - - + + {project.name} + ))} - - - + + {projects.map(project => ( + + + + ))} + + ) : ( Date: Sat, 1 Aug 2026 10:20:45 +0200 Subject: [PATCH 15/23] fixup: markdown --- .../src/components/JiraProjectCard/JiraProjectCard.module.css | 4 ---- .../src/components/JiraProjectCard/JiraProjectCard.tsx | 2 -- 2 files changed, 6 deletions(-) delete mode 100644 plugins/jira-dashboard/src/components/JiraProjectCard/JiraProjectCard.module.css diff --git a/plugins/jira-dashboard/src/components/JiraProjectCard/JiraProjectCard.module.css b/plugins/jira-dashboard/src/components/JiraProjectCard/JiraProjectCard.module.css deleted file mode 100644 index 494a99f9..00000000 --- a/plugins/jira-dashboard/src/components/JiraProjectCard/JiraProjectCard.module.css +++ /dev/null @@ -1,4 +0,0 @@ -/* Disable default paragraph margins inside the markdown description. */ -.markdown p { - margin: 0; -} diff --git a/plugins/jira-dashboard/src/components/JiraProjectCard/JiraProjectCard.tsx b/plugins/jira-dashboard/src/components/JiraProjectCard/JiraProjectCard.tsx index 48b7f86f..1b00725b 100644 --- a/plugins/jira-dashboard/src/components/JiraProjectCard/JiraProjectCard.tsx +++ b/plugins/jira-dashboard/src/components/JiraProjectCard/JiraProjectCard.tsx @@ -1,10 +1,8 @@ import { Avatar, ButtonLink, Flex, Text } from '@backstage/ui'; -import { MarkdownContent } from '@backstage/core-components'; import { EntityInfoCard } from '@backstage/plugin-catalog-react'; import { Project } from '@axis-backstage/plugin-jira-dashboard-common'; import { ProjectInfoLabel } from './ProjectInfoLabel'; import { getProjectUrl } from '../../lib'; -import styles from './JiraProjectCard.module.css'; type JiraProjectCardProps = { project: Project; From c68f5e0be9525938c7e68f7dcb08610f84f17dee Mon Sep 17 00:00:00 2001 From: Gabriel Dugny Date: Sat, 1 Aug 2026 10:30:49 +0200 Subject: [PATCH 16/23] chore: changeset --- .changeset/tall-pianos-lick.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .changeset/tall-pianos-lick.md diff --git a/.changeset/tall-pianos-lick.md b/.changeset/tall-pianos-lick.md new file mode 100644 index 00000000..40268fb8 --- /dev/null +++ b/.changeset/tall-pianos-lick.md @@ -0,0 +1,9 @@ +--- +'@axis-backstage/plugin-jira-dashboard': major +--- + +Migrate Jira Dashboard to Backstage UI. Cards, tabs, links, now use Backstage UI components. + +The migration removes the `@mui/material` and `@mui/styles` direct dependencies, but keeping using MUI tables. + +**Breaking:** Consumers must install and configure `@backstage/ui`. From 27ce3c0681434867a11e318d8a5af94b3afc1cc3 Mon Sep 17 00:00:00 2001 From: Gabriel Dugny Date: Sat, 1 Aug 2026 10:36:04 +0200 Subject: [PATCH 17/23] fix: use upstream entity title with avatar pattern --- .../JiraProjectCard/JiraProjectCard.tsx | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/plugins/jira-dashboard/src/components/JiraProjectCard/JiraProjectCard.tsx b/plugins/jira-dashboard/src/components/JiraProjectCard/JiraProjectCard.tsx index 1b00725b..cdc071ab 100644 --- a/plugins/jira-dashboard/src/components/JiraProjectCard/JiraProjectCard.tsx +++ b/plugins/jira-dashboard/src/components/JiraProjectCard/JiraProjectCard.tsx @@ -1,4 +1,4 @@ -import { Avatar, ButtonLink, Flex, Text } from '@backstage/ui'; +import { Avatar, ButtonLink, Flex } from '@backstage/ui'; import { EntityInfoCard } from '@backstage/plugin-catalog-react'; import { Project } from '@axis-backstage/plugin-jira-dashboard-common'; import { ProjectInfoLabel } from './ProjectInfoLabel'; @@ -8,23 +8,27 @@ type JiraProjectCardProps = { project: Project; }; +const CardTitle = (props: { title: string; pictureSrc?: string }) => ( + + + {props.title} + +); + export const JiraProjectCard = ({ project }: JiraProjectCardProps) => { + const title = project.projectTypeKey + ? `${project.name} | ${project.projectTypeKey}` + : project.name; + return ( - - - {project.projectTypeKey - ? `${project.name} | ${project.projectTypeKey}` - : project.name} - - + } > From a290879a21aa650dcf20f1cc1d583fe4c23a25d8 Mon Sep 17 00:00:00 2001 From: Gabriel Dugny Date: Sat, 1 Aug 2026 10:54:35 +0200 Subject: [PATCH 18/23] fix: Avatar from Jira are 48px, so use BUI 24px small size --- .../components/JiraTable/cells/AssigneeCell.tsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/plugins/jira-dashboard/src/components/JiraTable/cells/AssigneeCell.tsx b/plugins/jira-dashboard/src/components/JiraTable/cells/AssigneeCell.tsx index 3889c03a..34125ffb 100644 --- a/plugins/jira-dashboard/src/components/JiraTable/cells/AssigneeCell.tsx +++ b/plugins/jira-dashboard/src/components/JiraTable/cells/AssigneeCell.tsx @@ -1,5 +1,4 @@ -import { Avatar } from '@backstage/core-components'; -import { Flex, Link, Text } from '@backstage/ui'; +import { Avatar, Flex, Link, Text } from '@backstage/ui'; import { Issue } from '@axis-backstage/plugin-jira-dashboard-common'; import { EntityPeekAheadPopover } from '@backstage/plugin-catalog-react'; import { stringifyEntityRef } from '@backstage/catalog-model'; @@ -29,20 +28,23 @@ export const AssigneeCell = ({ assignee }: Props) => { align="center" data-testid="assignee-avatar" > - + Unassigned ); } + const displayName = assignee.displayName || assignee.name || ''; const avatar = ( - {assignee.displayName || assignee.name} + {displayName} ); From 4423e8492f9b29f07ad1e90ff728096385940509 Mon Sep 17 00:00:00 2001 From: Gabriel Dugny Date: Sat, 1 Aug 2026 11:11:36 +0200 Subject: [PATCH 19/23] fix: remove jira project title from card when multiple projects (already in tab) --- .../components/JiraDashboardContent/JiraDashboardContent.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/jira-dashboard/src/components/JiraDashboardContent/JiraDashboardContent.tsx b/plugins/jira-dashboard/src/components/JiraDashboardContent/JiraDashboardContent.tsx index da0fec14..80852a5d 100644 --- a/plugins/jira-dashboard/src/components/JiraDashboardContent/JiraDashboardContent.tsx +++ b/plugins/jira-dashboard/src/components/JiraDashboardContent/JiraDashboardContent.tsx @@ -43,7 +43,7 @@ export const JiraDashboardContent = (props?: { : []; return projects.length > 1 ? ( - + {projects.map(project => ( From 8e163866a096af5a4a5bfbd1090d993e4d8e38ce Mon Sep 17 00:00:00 2001 From: Gabriel Dugny Date: Tue, 4 Aug 2026 12:20:10 +0200 Subject: [PATCH 20/23] chore: bui 0.17 --- plugins/jira-dashboard/package.json | 2 +- yarn.lock | 29 +---------------------------- 2 files changed, 2 insertions(+), 29 deletions(-) diff --git a/plugins/jira-dashboard/package.json b/plugins/jira-dashboard/package.json index 730f6d21..eaae8465 100644 --- a/plugins/jira-dashboard/package.json +++ b/plugins/jira-dashboard/package.json @@ -52,7 +52,7 @@ "@backstage/frontend-plugin-api": "^0.17.3", "@backstage/plugin-catalog-react": "^3.2.0", "@backstage/theme": "^0.7.3", - "@backstage/ui": "^0.16.0", + "@backstage/ui": "^0.17.0", "lodash": "^4.18.1", "luxon": "^3.5.0", "react-use": "^17.2.4" diff --git a/yarn.lock b/yarn.lock index de5c1d62..6b584e2a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1949,7 +1949,7 @@ __metadata: "@backstage/plugin-catalog-react": "npm:^3.2.0" "@backstage/test-utils": "npm:^1.7.20" "@backstage/theme": "npm:^0.7.3" - "@backstage/ui": "npm:^0.16.0" + "@backstage/ui": "npm:^0.17.0" "@testing-library/dom": "npm:^10.0.0" "@testing-library/jest-dom": "npm:^6.0.0" "@testing-library/react": "npm:^16.0.0" @@ -5176,33 +5176,6 @@ __metadata: languageName: node linkType: hard -"@backstage/ui@npm:^0.16.0": - version: 0.16.0 - resolution: "@backstage/ui@npm:0.16.0" - dependencies: - "@backstage/version-bridge": "npm:^1.0.12" - "@braintree/sanitize-url": "npm:^7.1.2" - "@internationalized/date": "npm:^3.12.0" - "@remixicon/react": "npm:>=4.6.0 <4.9.0" - "@tanstack/react-table": "npm:^8.21.3" - clsx: "npm:^2.1.1" - marked: "npm:^15.0.12" - react-aria: "npm:~3.48.0" - react-aria-components: "npm:~1.17.0" - react-stately: "npm:~3.46.0" - use-sync-external-store: "npm:^1.4.0" - peerDependencies: - "@types/react": ^18.0.0 - react: ^18.0.0 - react-dom: ^18.0.0 - react-router-dom: ^6.30.2 - peerDependenciesMeta: - "@types/react": - optional: true - checksum: 10c0/7444482fc6d5866682e4fa69ea9a6e5c6e561b2bfc71e3f7397f651c78ccbd83fe09b0aa0010ae70a91bf377b36082c2636cf77891b6d88e6572c9acbca57758 - languageName: node - linkType: hard - "@backstage/ui@npm:^0.17.0": version: 0.17.0 resolution: "@backstage/ui@npm:0.17.0" From fb2db395c3906e172114e8bcc55b89e2677598bd Mon Sep 17 00:00:00 2001 From: Gabriel Dugny Date: Tue, 4 Aug 2026 13:45:18 +0200 Subject: [PATCH 21/23] fix: key column padding and icon size --- plugins/jira-dashboard/src/components/JiraTable/columns.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/jira-dashboard/src/components/JiraTable/columns.tsx b/plugins/jira-dashboard/src/components/JiraTable/columns.tsx index 6851c203..ed6683ca 100644 --- a/plugins/jira-dashboard/src/components/JiraTable/columns.tsx +++ b/plugins/jira-dashboard/src/components/JiraTable/columns.tsx @@ -30,7 +30,7 @@ export const columnKey: TableColumn = { {issue.fields?.issuetype.name} {issue.key} From a3933a5cf32c300a7a45fcdffb2147f56e8012ec Mon Sep 17 00:00:00 2001 From: Gabriel Dugny Date: Tue, 4 Aug 2026 13:50:20 +0200 Subject: [PATCH 22/23] fix: consistent table title with filters on --- .../jira-dashboard/src/components/JiraTable/JiraTable.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/jira-dashboard/src/components/JiraTable/JiraTable.tsx b/plugins/jira-dashboard/src/components/JiraTable/JiraTable.tsx index 031839ef..f4c371c7 100644 --- a/plugins/jira-dashboard/src/components/JiraTable/JiraTable.tsx +++ b/plugins/jira-dashboard/src/components/JiraTable/JiraTable.tsx @@ -72,7 +72,7 @@ export const JiraTable = ({ } } let title = ( - + {`${capitalize(tableContent.name)} (${nbrOfIssues})`} ); @@ -83,7 +83,8 @@ export const JiraTable = ({ href={`${getJiraBaseUrl(project.self)}/issues/?jql=${ tableContent.query }`} - variant="title-small" + variant="title-x-small" + weight="bold" data-testid="table-header" standalone target="_blank" From 0d592b20c80887d90bd11fb2fa4e3d0f190ed8d4 Mon Sep 17 00:00:00 2001 From: Gabriel Dugny Date: Thu, 6 Aug 2026 14:38:50 +0200 Subject: [PATCH 23/23] chore: add BUI styles to app --- packages/app/src/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/app/src/index.tsx b/packages/app/src/index.tsx index af62933f..2fa3b95f 100644 --- a/packages/app/src/index.tsx +++ b/packages/app/src/index.tsx @@ -1,4 +1,5 @@ import '@backstage/cli/asset-types'; +import '@backstage/ui/css/styles.css'; import ReactDOM from 'react-dom/client'; import App from './App';