mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Admin: Use primitive components for table views (#76512)
* Remove HorizontalGroup and VerticalGroup from OrgUserstable * Refactor OrgUnits * Refactor UsersTable * Add TableWrapper * Use Stack and Flex for TeamList * Revert pagination changes * Update betterer * Remove div wrapper * Codeformat
This commit is contained in:
parent
be5ba68132
commit
a4522812fe
@ -1761,11 +1761,6 @@ exports[`better eslint`] = {
|
|||||||
"public/app/features/admin/UserSessions.tsx:5381": [
|
"public/app/features/admin/UserSessions.tsx:5381": [
|
||||||
[0, 0, 0, "Styles should be written using objects.", "0"]
|
[0, 0, 0, "Styles should be written using objects.", "0"]
|
||||||
],
|
],
|
||||||
"public/app/features/admin/Users/OrgUnits.tsx:5381": [
|
|
||||||
[0, 0, 0, "Styles should be written using objects.", "0"],
|
|
||||||
[0, 0, 0, "Styles should be written using objects.", "1"],
|
|
||||||
[0, 0, 0, "Styles should be written using objects.", "2"]
|
|
||||||
],
|
|
||||||
"public/app/features/alerting/AlertTab.tsx:5381": [
|
"public/app/features/alerting/AlertTab.tsx:5381": [
|
||||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
||||||
|
@ -1,15 +1,13 @@
|
|||||||
import { css } from '@emotion/css';
|
import React, { forwardRef, PropsWithChildren } from 'react';
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import { GrafanaTheme2, IconName } from '@grafana/data';
|
import { IconName } from '@grafana/data';
|
||||||
import { Icon, Tooltip, useStyles2 } from '@grafana/ui';
|
import { Icon, Tooltip } from '@grafana/ui';
|
||||||
|
import { Box, Flex } from '@grafana/ui/src/unstable';
|
||||||
import { Unit } from 'app/types';
|
import { Unit } from 'app/types';
|
||||||
|
|
||||||
type OrgUnitProps = { units?: Unit[]; icon: IconName };
|
type OrgUnitProps = { units?: Unit[]; icon: IconName };
|
||||||
|
|
||||||
export const OrgUnits = ({ units, icon }: OrgUnitProps) => {
|
export const OrgUnits = ({ units, icon }: OrgUnitProps) => {
|
||||||
const styles = useStyles2(getStyles);
|
|
||||||
|
|
||||||
if (!units?.length) {
|
if (!units?.length) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -17,39 +15,25 @@ export const OrgUnits = ({ units, icon }: OrgUnitProps) => {
|
|||||||
return units.length > 1 ? (
|
return units.length > 1 ? (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
placement={'top'}
|
placement={'top'}
|
||||||
content={
|
content={<Flex direction={'column'}>{units?.map((unit) => <span key={unit.name}>{unit.name}</span>)}</Flex>}
|
||||||
<div className={styles.unitTooltip}>{units?.map((unit) => <span key={unit.name}>{unit.name}</span>)}</div>
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<div className={styles.unitItem}>
|
<Content icon={icon}>{units.length}</Content>
|
||||||
<Icon name={icon} /> <span>{units.length}</span>
|
|
||||||
</div>
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
) : (
|
) : (
|
||||||
<span className={styles.unitItem}>
|
<Content icon={icon}>{units[0].name}</Content>
|
||||||
<Icon name={icon} /> {units[0].name}
|
|
||||||
</span>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getStyles = (theme: GrafanaTheme2) => {
|
interface ContentProps extends PropsWithChildren {
|
||||||
return {
|
icon: IconName;
|
||||||
unitTooltip: css`
|
}
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
`,
|
|
||||||
unitItem: css`
|
|
||||||
padding: ${theme.spacing(0.5)} 0;
|
|
||||||
margin-right: ${theme.spacing(1)};
|
|
||||||
|
|
||||||
svg {
|
export const Content = forwardRef<HTMLElement, ContentProps>(({ children, icon }, ref) => {
|
||||||
margin-bottom: ${theme.spacing(0.25)};
|
return (
|
||||||
}
|
<Box ref={ref} display={'flex'} alignItems={'center'} marginRight={1}>
|
||||||
`,
|
<Icon name={icon} /> <Box marginLeft={1}>{children}</Box>
|
||||||
link: css`
|
</Box>
|
||||||
color: inherit;
|
);
|
||||||
cursor: pointer;
|
});
|
||||||
text-decoration: underline;
|
|
||||||
`,
|
Content.displayName = 'TooltipContent';
|
||||||
};
|
|
||||||
};
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { css } from '@emotion/css';
|
|
||||||
import React, { useEffect, useMemo, useState } from 'react';
|
import React, { useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { GrafanaTheme2, OrgRole } from '@grafana/data';
|
import { OrgRole } from '@grafana/data';
|
||||||
import { selectors as e2eSelectors } from '@grafana/e2e-selectors';
|
import { selectors as e2eSelectors } from '@grafana/e2e-selectors';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
@ -9,16 +8,14 @@ import {
|
|||||||
Icon,
|
Icon,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
CellProps,
|
CellProps,
|
||||||
useStyles2,
|
|
||||||
Tag,
|
Tag,
|
||||||
InteractiveTable,
|
InteractiveTable,
|
||||||
Column,
|
Column,
|
||||||
FetchDataFunc,
|
FetchDataFunc,
|
||||||
Pagination,
|
Pagination,
|
||||||
HorizontalGroup,
|
|
||||||
VerticalGroup,
|
|
||||||
Avatar,
|
Avatar,
|
||||||
} from '@grafana/ui';
|
} from '@grafana/ui';
|
||||||
|
import { Flex, Stack, Box } from '@grafana/ui/src/unstable';
|
||||||
import { UserRolePicker } from 'app/core/components/RolePicker/UserRolePicker';
|
import { UserRolePicker } from 'app/core/components/RolePicker/UserRolePicker';
|
||||||
import { fetchRoleOptions } from 'app/core/components/RolePicker/api';
|
import { fetchRoleOptions } from 'app/core/components/RolePicker/api';
|
||||||
import { TagBadge } from 'app/core/components/TagFilter/TagBadge';
|
import { TagBadge } from 'app/core/components/TagFilter/TagBadge';
|
||||||
@ -28,6 +25,8 @@ import { AccessControlAction, OrgUser, Role } from 'app/types';
|
|||||||
|
|
||||||
import { OrgRolePicker } from '../OrgRolePicker';
|
import { OrgRolePicker } from '../OrgRolePicker';
|
||||||
|
|
||||||
|
import { TableWrapper } from './TableWrapper';
|
||||||
|
|
||||||
type Cell<T extends keyof OrgUser = keyof OrgUser> = CellProps<OrgUser, OrgUser[T]>;
|
type Cell<T extends keyof OrgUser = keyof OrgUser> = CellProps<OrgUser, OrgUser[T]>;
|
||||||
|
|
||||||
const disabledRoleMessage = `This user's role is not editable because it is synchronized from your auth provider.
|
const disabledRoleMessage = `This user's role is not editable because it is synchronized from your auth provider.
|
||||||
@ -71,7 +70,6 @@ export const OrgUsersTable = ({
|
|||||||
}: Props) => {
|
}: Props) => {
|
||||||
const [userToRemove, setUserToRemove] = useState<OrgUser | null>(null);
|
const [userToRemove, setUserToRemove] = useState<OrgUser | null>(null);
|
||||||
const [roleOptions, setRoleOptions] = useState<Role[]>([]);
|
const [roleOptions, setRoleOptions] = useState<Role[]>([]);
|
||||||
const styles = useStyles2(getStyles);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function fetchOptions() {
|
async function fetchOptions() {
|
||||||
@ -148,7 +146,18 @@ export const OrgUsersTable = ({
|
|||||||
{
|
{
|
||||||
id: 'info',
|
id: 'info',
|
||||||
header: '',
|
header: '',
|
||||||
cell: InfoCell,
|
cell: ({ row: { original } }: Cell) => {
|
||||||
|
const basicRoleDisabled = getBasicRoleDisabled(original);
|
||||||
|
return (
|
||||||
|
basicRoleDisabled && (
|
||||||
|
<Box display={'flex'} alignItems={'center'} marginLeft={1}>
|
||||||
|
<Tooltip content={disabledRoleMessage}>
|
||||||
|
<Icon name="question-circle" />
|
||||||
|
</Tooltip>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'authLabels',
|
id: 'authLabels',
|
||||||
@ -186,18 +195,18 @@ export const OrgUsersTable = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<VerticalGroup spacing="md" data-testid={selectors.container}>
|
<Stack gap={2} data-testid={selectors.container}>
|
||||||
<div className={styles.wrapper}>
|
<TableWrapper>
|
||||||
<InteractiveTable
|
<InteractiveTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={users}
|
data={users}
|
||||||
getRowId={(user) => String(user.userId)}
|
getRowId={(user) => String(user.userId)}
|
||||||
fetchData={fetchData}
|
fetchData={fetchData}
|
||||||
/>
|
/>
|
||||||
<HorizontalGroup justify="flex-end">
|
<Flex justifyContent="flex-end">
|
||||||
<Pagination onNavigate={changePage} currentPage={page} numberOfPages={totalPages} hideWhenSinglePage={true} />
|
<Pagination onNavigate={changePage} currentPage={page} numberOfPages={totalPages} hideWhenSinglePage={true} />
|
||||||
</HorizontalGroup>
|
</Flex>
|
||||||
</div>
|
</TableWrapper>
|
||||||
{Boolean(userToRemove) && (
|
{Boolean(userToRemove) && (
|
||||||
<ConfirmModal
|
<ConfirmModal
|
||||||
body={`Are you sure you want to delete user ${userToRemove?.login}?`}
|
body={`Are you sure you want to delete user ${userToRemove?.login}?`}
|
||||||
@ -216,43 +225,6 @@ export const OrgUsersTable = ({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</VerticalGroup>
|
</Stack>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const InfoCell = ({ row: { original } }: Cell) => {
|
|
||||||
const styles = useStyles2(getStyles);
|
|
||||||
const basicRoleDisabled = getBasicRoleDisabled(original);
|
|
||||||
return (
|
|
||||||
basicRoleDisabled && (
|
|
||||||
<div className={styles.row}>
|
|
||||||
<Tooltip content={disabledRoleMessage}>
|
|
||||||
<Icon name="question-circle" className={styles.icon} />
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const getStyles = (theme: GrafanaTheme2) => ({
|
|
||||||
row: css({
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
}),
|
|
||||||
icon: css({
|
|
||||||
marginLeft: theme.spacing(1),
|
|
||||||
}),
|
|
||||||
// Enable RolePicker overflow
|
|
||||||
wrapper: css({
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
overflowX: 'auto',
|
|
||||||
overflowY: 'hidden',
|
|
||||||
minHeight: '100vh',
|
|
||||||
width: '100%',
|
|
||||||
'& > div': {
|
|
||||||
overflowX: 'unset',
|
|
||||||
marginBottom: theme.spacing(2),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
30
public/app/features/admin/Users/TableWrapper.tsx
Normal file
30
public/app/features/admin/Users/TableWrapper.tsx
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { css } from '@emotion/css';
|
||||||
|
import React, { PropsWithChildren } from 'react';
|
||||||
|
|
||||||
|
import { GrafanaTheme2 } from '@grafana/data';
|
||||||
|
import { useStyles2 } from '@grafana/ui';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A wrapper component for interactive tables using RolePicker to enable overflow.
|
||||||
|
* Should be removed when the RolePicker component uses portals to render its menu
|
||||||
|
*/
|
||||||
|
export const TableWrapper = ({ children }: PropsWithChildren) => {
|
||||||
|
const styles = useStyles2(getStyles);
|
||||||
|
return <div className={styles.wrapper}>{children}</div>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getStyles = (theme: GrafanaTheme2) => ({
|
||||||
|
// Enable RolePicker overflow
|
||||||
|
wrapper: css({
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
overflowX: 'auto',
|
||||||
|
overflowY: 'hidden',
|
||||||
|
minHeight: '100vh',
|
||||||
|
width: '100%',
|
||||||
|
'& > div': {
|
||||||
|
overflowX: 'unset',
|
||||||
|
marginBottom: theme.spacing(2),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
@ -1,21 +1,18 @@
|
|||||||
import { css } from '@emotion/css';
|
|
||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
|
|
||||||
import { GrafanaTheme2 } from '@grafana/data';
|
|
||||||
import {
|
import {
|
||||||
InteractiveTable,
|
InteractiveTable,
|
||||||
CellProps,
|
CellProps,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
Icon,
|
Icon,
|
||||||
useStyles2,
|
|
||||||
Tag,
|
Tag,
|
||||||
Pagination,
|
Pagination,
|
||||||
Column,
|
Column,
|
||||||
VerticalGroup,
|
|
||||||
HorizontalGroup,
|
|
||||||
FetchDataFunc,
|
FetchDataFunc,
|
||||||
|
Text,
|
||||||
Avatar,
|
Avatar,
|
||||||
} from '@grafana/ui';
|
} from '@grafana/ui';
|
||||||
|
import { Flex, Stack } from '@grafana/ui/src/unstable';
|
||||||
import { TagBadge } from 'app/core/components/TagFilter/TagBadge';
|
import { TagBadge } from 'app/core/components/TagFilter/TagBadge';
|
||||||
import { UserDTO } from 'app/types';
|
import { UserDTO } from 'app/types';
|
||||||
|
|
||||||
@ -69,7 +66,18 @@ export const UsersTable = ({
|
|||||||
{
|
{
|
||||||
id: 'orgs',
|
id: 'orgs',
|
||||||
header: 'Belongs to',
|
header: 'Belongs to',
|
||||||
cell: OrgUnitsCell,
|
cell: ({ cell: { value, row } }: Cell<'orgs'>) => {
|
||||||
|
return (
|
||||||
|
<Flex alignItems={'center'}>
|
||||||
|
<OrgUnits units={value} icon={'building'} />
|
||||||
|
{row.original.isAdmin && (
|
||||||
|
<Tooltip placement="top" content="Grafana Admin">
|
||||||
|
<Icon name="shield" />
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
},
|
||||||
sortType: (a, b) => (a.original.orgs?.length || 0) - (b.original.orgs?.length || 0),
|
sortType: (a, b) => (a.original.orgs?.length || 0) - (b.original.orgs?.length || 0),
|
||||||
},
|
},
|
||||||
...(showLicensedRole
|
...(showLicensedRole
|
||||||
@ -77,7 +85,18 @@ export const UsersTable = ({
|
|||||||
{
|
{
|
||||||
id: 'licensedRole',
|
id: 'licensedRole',
|
||||||
header: 'Licensed role',
|
header: 'Licensed role',
|
||||||
cell: LicensedRoleCell,
|
cell: ({ cell: { value } }: Cell<'licensedRole'>) => {
|
||||||
|
return value === 'None' ? (
|
||||||
|
<Text color={'disabled'}>
|
||||||
|
Not assigned{' '}
|
||||||
|
<Tooltip placement="top" content="A licensed role will be assigned when this user signs in">
|
||||||
|
<Icon name="question-circle" />
|
||||||
|
</Tooltip>
|
||||||
|
</Text>
|
||||||
|
) : (
|
||||||
|
value
|
||||||
|
);
|
||||||
|
},
|
||||||
// Needs the assertion here, the types are not inferred correctly due to the conditional assignment
|
// Needs the assertion here, the types are not inferred correctly due to the conditional assignment
|
||||||
sortType: 'string' as const,
|
sortType: 'string' as const,
|
||||||
},
|
},
|
||||||
@ -90,7 +109,9 @@ export const UsersTable = ({
|
|||||||
content: 'Time since user was seen using Grafana',
|
content: 'Time since user was seen using Grafana',
|
||||||
iconName: 'question-circle',
|
iconName: 'question-circle',
|
||||||
},
|
},
|
||||||
cell: LastSeenAtCell,
|
cell: ({ cell: { value } }: Cell<'lastSeenAtAge'>) => {
|
||||||
|
return <>{value && <>{value === '10 years' ? <Text color={'disabled'}>Never</Text> : value}</>}</>;
|
||||||
|
},
|
||||||
sortType: (a, b) => new Date(a.original.lastSeenAt!).getTime() - new Date(b.original.lastSeenAt!).getTime(),
|
sortType: (a, b) => new Date(a.original.lastSeenAt!).getTime() - new Date(b.original.lastSeenAt!).getTime(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -122,62 +143,13 @@ export const UsersTable = ({
|
|||||||
[showLicensedRole]
|
[showLicensedRole]
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<VerticalGroup spacing={'md'}>
|
<Stack gap={2}>
|
||||||
<InteractiveTable columns={columns} data={users} getRowId={(user) => String(user.id)} fetchData={fetchData} />
|
<InteractiveTable columns={columns} data={users} getRowId={(user) => String(user.id)} fetchData={fetchData} />
|
||||||
{showPaging && (
|
{showPaging && (
|
||||||
<HorizontalGroup justify={'flex-end'}>
|
<Flex justifyContent={'flex-end'}>
|
||||||
<Pagination numberOfPages={totalPages} currentPage={currentPage} onNavigate={onChangePage} />
|
<Pagination numberOfPages={totalPages} currentPage={currentPage} onNavigate={onChangePage} />
|
||||||
</HorizontalGroup>
|
</Flex>
|
||||||
)}
|
)}
|
||||||
</VerticalGroup>
|
</Stack>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const OrgUnitsCell = ({ cell: { value, row } }: Cell<'orgs'>) => {
|
|
||||||
const styles = useStyles2(getStyles);
|
|
||||||
return (
|
|
||||||
<div className={styles.row}>
|
|
||||||
<OrgUnits units={value} icon={'building'} />
|
|
||||||
{row.original.isAdmin && (
|
|
||||||
<Tooltip placement="top" content="Grafana Admin">
|
|
||||||
<Icon name="shield" />
|
|
||||||
</Tooltip>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const LicensedRoleCell = ({ cell: { value } }: Cell<'licensedRole'>) => {
|
|
||||||
const styles = useStyles2(getStyles);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{value === 'None' ? (
|
|
||||||
<span className={styles.disabled}>
|
|
||||||
Not assigned{' '}
|
|
||||||
<Tooltip placement="top" content="A licensed role will be assigned when this user signs in">
|
|
||||||
<Icon name="question-circle" />
|
|
||||||
</Tooltip>
|
|
||||||
</span>
|
|
||||||
) : (
|
|
||||||
value
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const LastSeenAtCell = ({ cell: { value } }: Cell<'lastSeenAtAge'>) => {
|
|
||||||
const styles = useStyles2(getStyles);
|
|
||||||
|
|
||||||
return <>{value && <>{value === '10 years' ? <span className={styles.disabled}>Never</span> : value}</>}</>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getStyles = (theme: GrafanaTheme2) => {
|
|
||||||
return {
|
|
||||||
disabled: css({ color: theme.colors.text.disabled }),
|
|
||||||
row: css({
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
import { css } from '@emotion/css';
|
|
||||||
import React, { useEffect, useMemo, useState } from 'react';
|
import React, { useEffect, useMemo, useState } from 'react';
|
||||||
import { connect, ConnectedProps } from 'react-redux';
|
import { connect, ConnectedProps } from 'react-redux';
|
||||||
|
|
||||||
import { GrafanaTheme2 } from '@grafana/data';
|
|
||||||
import {
|
import {
|
||||||
LinkButton,
|
LinkButton,
|
||||||
FilterInput,
|
FilterInput,
|
||||||
@ -13,12 +11,10 @@ import {
|
|||||||
Icon,
|
Icon,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
Column,
|
Column,
|
||||||
HorizontalGroup,
|
|
||||||
Pagination,
|
Pagination,
|
||||||
VerticalGroup,
|
|
||||||
useStyles2,
|
|
||||||
Avatar,
|
Avatar,
|
||||||
} from '@grafana/ui';
|
} from '@grafana/ui';
|
||||||
|
import { Stack, Flex } from '@grafana/ui/src/unstable';
|
||||||
import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA';
|
import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA';
|
||||||
import { Page } from 'app/core/components/Page/Page';
|
import { Page } from 'app/core/components/Page/Page';
|
||||||
import { fetchRoleOptions } from 'app/core/components/RolePicker/api';
|
import { fetchRoleOptions } from 'app/core/components/RolePicker/api';
|
||||||
@ -26,6 +22,7 @@ import { contextSrv } from 'app/core/services/context_srv';
|
|||||||
import { AccessControlAction, Role, StoreState, Team } from 'app/types';
|
import { AccessControlAction, Role, StoreState, Team } from 'app/types';
|
||||||
|
|
||||||
import { TeamRolePicker } from '../../core/components/RolePicker/TeamRolePicker';
|
import { TeamRolePicker } from '../../core/components/RolePicker/TeamRolePicker';
|
||||||
|
import { TableWrapper } from '../admin/Users/TableWrapper';
|
||||||
|
|
||||||
import { deleteTeam, loadTeams, changePage, changeQuery, changeSort } from './state/actions';
|
import { deleteTeam, loadTeams, changePage, changeQuery, changeSort } from './state/actions';
|
||||||
|
|
||||||
@ -50,7 +47,6 @@ export const TeamList = ({
|
|||||||
changeSort,
|
changeSort,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const [roleOptions, setRoleOptions] = useState<Role[]>([]);
|
const [roleOptions, setRoleOptions] = useState<Role[]>([]);
|
||||||
const styles = useStyles2(getStyles);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadTeams(true);
|
loadTeams(true);
|
||||||
@ -165,24 +161,24 @@ export const TeamList = ({
|
|||||||
New Team
|
New Team
|
||||||
</LinkButton>
|
</LinkButton>
|
||||||
</div>
|
</div>
|
||||||
<VerticalGroup spacing={'md'}>
|
<Stack gap={2}>
|
||||||
<div className={styles.wrapper}>
|
<TableWrapper>
|
||||||
<InteractiveTable
|
<InteractiveTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={teams}
|
data={teams}
|
||||||
getRowId={(team) => String(team.id)}
|
getRowId={(team) => String(team.id)}
|
||||||
fetchData={changeSort}
|
fetchData={changeSort}
|
||||||
/>
|
/>
|
||||||
<HorizontalGroup justify="flex-end">
|
<Flex justifyContent="flex-end">
|
||||||
<Pagination
|
<Pagination
|
||||||
hideWhenSinglePage
|
hideWhenSinglePage
|
||||||
currentPage={page}
|
currentPage={page}
|
||||||
numberOfPages={totalPages}
|
numberOfPages={totalPages}
|
||||||
onNavigate={changePage}
|
onNavigate={changePage}
|
||||||
/>
|
/>
|
||||||
</HorizontalGroup>
|
</Flex>
|
||||||
</div>
|
</TableWrapper>
|
||||||
</VerticalGroup>
|
</Stack>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Page.Contents>
|
</Page.Contents>
|
||||||
@ -190,24 +186,6 @@ export const TeamList = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getStyles = (theme: GrafanaTheme2) => {
|
|
||||||
return {
|
|
||||||
// Enable RolePicker overflow
|
|
||||||
wrapper: css({
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
overflowX: 'auto',
|
|
||||||
overflowY: 'hidden',
|
|
||||||
minHeight: '100vh',
|
|
||||||
width: '100%',
|
|
||||||
'& > div': {
|
|
||||||
overflowX: 'unset',
|
|
||||||
marginBottom: theme.spacing(2),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
function shouldDisplayRolePicker(): boolean {
|
function shouldDisplayRolePicker(): boolean {
|
||||||
return (
|
return (
|
||||||
contextSrv.licensedAccessControlEnabled() &&
|
contextSrv.licensedAccessControlEnabled() &&
|
||||||
|
Loading…
Reference in New Issue
Block a user