Chore: fix some pages on mobile (#101159)

fix some pages on mobile
This commit is contained in:
Ashley Harrison
2025-02-21 16:11:32 +00:00
committed by GitHub
parent 9e80b0f913
commit ffb8ef850b
8 changed files with 185 additions and 173 deletions
@@ -1,7 +1,7 @@
import { Fragment } from 'react';
import Skeleton from 'react-loading-skeleton';
import { Text } from '@grafana/ui';
import { ScrollContainer, Text } from '@grafana/ui';
import { SkeletonComponent, attachSkeleton } from '@grafana/ui/src/unstable';
import { Settings } from './AdminSettings';
@@ -12,28 +12,30 @@ interface Props {
const AdminSettingsTableComponent = ({ settings }: Props) => {
return (
<table className="filter-table">
<tbody>
{Object.entries(settings).map(([sectionName, sectionSettings], i) => (
<Fragment key={`section-${i}`}>
<tr>
<td>
<Text color="info" weight="bold">
{sectionName}
</Text>
</td>
<td />
</tr>
{Object.entries(sectionSettings).map(([settingName, settingValue], j) => (
<tr key={`property-${j}`}>
<td style={{ paddingLeft: '25px' }}>{settingName}</td>
<td style={{ whiteSpace: 'break-spaces' }}>{settingValue}</td>
<ScrollContainer overflowY="visible" overflowX="auto" width="100%">
<table className="filter-table">
<tbody>
{Object.entries(settings).map(([sectionName, sectionSettings], i) => (
<Fragment key={`section-${i}`}>
<tr>
<td>
<Text color="info" weight="bold">
{sectionName}
</Text>
</td>
<td />
</tr>
))}
</Fragment>
))}
</tbody>
</table>
{Object.entries(sectionSettings).map(([settingName, settingValue], j) => (
<tr key={`property-${j}`}>
<td style={{ paddingLeft: '25px' }}>{settingName}</td>
<td style={{ whiteSpace: 'break-spaces' }}>{settingValue}</td>
</tr>
))}
</Fragment>
))}
</tbody>
</table>
</ScrollContainer>
);
};
@@ -42,34 +44,36 @@ const randomValues = new Array(50).fill(null).map(() => Math.random());
const AdminSettingsTableSkeleton: SkeletonComponent = ({ rootProps }) => {
return (
<table className="filter-table" {...rootProps}>
<tbody>
{randomValues.map((randomValue, index) => {
const isSection = index === 0 || randomValue > 0.9;
<ScrollContainer overflowY="visible" overflowX="auto" width="100%">
<table className="filter-table" {...rootProps}>
<tbody>
{randomValues.map((randomValue, index) => {
const isSection = index === 0 || randomValue > 0.9;
return (
<Fragment key={index}>
{isSection && (
return (
<Fragment key={index}>
{isSection && (
<tr>
<td className="admin-settings-section">
<Skeleton width={getRandomInRange(40, 80, randomValue)} />
</td>
<td />
</tr>
)}
<tr>
<td className="admin-settings-section">
<Skeleton width={getRandomInRange(40, 80, randomValue)} />
<td style={{ paddingLeft: '25px' }}>
<Skeleton width={getRandomInRange(60, 100, randomValue)} />
</td>
<td>
<Skeleton width={getRandomInRange(80, 320, randomValue)} />
</td>
<td />
</tr>
)}
<tr>
<td style={{ paddingLeft: '25px' }}>
<Skeleton width={getRandomInRange(60, 100, randomValue)} />
</td>
<td>
<Skeleton width={getRandomInRange(80, 320, randomValue)} />
</td>
</tr>
</Fragment>
);
})}
</tbody>
</table>
</Fragment>
);
})}
</tbody>
</table>
</ScrollContainer>
);
};
+3 -3
View File
@@ -12,7 +12,7 @@ const getStyles = (theme: GrafanaTheme2) => {
return {
container: css({
padding: '36px 79px',
padding: theme.spacing(4),
background: theme.components.panel.background,
}),
footer: css({
@@ -22,7 +22,7 @@ const getStyles = (theme: GrafanaTheme2) => {
}),
header: css({
height: '137px',
padding: '40px 0 0 79px',
padding: theme.spacing(4, 0, 0, 4),
position: 'relative',
background: `url('${backgroundUrl}') right`,
}),
@@ -52,7 +52,7 @@ export function LicenseChrome({ header, editionNotice, subheader, children }: Pr
background: '#0A1C36',
position: 'absolute',
top: '19px',
left: '71%',
right: '5%',
}}
>
<img
+11 -26
View File
@@ -3,7 +3,7 @@ import { useEffect, useState } from 'react';
import { GrafanaTheme2 } from '@grafana/data';
import { config, GrafanaBootConfig } from '@grafana/runtime';
import { LinkButton, useStyles2 } from '@grafana/ui';
import { LinkButton, Stack, useStyles2 } from '@grafana/ui';
import { Trans } from 'app/core/internationalization';
import { AccessControlAction } from 'app/types';
@@ -43,7 +43,13 @@ export const ServerStats = () => {
<Trans i18nKey="admin.server-settings.not-found">No stats found.</Trans>
</p>
) : (
<div className={styles.row}>
<Stack
gap={2}
direction={{
xs: 'column',
md: 'row',
}}
>
<ServerStatsCard
isLoading={isLoading}
content={[
@@ -59,7 +65,7 @@ export const ServerStats = () => {
}
/>
<div className={styles.doubleRow}>
<Stack direction="column" gap={2}>
<ServerStatsCard
isLoading={isLoading}
content={[{ name: 'Data sources', value: stats?.datasources }]}
@@ -80,7 +86,7 @@ export const ServerStats = () => {
</LinkButton>
}
/>
</div>
</Stack>
<ServerStatsCard
isLoading={isLoading}
content={[
@@ -98,7 +104,7 @@ export const ServerStats = () => {
)
}
/>
</div>
</Stack>
)}
</>
);
@@ -133,27 +139,6 @@ const getStyles = (theme: GrafanaTheme2) => {
title: css({
marginBottom: theme.spacing(4),
}),
row: css({
display: 'flex',
justifyContent: 'space-between',
width: '100%',
'& > div:not(:last-of-type)': {
marginRight: theme.spacing(2),
},
'& > div': {
width: '33.3%',
},
}),
doubleRow: css({
display: 'flex',
flexDirection: 'column',
'& > div:first-of-type': {
marginBottom: theme.spacing(2),
},
}),
notFound: css({
fontSize: theme.typography.h6.fontSize,
textAlign: 'center',
@@ -50,6 +50,7 @@ const getStyles = (theme: GrafanaTheme2) => {
display: 'flex',
flexDirection: 'column',
gap: theme.spacing(2),
margin: 0,
padding: theme.spacing(2),
}),
indent: css({
@@ -2,7 +2,7 @@ import { useState, useCallback } from 'react';
import useAsync from 'react-use/lib/useAsync';
import { config } from '@grafana/runtime';
import { ConfirmModal, EmptyState, TextLink } from '@grafana/ui';
import { ConfirmModal, EmptyState, ScrollContainer, TextLink } from '@grafana/ui';
import { Trans, t } from 'app/core/internationalization';
import { getDashboardSnapshotSrv, Snapshot } from 'app/features/dashboard/services/SnapshotSrv';
@@ -62,7 +62,7 @@ export const SnapshotListTable = () => {
}
return (
<div>
<ScrollContainer overflowY="visible" overflowX="auto" width="100%">
<table className="filter-table">
<thead>
<tr>
@@ -112,6 +112,6 @@ export const SnapshotListTable = () => {
setRemoveSnapshot(undefined);
}}
/>
</div>
</ScrollContainer>
);
};
@@ -1,7 +1,7 @@
import { PureComponent } from 'react';
import { selectors } from '@grafana/e2e-selectors';
import { Button, LoadingPlaceholder } from '@grafana/ui';
import { Button, LoadingPlaceholder, ScrollContainer } from '@grafana/ui';
import { Trans } from 'app/core/internationalization';
import { UserDTO, UserOrg } from 'app/types';
@@ -30,46 +30,48 @@ export class UserOrganizations extends PureComponent<Props> {
<Trans i18nKey="user-orgs.title">Organizations</Trans>
</h3>
<table className="filter-table form-inline" data-testid={selectors.components.UserProfile.orgsTable}>
<thead>
<tr>
<th>
<Trans i18nKey="user-orgs.name-column">Name</Trans>
</th>
<th>
<Trans i18nKey="user-orgs.role-column">Role</Trans>
</th>
<th />
</tr>
</thead>
<tbody>
{orgs.map((org: UserOrg, index) => {
return (
<tr key={index}>
<td>{org.name}</td>
<td>{org.role}</td>
<td className="text-right">
{org.orgId === user?.orgId ? (
<Button variant="secondary" size="sm" disabled>
<Trans i18nKey="user-orgs.current-org-button">Current</Trans>
</Button>
) : (
<Button
variant="secondary"
size="sm"
onClick={() => {
this.props.setUserOrg(org);
}}
>
<Trans i18nKey="user-orgs.select-org-button">Select organisation</Trans>
</Button>
)}
</td>
</tr>
);
})}
</tbody>
</table>
<ScrollContainer overflowY="visible" overflowX="auto" width="100%">
<table className="filter-table form-inline" data-testid={selectors.components.UserProfile.orgsTable}>
<thead>
<tr>
<th>
<Trans i18nKey="user-orgs.name-column">Name</Trans>
</th>
<th>
<Trans i18nKey="user-orgs.role-column">Role</Trans>
</th>
<th />
</tr>
</thead>
<tbody>
{orgs.map((org: UserOrg, index) => {
return (
<tr key={index}>
<td>{org.name}</td>
<td>{org.role}</td>
<td className="text-right">
{org.orgId === user?.orgId ? (
<Button variant="secondary" size="sm" disabled>
<Trans i18nKey="user-orgs.current-org-button">Current</Trans>
</Button>
) : (
<Button
variant="secondary"
size="sm"
onClick={() => {
this.props.setUserOrg(org);
}}
>
<Trans i18nKey="user-orgs.select-org-button">Select organisation</Trans>
</Button>
)}
</td>
</tr>
);
})}
</tbody>
</table>
</ScrollContainer>
</div>
);
}
+51 -49
View File
@@ -2,7 +2,7 @@ import { css } from '@emotion/css';
import { PureComponent } from 'react';
import { selectors } from '@grafana/e2e-selectors';
import { Button, Icon, LoadingPlaceholder } from '@grafana/ui';
import { Button, Icon, LoadingPlaceholder, ScrollContainer } from '@grafana/ui';
import { TagBadge } from 'app/core/components/TagFilter/TagBadge';
import { t, Trans } from 'app/core/internationalization';
import { formatDate } from 'app/core/internationalization/dates';
@@ -28,55 +28,57 @@ class UserSessions extends PureComponent<Props> {
{sessions.length > 0 && (
<>
<h3 className="page-sub-heading">Sessions</h3>
<table className="filter-table form-inline" data-testid={selectors.components.UserProfile.sessionsTable}>
<thead>
<tr>
<th>
<Trans i18nKey="user-session.seen-at-column">Last seen</Trans>
</th>
<th>
<Trans i18nKey="user-session.created-at-column">Logged on</Trans>
</th>
<th>
<Trans i18nKey="user-session.ip-column">IP address</Trans>
</th>
<th>
<Trans i18nKey="user-session.browser-column">Browser & OS</Trans>
</th>
<th>
<Trans i18nKey="user-session.identity-provider-column">Identity Provider</Trans>
</th>
<th></th>
</tr>
</thead>
<tbody>
{sessions.map((session: UserSession, index) => (
<tr key={index}>
{session.isActive ? <td>Now</td> : <td>{session.seenAt}</td>}
<td>{formatDate(session.createdAt, { dateStyle: 'long' })}</td>
<td>{session.clientIp}</td>
<td>
{session.browser} on {session.os} {session.osVersion}
</td>
<td>
{session.authModule && <TagBadge label={session.authModule} removeIcon={false} count={0} />}
</td>
<td>
<Button
size="sm"
variant="destructive"
tooltip={t('user-session.revoke', 'Revoke user session')}
onClick={() => revokeUserSession(session.id)}
aria-label={t('user-session.revoke', 'Revoke user session')}
>
<Icon name="power" />
</Button>
</td>
<ScrollContainer overflowY="visible" overflowX="auto" width="100%">
<table className="filter-table form-inline" data-testid={selectors.components.UserProfile.sessionsTable}>
<thead>
<tr>
<th>
<Trans i18nKey="user-session.seen-at-column">Last seen</Trans>
</th>
<th>
<Trans i18nKey="user-session.created-at-column">Logged on</Trans>
</th>
<th>
<Trans i18nKey="user-session.ip-column">IP address</Trans>
</th>
<th>
<Trans i18nKey="user-session.browser-column">Browser & OS</Trans>
</th>
<th>
<Trans i18nKey="user-session.identity-provider-column">Identity Provider</Trans>
</th>
<th></th>
</tr>
))}
</tbody>
</table>
</thead>
<tbody>
{sessions.map((session: UserSession, index) => (
<tr key={index}>
{session.isActive ? <td>Now</td> : <td>{session.seenAt}</td>}
<td>{formatDate(session.createdAt, { dateStyle: 'long' })}</td>
<td>{session.clientIp}</td>
<td>
{session.browser} on {session.os} {session.osVersion}
</td>
<td>
{session.authModule && <TagBadge label={session.authModule} removeIcon={false} count={0} />}
</td>
<td>
<Button
size="sm"
variant="destructive"
tooltip={t('user-session.revoke', 'Revoke user session')}
onClick={() => revokeUserSession(session.id)}
aria-label={t('user-session.revoke', 'Revoke user session')}
>
<Icon name="power" />
</Button>
</td>
</tr>
))}
</tbody>
</table>
</ScrollContainer>
</>
)}
</div>
@@ -1,9 +1,20 @@
import { css } from '@emotion/css';
import pluralize from 'pluralize';
import { useEffect, useState } from 'react';
import { connect, ConnectedProps } from 'react-redux';
import { OrgRole } from '@grafana/data';
import { ConfirmModal, FilterInput, LinkButton, RadioButtonGroup, InlineField, EmptyState, Box } from '@grafana/ui';
import { GrafanaTheme2, OrgRole } from '@grafana/data';
import {
ConfirmModal,
FilterInput,
LinkButton,
RadioButtonGroup,
InlineField,
EmptyState,
Box,
Stack,
useStyles2,
} from '@grafana/ui';
import { Page } from 'app/core/components/Page/Page';
import config from 'app/core/config';
import { contextSrv } from 'app/core/core';
@@ -78,6 +89,7 @@ export const ServiceAccountsListPageUnconnected = ({
const [isDisableModalOpen, setIsDisableModalOpen] = useState(false);
const [newToken, setNewToken] = useState('');
const [currentServiceAccount, setCurrentServiceAccount] = useState<ServiceAccountDTO | null>(null);
const styles = useStyles2(getStyles);
useEffect(() => {
fetchServiceAccounts({ withLoadingIndicator: true });
@@ -192,13 +204,13 @@ export const ServiceAccountsListPageUnconnected = ({
}
>
<Page.Contents>
<div className="page-action-bar">
<Stack justifyContent="space-between" wrap="wrap">
<InlineField grow>
<FilterInput
className={styles.filterInput}
placeholder="Search service account by name"
value={query}
onChange={onQueryChange}
width={50}
/>
</InlineField>
<Box marginBottom={1}>
@@ -208,7 +220,7 @@ export const ServiceAccountsListPageUnconnected = ({
value={serviceAccountStateFilter}
/>
</Box>
</div>
</Stack>
{!isLoading && !noServiceAccountsCreated && serviceAccounts.length === 0 && (
<EmptyState
variant="not-found"
@@ -291,5 +303,11 @@ export const ServiceAccountsListPageUnconnected = ({
);
};
const getStyles = (theme: GrafanaTheme2) => ({
filterInput: css({
maxWidth: theme.spacing(50),
}),
});
const ServiceAccountsListPage = connector(ServiceAccountsListPageUnconnected);
export default ServiceAccountsListPage;