TablePanel: Improve and align table styles with the rest of Grafana (#60365)

* TablePanel: Improve and align table styles with rest of Grafana

* Fixing footer styles
This commit is contained in:
Torkel Ödegaard 2022-12-15 16:45:00 +01:00 committed by GitHub
parent 8b5ad5824a
commit 0fb9987d12
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 35 additions and 34 deletions

View File

@ -14,11 +14,10 @@ export interface FooterRowProps {
footerGroups: HeaderGroup[]; footerGroups: HeaderGroup[];
footerValues: FooterItem[]; footerValues: FooterItem[];
isPaginationVisible: boolean; isPaginationVisible: boolean;
height: number;
} }
export const FooterRow = (props: FooterRowProps) => { export const FooterRow = (props: FooterRowProps) => {
const { totalColumnsWidth, footerGroups, height, isPaginationVisible } = props; const { totalColumnsWidth, footerGroups, isPaginationVisible } = props;
const e2eSelectorsTable = selectors.components.Panels.Visualization.Table; const e2eSelectorsTable = selectors.components.Panels.Visualization.Table;
const tableStyles = useStyles2(getTableStyles); const tableStyles = useStyles2(getTableStyles);
@ -33,14 +32,8 @@ export const FooterRow = (props: FooterRowProps) => {
{footerGroups.map((footerGroup: HeaderGroup) => { {footerGroups.map((footerGroup: HeaderGroup) => {
const { key, ...footerGroupProps } = footerGroup.getFooterGroupProps(); const { key, ...footerGroupProps } = footerGroup.getFooterGroupProps();
return ( return (
<div <div className={tableStyles.tfoot} {...footerGroupProps} key={key} data-testid={e2eSelectorsTable.footer}>
className={tableStyles.tfoot} {footerGroup.headers.map((column: ColumnInstance) => renderFooterCell(column, tableStyles))}
{...footerGroupProps}
key={key}
data-testid={e2eSelectorsTable.footer}
style={height ? { height: `${height}px` } : undefined}
>
{footerGroup.headers.map((column: ColumnInstance) => renderFooterCell(column, tableStyles, height))}
</div> </div>
); );
})} })}
@ -48,7 +41,7 @@ export const FooterRow = (props: FooterRowProps) => {
); );
}; };
function renderFooterCell(column: ColumnInstance, tableStyles: TableStyles, height?: number) { function renderFooterCell(column: ColumnInstance, tableStyles: TableStyles) {
const footerProps = column.getHeaderProps(); const footerProps = column.getHeaderProps();
if (!footerProps) { if (!footerProps) {
@ -58,9 +51,6 @@ function renderFooterCell(column: ColumnInstance, tableStyles: TableStyles, heig
footerProps.style = footerProps.style ?? {}; footerProps.style = footerProps.style ?? {};
footerProps.style.position = 'absolute'; footerProps.style.position = 'absolute';
footerProps.style.justifyContent = (column as any).justifyContent; footerProps.style.justifyContent = (column as any).justifyContent;
if (height) {
footerProps.style.height = height;
}
return ( return (
<div className={tableStyles.headerCell} {...footerProps}> <div className={tableStyles.headerCell} {...footerProps}>

View File

@ -21,7 +21,7 @@ export const HeaderRow = (props: HeaderRowProps) => {
const tableStyles = useStyles2(getTableStyles); const tableStyles = useStyles2(getTableStyles);
return ( return (
<div role="rowgroup"> <div role="rowgroup" className={tableStyles.headerRow}>
{headerGroups.map((headerGroup: HeaderGroup) => { {headerGroups.map((headerGroup: HeaderGroup) => {
const { key, ...headerGroupProps } = headerGroup.getHeaderGroupProps(); const { key, ...headerGroupProps } = headerGroup.getHeaderGroupProps();
return ( return (
@ -62,9 +62,12 @@ function renderHeaderCell(column: any, tableStyles: TableStyles, showTypeIcons?:
<Icon name={getFieldTypeIcon(field)} title={field?.type} size="sm" className={tableStyles.typeIcon} /> <Icon name={getFieldTypeIcon(field)} title={field?.type} size="sm" className={tableStyles.typeIcon} />
)} )}
<div>{column.render('Header')}</div> <div>{column.render('Header')}</div>
<div> {column.isSorted &&
{column.isSorted && (column.isSortedDesc ? <Icon name="arrow-down" /> : <Icon name="arrow-up" />)} (column.isSortedDesc ? (
</div> <Icon size="lg" name="arrow-down" className={tableStyles.sortIcon} />
) : (
<Icon name="arrow-up" size="lg" className={tableStyles.sortIcon} />
))}
</button> </button>
{column.canFilter && <Filter column={column} tableStyles={tableStyles} field={field} />} {column.canFilter && <Filter column={column} tableStyles={tableStyles} field={field} />}
</> </>

View File

@ -150,13 +150,13 @@ export const Table = memo((props: Props) => {
const variableSizeListScrollbarRef = useRef<HTMLDivElement>(null); const variableSizeListScrollbarRef = useRef<HTMLDivElement>(null);
const tableStyles = useStyles2(getTableStyles); const tableStyles = useStyles2(getTableStyles);
const theme = useTheme2(); const theme = useTheme2();
const headerHeight = noHeader ? 0 : tableStyles.cellHeight; const headerHeight = noHeader ? 0 : tableStyles.rowHeight;
const [footerItems, setFooterItems] = useState<FooterItem[] | undefined>(footerValues); const [footerItems, setFooterItems] = useState<FooterItem[] | undefined>(footerValues);
const [expandedIndexes, setExpandedIndexes] = useState<Set<number>>(new Set()); const [expandedIndexes, setExpandedIndexes] = useState<Set<number>>(new Set());
const prevExpandedIndexes = usePrevious(expandedIndexes); const prevExpandedIndexes = usePrevious(expandedIndexes);
const footerHeight = useMemo(() => { const footerHeight = useMemo(() => {
const EXTENDED_ROW_HEIGHT = 33; const EXTENDED_ROW_HEIGHT = headerHeight;
let length = 0; let length = 0;
if (!footerItems) { if (!footerItems) {
@ -174,7 +174,7 @@ export const Table = memo((props: Props) => {
} }
return EXTENDED_ROW_HEIGHT; return EXTENDED_ROW_HEIGHT;
}, [footerItems]); }, [footerItems, headerHeight]);
// React table data array. This data acts just like a dummy array to let react-table know how many rows exist // React table data array. This data acts just like a dummy array to let react-table know how many rows exist
// The cells use the field to look up values // The cells use the field to look up values
@ -288,7 +288,9 @@ export const Table = memo((props: Props) => {
if (enablePagination) { if (enablePagination) {
listHeight -= tableStyles.cellHeight; listHeight -= tableStyles.cellHeight;
} }
const pageSize = Math.round(listHeight / tableStyles.cellHeight) - 1;
const pageSize = Math.round(listHeight / tableStyles.rowHeight) - 1;
useEffect(() => { useEffect(() => {
// Don't update the page size if it is less than 1 // Don't update the page size if it is less than 1
if (pageSize <= 0) { if (pageSize <= 0) {
@ -473,7 +475,6 @@ export const Table = memo((props: Props) => {
)} )}
{footerItems && ( {footerItems && (
<FooterRow <FooterRow
height={footerHeight}
isPaginationVisible={Boolean(enablePagination)} isPaginationVisible={Boolean(enablePagination)}
footerValues={footerItems} footerValues={footerItems}
footerGroups={footerGroups} footerGroups={footerGroups}

View File

@ -3,8 +3,6 @@ import { css, CSSObject } from '@emotion/css';
import { GrafanaTheme2 } from '@grafana/data'; import { GrafanaTheme2 } from '@grafana/data';
export const getTableStyles = (theme: GrafanaTheme2) => { export const getTableStyles = (theme: GrafanaTheme2) => {
const { colors } = theme;
const headerBg = theme.colors.background.secondary;
const borderColor = theme.colors.border.weak; const borderColor = theme.colors.border.weak;
const resizerColor = theme.colors.primary.border; const resizerColor = theme.colors.primary.border;
const cellPadding = 6; const cellPadding = 6;
@ -107,27 +105,29 @@ export const getTableStyles = (theme: GrafanaTheme2) => {
`, `,
thead: css` thead: css`
label: thead; label: thead;
height: ${cellHeight}px; height: ${rowHeight}px;
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
background: ${headerBg};
position: relative; position: relative;
`, `,
tfoot: css` tfoot: css`
label: tfoot; label: tfoot;
height: ${cellHeight}px; height: ${rowHeight}px;
border-top: 1px solid ${borderColor};
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
background: ${headerBg};
position: relative; position: relative;
`, `,
headerRow: css`
label: row;
border-bottom: 1px solid ${borderColor};
`,
headerCell: css` headerCell: css`
padding: ${cellPadding}px; padding: ${cellPadding}px;
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
color: ${colors.primary.text};
border-right: 1px solid ${theme.colors.border.weak};
display: flex; display: flex;
font-weight: ${theme.typography.fontWeightMedium};
&:last-child { &:last-child {
border-right: none; border-right: none;
@ -141,8 +141,15 @@ export const getTableStyles = (theme: GrafanaTheme2) => {
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
font-weight: ${theme.typography.fontWeightMedium};
display: flex; display: flex;
align-items: center;
margin-right: ${theme.spacing(0.5)}; margin-right: ${theme.spacing(0.5)};
&:hover {
text-decoration: underline;
color: ${theme.colors.text.link};
}
`, `,
cellContainer: buildCellContainerStyle(undefined, undefined, true), cellContainer: buildCellContainerStyle(undefined, undefined, true),
cellContainerNoOverflow: buildCellContainerStyle(undefined, undefined, false), cellContainerNoOverflow: buildCellContainerStyle(undefined, undefined, false),
@ -152,6 +159,9 @@ export const getTableStyles = (theme: GrafanaTheme2) => {
user-select: text; user-select: text;
white-space: nowrap; white-space: nowrap;
`, `,
sortIcon: css`
margin-left: ${theme.spacing(0.5)};
`,
cellLink: css` cellLink: css`
cursor: pointer; cursor: pointer;
overflow: hidden; overflow: hidden;
@ -173,12 +183,10 @@ export const getTableStyles = (theme: GrafanaTheme2) => {
`, `,
paginationWrapper: css` paginationWrapper: css`
display: flex; display: flex;
background: ${headerBg};
height: ${cellHeight}px; height: ${cellHeight}px;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
width: 100%; width: 100%;
border-top: 1px solid ${theme.colors.border.weak};
li { li {
margin-bottom: 0; margin-bottom: 0;
} }

View File

@ -20,7 +20,6 @@ const footerCategory = 'Table footer';
export const plugin = new PanelPlugin<PanelOptions, TableFieldOptions>(TablePanel) export const plugin = new PanelPlugin<PanelOptions, TableFieldOptions>(TablePanel)
.setPanelChangeHandler(tablePanelChangedHandler) .setPanelChangeHandler(tablePanelChangedHandler)
.setMigrationHandler(tableMigrationHandler) .setMigrationHandler(tableMigrationHandler)
.setNoPadding()
.useFieldConfig({ .useFieldConfig({
useCustomConfig: (builder) => { useCustomConfig: (builder) => {
builder builder