Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 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
a2f766b
Merge branch 'v3' into PMM-14930-settings-page-native-ui
matejkubinec Apr 21, 2026
8c4b3f4
PMM-14930 Use form components from percona-ui
matejkubinec Apr 21, 2026
5b9a0df
PMM-14930 Use zod for validation for metrics resolution
matejkubinec Apr 21, 2026
80d2a14
PMM-14930 Use zod for ssh key validation
matejkubinec Apr 21, 2026
a57598c
PMM-14930 Use zod for advanced settings validation
matejkubinec Apr 21, 2026
e54b75c
PMM-14930 Run lint & format
matejkubinec Apr 21, 2026
fe9437a
Merge branch 'v3' into PMM-14930-settings-page-native-ui
matejkubinec Apr 21, 2026
fbc02fd
Merge branch 'v3' into PMM-14930-settings-page-native-ui
matejkubinec Apr 21, 2026
114c6e1
PMM-14930 Refactoring and fixes
matejkubinec Apr 22, 2026
a563172
Merge branch 'v3' into PMM-14930-settings-page-native-ui
matejkubinec Apr 22, 2026
5ab549d
PMM-14930 Revert some changes
matejkubinec Apr 22, 2026
52b638f
PMM-14930 Cleanup settings payload
matejkubinec Apr 22, 2026
02ec8e7
PMM-14930 Use toPayload util function
matejkubinec Apr 22, 2026
4bcec3e
Merge branch 'PMM-14930-settings-page-native-ui' of github.com:percon…
matejkubinec Apr 22, 2026
9be33fb
PMM-14930 Run lint & format
matejkubinec Apr 22, 2026
5ba194d
PMM-14930 Use messages file
matejkubinec Apr 22, 2026
8da647d
Merge branch 'v3' into PMM-14930-settings-page-native-ui
matejkubinec Apr 23, 2026
0bfc549
PMM-14930 Correctly handle settings in readonly
matejkubinec Apr 23, 2026
6fb2eda
Merge branch 'v3' into PMM-14930-settings-page-native-ui
matejkubinec Apr 23, 2026
a106449
Merge branch 'v3' into PMM-14930-settings-page-native-ui
matejkubinec Apr 23, 2026
cfb2516
Merge branch 'v3' into PMM-14930-settings-page-native-ui
matejkubinec Apr 27, 2026
bec3970
PMM-14930 Use constant for feature management fields
matejkubinec Apr 27, 2026
edc2aac
Merge branch 'v3' into PMM-14930-settings-page-native-ui
matejkubinec Apr 28, 2026
0c51ec0
PMM-7 - PMM's AI-agent documentation housekeeping (#5298)
ondrejpatocka Apr 28, 2026
1fcce44
Merge branch 'v3' of github.com:percona/pmm into PMM-14930-settings-p…
matejkubinec Apr 29, 2026
42760dc
PMM-14930 Use same settings urls as previously
matejkubinec Apr 29, 2026
25fc9c4
PMM-14930 Add fallback for old settings page
matejkubinec Apr 30, 2026
262aef1
PMM-14930 Fix unit test
matejkubinec Apr 30, 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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,4 @@ dev/clickhouse-backups/
# Exclude AI skills to support fast local experimentation
.claude/
.cursor/
.cursorrules/
.cursorrules/
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
7 changes: 5 additions & 2 deletions ui/apps/pmm/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@
"@fontsource/poppins": "^5.0.15",
"@fontsource/roboto": "^5.0.14",
"@fontsource/roboto-mono": "^5.0.18",
"@hookform/resolvers": "^5.2.2",
"@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.15",
"@pmm/shared": "*",
"@reactour/tour": "^3.8.0",
"@tanstack/react-query": "^5.45.1",
Expand All @@ -35,13 +36,15 @@
"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",
"react-syntax-highlighter": "^16.1.0",
"rehype-raw": "^7.0.0",
"remark-gfm": "^4.0.0",
"vite-plugin-svgr": "^4.3.0"
"vite-plugin-svgr": "^4.3.0",
"zod": "^4.3.6"
},
"devDependencies": {
"@percona/prettier-config": "^1.1.0",
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
17 changes: 15 additions & 2 deletions ui/apps/pmm/src/api/settings.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import {
GetFrontendSettingsResponse,
GetReadonlySettingsResponse,
GetSettingsResponse,
ReadonlySettings,
Settings,
UpdateSettingsPayload,
} from 'types/settings.types';
import { api, grafanaApi } from './api';

Expand All @@ -9,8 +13,10 @@ export const getSettings = async () => {
return res.data.settings;
};

export const getReadonlySettings = async () => {
const res = await api.get<GetSettingsResponse>('/server/settings/readonly');
export const getReadonlySettings = async (): Promise<ReadonlySettings> => {
const res = await api.get<GetReadonlySettingsResponse>(
'/server/settings/readonly'
);
return res.data.settings;
};

Expand All @@ -19,3 +25,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
100 changes: 64 additions & 36 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,71 @@ 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) => ({
flex: 1,
[theme.breakpoints.up('lg')]: {
width: 1000,
},
width: {
md: 'auto',
},
p: {
xs: 2,
},
mx: 'auto',
gap: 3,
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>
<>
{surface && (
<GlobalStyles
styles={(theme) => ({
'html, body': {
backgroundColor:
surface === 'paper'
? theme.palette.background.paper
: theme.palette.background.default,
},
})}
/>
)}
{footer !== undefined ? footer : <Footer />}
</Stack>
<Stack
sx={{
flex: 1,
width: '100%',
maxWidth: {
lg: 1000,
},
p: {
xs: 2,
},
px: {
md: fullWidth ? 4 : undefined,
},
mx: 'auto',
gap: 2,
mt: 1,
}}
>
{topBar}
{!!title && <Typography variant="h2">{title}</Typography>}
<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