Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
2312860
feat(settings): extend types, add updateSettings API and useUpdateSet…
matejkubinec Mar 19, 2026
680823a
feat(settings): add Settings page shell with MUI tabs and placeholder…
matejkubinec Mar 19, 2026
b19e46d
feat(settings): implement SSH Key form with react-hook-form and useUp…
matejkubinec Mar 19, 2026
b6d5923
feat(settings): implement Metrics Resolution form with react-hook-form
matejkubinec Mar 19, 2026
11c2ecf
feat(settings): implement Advanced Settings form with react-hook-form
matejkubinec Mar 19, 2026
a6bba5c
feat(settings): add /settings route, update Configuration menu and PM…
matejkubinec Mar 19, 2026
805e281
fix(settings): resolve useMemo exhaustive-deps lint; add Settings loa…
matejkubinec Mar 19, 2026
261a367
feat: smaller improvements
matejkubinec Mar 19, 2026
614e2c7
fix: tooltips on advanced page
matejkubinec Mar 23, 2026
981e60f
Merge branch 'v3' into PMM-14930-settings-page-native-ui
matejkubinec Mar 23, 2026
7ba6546
feat: update imports, use types files, run formatter
matejkubinec Apr 2, 2026
4af5a59
Merge branch 'v3'
matejkubinec Apr 2, 2026
3b60e9a
PMM-14930 Send updated settings event
matejkubinec Apr 2, 2026
a4f770c
Enhanced layout behaviors to make settings better
freenandes Apr 2, 2026
255cf82
Improved layout in Settings' Metrics Resolution tab
freenandes Apr 3, 2026
638feaf
Enhance Settings UI and improve user experience
freenandes Apr 3, 2026
0be67d2
Fixed surfaces colors and how it's applied to pages
freenandes Apr 3, 2026
67499d2
Refactor Settings test to remove MemoryRouter
freenandes Apr 6, 2026
e7d80da
Merge branch 'v3' into PMM-14930-settings-page-native-ui
matejkubinec Apr 9, 2026
116ee62
PMM-14852 Fix Mongodb InMemory dashboard (#5219)
matejkubinec Apr 9, 2026
2fbf51b
Bump golang.org/x/sys from 0.42.0 to 0.43.0 (#5228)
dependabot[bot] Apr 9, 2026
5d94805
PMM-14944 Upgrade VictoriaMetrics to v1.139.0 (#5218)
ademidoff Apr 9, 2026
8e7d1f1
PMM-14980 Fix the healthcheck (#5227)
ademidoff Apr 9, 2026
d75782a
PMM-14930 Refactor sidebar collapsing
matejkubinec Apr 9, 2026
eb110f2
PMM-14930 Update settings messages
matejkubinec Apr 10, 2026
a3f081a
PMM-14930 Prevent navigation from grafana when on pmm ui page
matejkubinec Apr 10, 2026
31f0792
PMM-14930 Update percona-ui package
matejkubinec Apr 10, 2026
25f9069
PMM-14930 Correctly handle navitem clicks
matejkubinec Apr 10, 2026
626f6eb
Merge branch 'v3' into PMM-14930-settings-page-native-ui
matejkubinec Apr 10, 2026
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,4 @@ RTA-Jira-Tasks-Summary.md

# PMM Demo backups (it would increase repository size significantly)
dev/clickhouse-backups/
node_modules/@percona/percona-ui
50 changes: 50 additions & 0 deletions ui/.cursor/rules/percona_ui-styling.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---
description: "Percona UI + PMM UI styling — MUI + @percona/percona-ui, theme tokens, layout conventions, what not to do"
alwaysApply: true
---

## Stack order

1. **MUI** (mostly `@mui/material`, `@mui/icons-material`, `@mui/x-date-pickers` — though other MUI dependencies may also be in use) is the base component and styling API. Use MUI primitives for layout and composition.
2. **`@percona/percona-ui`** is the Percona design layer on top of MUI (theme, branded components, tables, dialogs). Prefer exports from `@percona/percona-ui` when they wrap or standardize behavior (e.g. `Table`, `Dialog`, `ThemeContextProvider`, `pmmThemeOptions`, `NotistackMuiSnackbar`, `primitives`).

## Do not introduce

- Do not introduce other CSS/UI frameworks (Tailwind, Bootstrap, styled-components, etc.) or ad-hoc global CSS that fights the theme.
- Do not introduce hard-coded colors, font families, or spacing that bypass the theme. Prefer `sx` with `theme.palette`, `theme.spacing`, breakpoints, and MUI `Typography` variants.
- Do not introduce custom component libraries outside MUI + `@percona/percona-ui` unless explicitly requested.

## Theming and color mode

- The app root uses `ThemeContextProvider` with `pmmThemeOptions` from `@percona/percona-ui` — do not replace with a separate `ThemeProvider` or duplicate theme objects in feature code.
- Use `ColorModeContext` / existing hooks (e.g. `useColorMode` in `hooks/theme.ts`) for light/dark; avoid direct `document` or `localStorage` theme hacks.

## Layout and PMM consistency

- Follow established layout patterns: MUI `Stack`, `Box`, `Grid`, page shells like `components/page/Page.tsx` (max width, padding, `gap` aligned with existing `sx`).
- Avoid one-off page structures that break alignment with the rest of PMM (arbitrary full-viewport hacks, inconsistent gutters).

## Styling mechanics

- Prefer **`sx`** and **`useTheme()`** from `@mui/material/styles` for component-level styling.
- For icons, use **`@mui/icons-material`** (Outlined variants where the codebase already does).

## When something is missing

- If a token or component behavior should be shared across products, explain it to them, plan a change, present it as a proposal to extend **`@percona/percona-ui`** (theme / components) rather than embedding one-off design in PMM only.
- If unsure whether a primitive exists in `percona-ui`, check that package or Storybook before inventing a parallel implementation in PMM.

## Examples

```tsx
// Good — MUI + theme tokens
<Stack spacing={2} sx={{ pt: 1, color: 'text.secondary' }}>

// Bad — unrelated styling stack
<div className="tailwind-p-4 text-gray-500">

// Good — branded table from design system
import { Table } from '@percona/percona-ui';

// Bad — pulling in another data-table library for the same job
```
5 changes: 5 additions & 0 deletions ui/apps/pmm-compat/src/compat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,11 @@ export const initialize = () => {
},
});

messenger.addListener({
type: 'SETTINGS_CHANGED',
onMessage: () => getAppEvents().publish(new SettingsUpdatedEvent()),
});

getAppEvents().subscribe(SettingsUpdatedEvent, () => {
messenger.sendMessage({
type: 'SETTINGS_CHANGED',
Expand Down
1 change: 1 addition & 0 deletions ui/apps/pmm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ See the [PMM Documentation](https://www.percona.com/doc/percona-monitoring-and-m
See detailed information about prerequisites and setup [here](../../README.md)

# Locally testing @percona/percona-ui

- Checkout code from https://github.com/percona/percona-ui
- From the lib folder, run `pnpm build:watch` and `yarn link`
- On this repo's `ui/apps/pmm` folder, run `yarn link @percona/percona-ui` and uncomment the `exclude` block from `vite.config.ts`
Expand Down
3 changes: 2 additions & 1 deletion ui/apps/pmm/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"@mui/icons-material": "^7.3.7",
"@mui/material": "^7.3.7",
"@mui/x-date-pickers": "^7.5.0",
"@percona/percona-ui": "1.0.13",
"@percona/percona-ui": "1.0.14",
"@pmm/shared": "*",
"@reactour/tour": "^3.8.0",
"@tanstack/react-query": "^5.45.1",
Expand All @@ -35,6 +35,7 @@
"notistack": "^3.0.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-hook-form": "^7.71.2",
"react-is": "18.3.1",
"react-markdown": "^9.0.1",
"react-router-dom": "^6.30.2",
Expand Down
3 changes: 3 additions & 0 deletions ui/apps/pmm/src/Providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ const Providers: FC<PropsWithChildren> = () => (
<TourProvider>
<GlobalStyles
styles={{
html: {
scrollbarGutter: 'stable',
},
'html, body, div#root': {
minHeight: '100vh',
},
Expand Down
9 changes: 9 additions & 0 deletions ui/apps/pmm/src/api/settings.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import {
GetFrontendSettingsResponse,
GetSettingsResponse,
Settings,
UpdateSettingsPayload,
} from 'types/settings.types';
import { api, grafanaApi } from './api';

Expand All @@ -19,3 +21,10 @@ export const getFrontendSettings = async () => {
await grafanaApi.get<GetFrontendSettingsResponse>('/frontend/settings');
return res.data;
};

export const updateSettings = async (
payload: UpdateSettingsPayload
): Promise<Settings> => {
const res = await api.put<GetSettingsResponse>('/server/settings', payload);
return res.data.settings;
};
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ export const getStyles = (theme: Theme) => ({
: theme.palette.error.dark,
backgroundColor:
theme.palette.mode === 'light'
? theme.palette.error.surface
: theme.palette.error.dark,
? theme.palette.error.surface
: theme.palette.error.dark,
transition: 'none',
},
down: {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { Theme } from '@mui/material';

export const getStyles = ({ palette: { background, warning, error, info } }: Theme) => ({
export const getStyles = ({
palette: { background, warning, error, info },
}: Theme) => ({
icon: {
width: 20,
height: 20,
Expand Down
10 changes: 7 additions & 3 deletions ui/apps/pmm/src/components/main/MainWithNav.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const setup = ({
)}
</TestWrapper>
);
}
};

describe('MainWithNav', () => {
it('shows loading', () => {
Expand Down Expand Up @@ -60,12 +60,16 @@ describe('MainWithNav', () => {
});

it('hides sidebar so the renderer gets a minimal layout', () => {
setup({ isLoading: false, isLoggedIn: true, kioskModeActive: false, search: 'render=1' });
setup({
isLoading: false,
isLoggedIn: true,
kioskModeActive: false,
search: 'render=1',
});

expect(screen.queryByTestId('pmm-sidebar')).toBeNull();
});


it('shows sidebar when not in renderer mode', () => {
setup({ isLoading: false, isLoggedIn: true, kioskModeActive: false });

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ export const QanHeaderActions: FC = () => {

const handleCopy = async () => {
try {
const path = constructUrl(location).replace(/\/pmm-ui\/(next\/)?graph\//, '');
const path = constructUrl(location).replace(
/\/pmm-ui\/(next\/)?graph\//,
''
);
const res = location.pathname.includes('/graph')
? await createShortUrl(path)
: { url: window.location.href };
Expand Down
77 changes: 52 additions & 25 deletions ui/apps/pmm/src/components/page/Page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ import { FC } from 'react';
import { PageProps } from './Page.types';
import {
Alert,
Box,
Card,
CardActions,
Divider,
GlobalStyles,
Link,
Stack,
Typography,
Expand All @@ -14,46 +17,70 @@ import { PMM_HOME_URL } from 'lib/constants';
import { Footer } from 'components/footer';
import { updateDocumentTitle } from 'utils/document.utils';

export const Page: FC<PageProps> = ({ title, topBar, footer, children }) => {
export const Page: FC<PageProps> = ({
title,
topBar,
footer,
children,
fullWidth,
surface,
}) => {
const { user } = useUser();
updateDocumentTitle(title);

return (
<Stack
sx={(theme) => ({
<>
{surface && (
<GlobalStyles
styles={(theme) => ({
'html, body': {
backgroundColor: surface === 'paper'
? theme.palette.background.paper
: theme.palette.background.default,
},
})}
/>
)}
<Stack
sx={{
flex: 1,
[theme.breakpoints.up('lg')]: {
width: 1000,
},
width: {
md: 'auto',
width: '100%',
maxWidth: {
lg: 1000,
},
p: {
xs: 2,
},
px: {
md: fullWidth ? 4 : undefined,
},
mx: 'auto',
gap: 3,
gap: 2,
mt: 1,
})}
}}
>
{topBar}
{!!title && <Typography variant="h2">{title}</Typography>}
{user?.isAuthorized ? (
children
) : (
<Card variant="outlined" sx={{ p: 2 }}>
<Alert severity="error" sx={{ mb: 1 }}>
{Messages.noAcccess}
</Alert>
<CardActions>
<Typography>
{Messages.goBack}
<Link href={PMM_HOME_URL}>{Messages.home}</Link>
</Typography>
</CardActions>
</Card>
)}
<Box sx={{ flex: 1, display: 'flex', flexDirection: 'column' }}>
{user?.isAuthorized ? (
children
) : (
<Card variant="outlined" sx={{ p: 2 }}>
<Alert severity="error" sx={{ mb: 1 }}>
{Messages.noAcccess}
</Alert>
<CardActions>
<Typography>
{Messages.goBack}
<Link href={PMM_HOME_URL}>{Messages.home}</Link>
</Typography>
</CardActions>
</Card>
)}
</Box>
<Divider />
{footer !== undefined ? footer : <Footer />}
</Stack>
</>
);
};
2 changes: 2 additions & 0 deletions ui/apps/pmm/src/components/page/Page.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ export interface PageProps extends PropsWithChildren {
title?: string;
footer?: ReactNode;
topBar?: ReactNode;
fullWidth?: boolean;
surface?: 'default' | 'paper';
}
12 changes: 12 additions & 0 deletions ui/apps/pmm/src/components/sidebar/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { NavigationHeading } from './nav-heading';
import { Drawer } from './drawer';
import { NavItem } from './nav-item';
import List from '@mui/material/List';
import useMediaQuery from '@mui/material/useMediaQuery';
import { useTheme } from '@mui/material/styles';
import { findActiveNavItem } from 'utils/navigation.utils';
import { useLocation } from 'react-router-dom';
import { NavItem as NavItemType } from 'types/navigation.types';
Expand All @@ -13,6 +15,8 @@ export const Sidebar: FC = () => {
const [activeItem, setActiveItem] = useState<NavItemType>(navTree[0]);
const [animating, setAnimating] = useState(false);
const location = useLocation();
const theme = useTheme();
const isNarrow = useMediaQuery(theme.breakpoints.down('md'));

const toggleSidebar = useCallback(() => {
setNavOpen(!navOpen);
Expand All @@ -35,6 +39,13 @@ export const Sidebar: FC = () => {
}
}, [navTree, location.pathname]);

const handleNavItemClick = () => {
// autoclose sidebar when layout is narrow
if (isNarrow) {
setNavOpen(false);
}
};

return (
<Drawer
open={navOpen}
Expand Down Expand Up @@ -63,6 +74,7 @@ export const Sidebar: FC = () => {
item={item}
activeItem={activeItem}
drawerOpen={navOpen}
onClick={handleNavItemClick}
/>
))}
</List>
Expand Down
11 changes: 11 additions & 0 deletions ui/apps/pmm/src/components/sidebar/drawer/Drawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,17 @@ const Drawer = styled(MuiDrawer, {
style: {
...openedMixin(theme),
'& .MuiDrawer-paper': openedMixin(theme),
[theme.breakpoints.down('md')]: {
...closedMixin(theme),
'& .MuiDrawer-paper': {
...openedMixin(theme),
position: 'fixed',
top: 0,
left: 0,
height: '100vh',
zIndex: theme.zIndex.drawer + 1,
},
},
},
},
{
Expand Down
Loading
Loading