Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions workspaces/servicenow/.changeset/cruel-nails-happen.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@backstage-community/plugin-servicenow': major
---

The ServiceNow plugin is actively transitioning from Material-UI (MUI) to Backstage UI (BUI). Progress includes: migrated layout components using BUI's Box and Text, replaced MUI icons with Remix Icons, and implemented CSS Modules for styling with proper spacing (24px 16px 24px 20px) and alternating row backgrounds. Remaining work involves replacing MUI Table components (TableBody, TableRow, TableCell, TablePagination, TableSortLabel) and Form components (Autocomplete, Checkbox, TextField). Once completed, the plugin will achieve full BUI compliance, removing all MUI dependencies while maintaining functionality and design consistency.
17 changes: 8 additions & 9 deletions workspaces/servicenow/plugins/servicenow/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,17 +55,17 @@
"@backstage/frontend-plugin-api": "^0.16.2",
"@backstage/plugin-catalog-react": "^2.1.4",
"@backstage/theme": "^0.7.3",
"@mui/icons-material": "^5.15.17",
"@mui/lab": "^5.0.0-alpha.153",
"@mui/material": "^5.17.1",
"@mui/styles": "5.18.0",
"react-dom": "^16.13.1 || ^17.0.0 || ^18.0.0",
"@backstage/ui": "^0.15.0",
"@mui/material": "^9.1.1",
"@remixicon/react": "^4.7.0",
"react-aria-components": "^1.4.0",
"react-dom": "^18.0.0",
"react-router-dom": "^6.0.0",
"react-use": "^17.2.4"
},
"peerDependencies": {
"react": "^16.13.1 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.13.1 || ^17.0.0 || ^18.0.0",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"react-router-dom": "^6.0.0"
},
"devDependencies": {
Expand All @@ -75,14 +75,13 @@
"@backstage/dev-utils": "^1.1.22",
"@backstage/frontend-test-utils": "^0.5.2",
"@backstage/test-utils": "^1.7.17",
"@material-ui/core": "^4.12.4",
"@playwright/test": "1.60.0",
"@testing-library/dom": "^10.4.1",
"@testing-library/jest-dom": "^6.0.0",
"@testing-library/react": "^16.0.0",
"@testing-library/user-event": "^14.0.0",
"msw": "^1.0.0",
"react": "^16.13.1 || ^17.0.0 || ^18.0.0",
"react": "^18.0.0",
"rxjs": "^7.8.2"
},
"files": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,22 @@
* limitations under the License.
*/

import SvgIcon from '@mui/material/SvgIcon';

export const CustomSvgIcon = ({
path,
viewBox = '0 0 24 24',
size = 16,
style = {},
...props
}: any) => {
return (
<SvgIcon viewBox={viewBox} {...props}>
<svg
viewBox={viewBox}
width={size}
height={size}
style={{ display: 'inline-block', ...style }}
{...props}
>
{path}
</SvgIcon>
</svg>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
@layer components {
.errorContainer {
padding: var(--bui-space-4);
color: var(--bui-status-error);
}

.emptyContent {
padding: var(--bui-space-4);
display: flex;
justify-content: center;
}

.loadingRow {
display: flex;
align-items: center;
justify-content: center;
padding: var(--bui-space-6);
}

.tableRowGroup {
display: table-row-group;
}

.tableRow {
display: table-row;
}

.tableCellLoading {
display: table-cell;
padding: 24px 16px 24px 20px;
text-align: center;
width: 100%;
}

.paginationContainer {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 16px;
margin-top: 24px;
}

.rowsPerPageSelect {
padding: 4px 8px;
border-radius: 4px;
border: none;
background-color: transparent;
color: var(--bui-fg-primary);
font-size: 0.875rem;
cursor: pointer;
}

.paginationInfo {
font-size: 0.875rem;
color: var(--bui-fg-secondary);
}

.paginationButtons {
display: flex;
gap: 8px;
}

.tableHeader {
display: flex;
justify-content: space-between;
align-items: flex-end;
margin-bottom: 24px;
}

.titleHeading {
margin: 0;
}

.searchContainer {
display: flex;
align-items: center;
gap: 8px;
padding-bottom: 8px;
border-bottom: 2px solid #e0e0e0;
position: relative;
}

.searchInput {
padding: 0 8px 4px 0;
border: none;
background: transparent;
font-size: 0.875rem;
outline: none;
color: var(--bui-fg-primary);
min-width: 200px;
}

.clearSearchButton {
position: absolute;
right: 0;
background: none;
border: none;
cursor: pointer;
padding: 4px;
display: flex;
align-items: center;
justify-content: center;
color: var(--bui-fg-secondary);
}

.table {
display: table;
width: 100%;
border-collapse: collapse;
border-radius: 4px;
overflow: hidden;
border: 1px solid #e0e0e0;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ import userEvent from '@testing-library/user-event';

import { MemoryRouter } from 'react-router-dom';

import { ThemeProvider, createTheme } from '@material-ui/core';

import { of } from 'rxjs';

import { serviceNowApiRef } from '../../api/ServiceNowBackendClient';
Expand Down Expand Up @@ -87,8 +85,6 @@ const mockTranslationApi = {
),
};

const theme = createTheme();

describe('ServicenowContent', () => {
const mockServiceNowApi = {
getIncidents: jest.fn(),
Expand All @@ -105,22 +101,20 @@ describe('ServicenowContent', () => {
it('renders the table with incident rows', async () => {
render(
<MemoryRouter>
<ThemeProvider theme={theme}>
<TestApiProvider
apis={[
[serviceNowApiRef, mockServiceNowApi],
[catalogApiRef, mockCatalogApi],
[identityApiRef, mockIdentityApi],
[alertApiRef, mockAlertApi],
[errorApiRef, mockErrorApi],
[translationApiRef, mockTranslationApi as any],
]}
>
<EntityProvider entity={mockEntity}>
<EntityServicenowContent />
</EntityProvider>
</TestApiProvider>
</ThemeProvider>
<TestApiProvider
apis={[
[serviceNowApiRef, mockServiceNowApi],
[catalogApiRef, mockCatalogApi],
[identityApiRef, mockIdentityApi],
[alertApiRef, mockAlertApi],
[errorApiRef, mockErrorApi],
[translationApiRef, mockTranslationApi as any],
]}
>
<EntityProvider entity={mockEntity}>
<EntityServicenowContent />
</EntityProvider>
</TestApiProvider>
</MemoryRouter>,
);

Expand Down Expand Up @@ -149,22 +143,20 @@ describe('ServicenowContent', () => {
it('displays pagination dropdown', async () => {
render(
<MemoryRouter>
<ThemeProvider theme={theme}>
<TestApiProvider
apis={[
[serviceNowApiRef, mockServiceNowApi],
[catalogApiRef, mockCatalogApi],
[identityApiRef, mockIdentityApi],
[alertApiRef, mockAlertApi],
[errorApiRef, mockErrorApi],
[translationApiRef, mockTranslationApi as any],
]}
>
<EntityProvider entity={mockEntity}>
<EntityServicenowContent />
</EntityProvider>
</TestApiProvider>
</ThemeProvider>
<TestApiProvider
apis={[
[serviceNowApiRef, mockServiceNowApi],
[catalogApiRef, mockCatalogApi],
[identityApiRef, mockIdentityApi],
[alertApiRef, mockAlertApi],
[errorApiRef, mockErrorApi],
[translationApiRef, mockTranslationApi as any],
]}
>
<EntityProvider entity={mockEntity}>
<EntityServicenowContent />
</EntityProvider>
</TestApiProvider>
</MemoryRouter>,
);

Expand All @@ -191,22 +183,20 @@ describe('ServicenowContent', () => {

render(
<MemoryRouter>
<ThemeProvider theme={theme}>
<TestApiProvider
apis={[
[serviceNowApiRef, mockServiceNowApi],
[catalogApiRef, mockCatalogApi],
[identityApiRef, mockIdentityApi],
[alertApiRef, mockAlertApi],
[errorApiRef, mockErrorApi],
[translationApiRef, mockTranslationApi as any],
]}
>
<EntityProvider entity={mockEntity}>
<EntityServicenowContent />
</EntityProvider>
</TestApiProvider>
</ThemeProvider>
<TestApiProvider
apis={[
[serviceNowApiRef, mockServiceNowApi],
[catalogApiRef, mockCatalogApi],
[identityApiRef, mockIdentityApi],
[alertApiRef, mockAlertApi],
[errorApiRef, mockErrorApi],
[translationApiRef, mockTranslationApi as any],
]}
>
<EntityProvider entity={mockEntity}>
<EntityServicenowContent />
</EntityProvider>
</TestApiProvider>
</MemoryRouter>,
);

Expand All @@ -224,22 +214,20 @@ describe('ServicenowContent', () => {
const user = userEvent.setup();
render(
<MemoryRouter>
<ThemeProvider theme={theme}>
<TestApiProvider
apis={[
[serviceNowApiRef, mockServiceNowApi],
[catalogApiRef, mockCatalogApi],
[identityApiRef, mockIdentityApi],
[alertApiRef, mockAlertApi],
[errorApiRef, mockErrorApi],
[translationApiRef, mockTranslationApi as any],
]}
>
<EntityProvider entity={mockEntity}>
<EntityServicenowContent />
</EntityProvider>
</TestApiProvider>
</ThemeProvider>
<TestApiProvider
apis={[
[serviceNowApiRef, mockServiceNowApi],
[catalogApiRef, mockCatalogApi],
[identityApiRef, mockIdentityApi],
[alertApiRef, mockAlertApi],
[errorApiRef, mockErrorApi],
[translationApiRef, mockTranslationApi as any],
]}
>
<EntityProvider entity={mockEntity}>
<EntityServicenowContent />
</EntityProvider>
</TestApiProvider>
</MemoryRouter>,
);

Expand Down
Loading
Loading