mirror of https://github.com/grafana/grafana.git
				
				
				
			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:
		
							parent
							
								
									8b5ad5824a
								
							
						
					
					
						commit
						0fb9987d12
					
				|  | @ -14,11 +14,10 @@ export interface FooterRowProps { | |||
|   footerGroups: HeaderGroup[]; | ||||
|   footerValues: FooterItem[]; | ||||
|   isPaginationVisible: boolean; | ||||
|   height: number; | ||||
| } | ||||
| 
 | ||||
| export const FooterRow = (props: FooterRowProps) => { | ||||
|   const { totalColumnsWidth, footerGroups, height, isPaginationVisible } = props; | ||||
|   const { totalColumnsWidth, footerGroups, isPaginationVisible } = props; | ||||
|   const e2eSelectorsTable = selectors.components.Panels.Visualization.Table; | ||||
|   const tableStyles = useStyles2(getTableStyles); | ||||
| 
 | ||||
|  | @ -33,14 +32,8 @@ export const FooterRow = (props: FooterRowProps) => { | |||
|       {footerGroups.map((footerGroup: HeaderGroup) => { | ||||
|         const { key, ...footerGroupProps } = footerGroup.getFooterGroupProps(); | ||||
|         return ( | ||||
|           <div | ||||
|             className={tableStyles.tfoot} | ||||
|             {...footerGroupProps} | ||||
|             key={key} | ||||
|             data-testid={e2eSelectorsTable.footer} | ||||
|             style={height ? { height: `${height}px` } : undefined} | ||||
|           > | ||||
|             {footerGroup.headers.map((column: ColumnInstance) => renderFooterCell(column, tableStyles, height))} | ||||
|           <div className={tableStyles.tfoot} {...footerGroupProps} key={key} data-testid={e2eSelectorsTable.footer}> | ||||
|             {footerGroup.headers.map((column: ColumnInstance) => renderFooterCell(column, tableStyles))} | ||||
|           </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(); | ||||
| 
 | ||||
|   if (!footerProps) { | ||||
|  | @ -58,9 +51,6 @@ function renderFooterCell(column: ColumnInstance, tableStyles: TableStyles, heig | |||
|   footerProps.style = footerProps.style ?? {}; | ||||
|   footerProps.style.position = 'absolute'; | ||||
|   footerProps.style.justifyContent = (column as any).justifyContent; | ||||
|   if (height) { | ||||
|     footerProps.style.height = height; | ||||
|   } | ||||
| 
 | ||||
|   return ( | ||||
|     <div className={tableStyles.headerCell} {...footerProps}> | ||||
|  |  | |||
|  | @ -21,7 +21,7 @@ export const HeaderRow = (props: HeaderRowProps) => { | |||
|   const tableStyles = useStyles2(getTableStyles); | ||||
| 
 | ||||
|   return ( | ||||
|     <div role="rowgroup"> | ||||
|     <div role="rowgroup" className={tableStyles.headerRow}> | ||||
|       {headerGroups.map((headerGroup: HeaderGroup) => { | ||||
|         const { key, ...headerGroupProps } = headerGroup.getHeaderGroupProps(); | ||||
|         return ( | ||||
|  | @ -62,9 +62,12 @@ function renderHeaderCell(column: any, tableStyles: TableStyles, showTypeIcons?: | |||
|               <Icon name={getFieldTypeIcon(field)} title={field?.type} size="sm" className={tableStyles.typeIcon} /> | ||||
|             )} | ||||
|             <div>{column.render('Header')}</div> | ||||
|             <div> | ||||
|               {column.isSorted && (column.isSortedDesc ? <Icon name="arrow-down" /> : <Icon name="arrow-up" />)} | ||||
|             </div> | ||||
|             {column.isSorted && | ||||
|               (column.isSortedDesc ? ( | ||||
|                 <Icon size="lg" name="arrow-down" className={tableStyles.sortIcon} /> | ||||
|               ) : ( | ||||
|                 <Icon name="arrow-up" size="lg" className={tableStyles.sortIcon} /> | ||||
|               ))} | ||||
|           </button> | ||||
|           {column.canFilter && <Filter column={column} tableStyles={tableStyles} field={field} />} | ||||
|         </> | ||||
|  |  | |||
|  | @ -150,13 +150,13 @@ export const Table = memo((props: Props) => { | |||
|   const variableSizeListScrollbarRef = useRef<HTMLDivElement>(null); | ||||
|   const tableStyles = useStyles2(getTableStyles); | ||||
|   const theme = useTheme2(); | ||||
|   const headerHeight = noHeader ? 0 : tableStyles.cellHeight; | ||||
|   const headerHeight = noHeader ? 0 : tableStyles.rowHeight; | ||||
|   const [footerItems, setFooterItems] = useState<FooterItem[] | undefined>(footerValues); | ||||
|   const [expandedIndexes, setExpandedIndexes] = useState<Set<number>>(new Set()); | ||||
|   const prevExpandedIndexes = usePrevious(expandedIndexes); | ||||
| 
 | ||||
|   const footerHeight = useMemo(() => { | ||||
|     const EXTENDED_ROW_HEIGHT = 33; | ||||
|     const EXTENDED_ROW_HEIGHT = headerHeight; | ||||
|     let length = 0; | ||||
| 
 | ||||
|     if (!footerItems) { | ||||
|  | @ -174,7 +174,7 @@ export const Table = memo((props: Props) => { | |||
|     } | ||||
| 
 | ||||
|     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
 | ||||
|   // The cells use the field to look up values
 | ||||
|  | @ -288,7 +288,9 @@ export const Table = memo((props: Props) => { | |||
|   if (enablePagination) { | ||||
|     listHeight -= tableStyles.cellHeight; | ||||
|   } | ||||
|   const pageSize = Math.round(listHeight / tableStyles.cellHeight) - 1; | ||||
| 
 | ||||
|   const pageSize = Math.round(listHeight / tableStyles.rowHeight) - 1; | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     // Don't update the page size if it is less than 1
 | ||||
|     if (pageSize <= 0) { | ||||
|  | @ -473,7 +475,6 @@ export const Table = memo((props: Props) => { | |||
|           )} | ||||
|           {footerItems && ( | ||||
|             <FooterRow | ||||
|               height={footerHeight} | ||||
|               isPaginationVisible={Boolean(enablePagination)} | ||||
|               footerValues={footerItems} | ||||
|               footerGroups={footerGroups} | ||||
|  |  | |||
|  | @ -3,8 +3,6 @@ import { css, CSSObject } from '@emotion/css'; | |||
| import { GrafanaTheme2 } from '@grafana/data'; | ||||
| 
 | ||||
| export const getTableStyles = (theme: GrafanaTheme2) => { | ||||
|   const { colors } = theme; | ||||
|   const headerBg = theme.colors.background.secondary; | ||||
|   const borderColor = theme.colors.border.weak; | ||||
|   const resizerColor = theme.colors.primary.border; | ||||
|   const cellPadding = 6; | ||||
|  | @ -107,27 +105,29 @@ export const getTableStyles = (theme: GrafanaTheme2) => { | |||
|     `,
 | ||||
|     thead: css` | ||||
|       label: thead; | ||||
|       height: ${cellHeight}px; | ||||
|       height: ${rowHeight}px; | ||||
|       overflow-y: auto; | ||||
|       overflow-x: hidden; | ||||
|       background: ${headerBg}; | ||||
|       position: relative; | ||||
|     `,
 | ||||
|     tfoot: css` | ||||
|       label: tfoot; | ||||
|       height: ${cellHeight}px; | ||||
|       height: ${rowHeight}px; | ||||
|       border-top: 1px solid ${borderColor}; | ||||
|       overflow-y: auto; | ||||
|       overflow-x: hidden; | ||||
|       background: ${headerBg}; | ||||
|       position: relative; | ||||
|     `,
 | ||||
|     headerRow: css` | ||||
|       label: row; | ||||
|       border-bottom: 1px solid ${borderColor}; | ||||
|     `,
 | ||||
|     headerCell: css` | ||||
|       padding: ${cellPadding}px; | ||||
|       overflow: hidden; | ||||
|       white-space: nowrap; | ||||
|       color: ${colors.primary.text}; | ||||
|       border-right: 1px solid ${theme.colors.border.weak}; | ||||
|       display: flex; | ||||
|       font-weight: ${theme.typography.fontWeightMedium}; | ||||
| 
 | ||||
|       &:last-child { | ||||
|         border-right: none; | ||||
|  | @ -141,8 +141,15 @@ export const getTableStyles = (theme: GrafanaTheme2) => { | |||
|       white-space: nowrap; | ||||
|       overflow: hidden; | ||||
|       text-overflow: ellipsis; | ||||
|       font-weight: ${theme.typography.fontWeightMedium}; | ||||
|       display: flex; | ||||
|       align-items: center; | ||||
|       margin-right: ${theme.spacing(0.5)}; | ||||
| 
 | ||||
|       &:hover { | ||||
|         text-decoration: underline; | ||||
|         color: ${theme.colors.text.link}; | ||||
|       } | ||||
|     `,
 | ||||
|     cellContainer: buildCellContainerStyle(undefined, undefined, true), | ||||
|     cellContainerNoOverflow: buildCellContainerStyle(undefined, undefined, false), | ||||
|  | @ -152,6 +159,9 @@ export const getTableStyles = (theme: GrafanaTheme2) => { | |||
|       user-select: text; | ||||
|       white-space: nowrap; | ||||
|     `,
 | ||||
|     sortIcon: css` | ||||
|       margin-left: ${theme.spacing(0.5)}; | ||||
|     `,
 | ||||
|     cellLink: css` | ||||
|       cursor: pointer; | ||||
|       overflow: hidden; | ||||
|  | @ -173,12 +183,10 @@ export const getTableStyles = (theme: GrafanaTheme2) => { | |||
|     `,
 | ||||
|     paginationWrapper: css` | ||||
|       display: flex; | ||||
|       background: ${headerBg}; | ||||
|       height: ${cellHeight}px; | ||||
|       justify-content: center; | ||||
|       align-items: center; | ||||
|       width: 100%; | ||||
|       border-top: 1px solid ${theme.colors.border.weak}; | ||||
|       li { | ||||
|         margin-bottom: 0; | ||||
|       } | ||||
|  |  | |||
|  | @ -20,7 +20,6 @@ const footerCategory = 'Table footer'; | |||
| export const plugin = new PanelPlugin<PanelOptions, TableFieldOptions>(TablePanel) | ||||
|   .setPanelChangeHandler(tablePanelChangedHandler) | ||||
|   .setMigrationHandler(tableMigrationHandler) | ||||
|   .setNoPadding() | ||||
|   .useFieldConfig({ | ||||
|     useCustomConfig: (builder) => { | ||||
|       builder | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue