diff --git a/packages/grafana-ui/src/components/InteractiveTable/InteractiveTable.tsx b/packages/grafana-ui/src/components/InteractiveTable/InteractiveTable.tsx index 8801e8ec529..e556c4388fd 100644 --- a/packages/grafana-ui/src/components/InteractiveTable/InteractiveTable.tsx +++ b/packages/grafana-ui/src/components/InteractiveTable/InteractiveTable.tsx @@ -11,57 +11,90 @@ import { Icon } from '../Icon/Icon'; import { Column } from './types'; import { EXPANDER_CELL_ID, getColumns } from './utils'; -const getStyles = (theme: GrafanaTheme2) => ({ - table: css` - border-radius: ${theme.shape.borderRadius()}; - border: solid 1px ${theme.colors.border.weak}; - background-color: ${theme.colors.background.secondary}; - width: 100%; +const getStyles = (theme: GrafanaTheme2) => { + const rowHoverBg = theme.colors.emphasize(theme.colors.background.primary, 0.03); - td { - padding: ${theme.spacing(1)}; - } - - td, - th { - min-width: ${theme.spacing(3)}; - } - `, - evenRow: css` - background: ${theme.colors.background.primary}; - `, - disableGrow: css` - width: 0%; - `, - header: css` - &, - & > button { - position: relative; - white-space: nowrap; - padding: ${theme.spacing(1)}; - } - & > button { - &:after { - content: '\\00a0'; - } + return { + table: css` + border-radius: ${theme.shape.borderRadius()}; width: 100%; - height: 100%; - background: none; - border: none; - padding-right: ${theme.spacing(2.5)}; - text-align: left; - &:hover { - background-color: ${theme.colors.emphasize(theme.colors.background.secondary, 0.05)}; + + td { + padding: ${theme.spacing(1)}; } - } - `, - sortableHeader: css` - /* increases selector's specificity so that it always takes precedence over default styles */ - && { - padding: 0; - } - `, -}); + + td, + th { + min-width: ${theme.spacing(3)}; + } + `, + disableGrow: css` + width: 0%; + `, + header: css` + border-bottom: 1px solid ${theme.colors.border.weak}; + &, + & > button { + position: relative; + white-space: nowrap; + padding: ${theme.spacing(1)}; + } + & > button { + &:after { + content: '\\00a0'; + } + width: 100%; + height: 100%; + background: none; + border: none; + padding-right: ${theme.spacing(2.5)}; + text-align: left; + font-weight: ${theme.typography.fontWeightMedium}; + } + `, + row: css` + label: row; + border-bottom: 1px solid ${theme.colors.border.weak}; + + &:hover { + background-color: ${rowHoverBg}; + } + + &:last-child { + border-bottom: 0; + } + `, + expandedRow: css` + label: expanded-row-content; + border-bottom: none; + `, + expandedContentRow: css` + label: expanded-row-content; + + td { + border-bottom: 1px solid ${theme.colors.border.weak}; + position: relative; + padding: ${theme.spacing(2, 2, 2, 5)}; + + &:before { + content: ''; + position: absolute; + width: 1px; + top: 0; + left: 16px; + bottom: ${theme.spacing(2)}; + background: ${theme.colors.border.medium}; + } + } + `, + sortableHeader: css` + /* increases selector's specificity so that it always takes precedence over default styles */ + && { + padding: 0; + } + `, + }; +}; interface Props { /** @@ -161,14 +194,15 @@ export function InteractiveTable({ - {rows.map((row, rowIndex) => { - const className = cx(rowIndex % 2 === 0 && styles.evenRow); + {rows.map((row) => { const { key, ...otherRowProps } = row.getRowProps(); const rowId = getRowHTMLID(row); + // @ts-expect-error react-table doesn't ship with useExpanded types and we can't use declaration merging without affecting the table viz + const isExpanded = row.isExpanded; return ( - + {row.cells.map((cell) => { const { key, ...otherCellProps } = cell.getCellProps(); return ( @@ -178,14 +212,11 @@ export function InteractiveTable({ ); })} - { - // @ts-expect-error react-table doesn't ship with useExpanded types and we can't use declaration merging without affecting the table viz - row.isExpanded && renderExpandedRow && ( - - {renderExpandedRow(row.original)} - - ) - } + {isExpanded && renderExpandedRow && ( + + {renderExpandedRow(row.original)} + + )} ); })} diff --git a/public/app/features/correlations/CorrelationsPage.test.tsx b/public/app/features/correlations/CorrelationsPage.test.tsx index 9b9430cd34c..4de653007fc 100644 --- a/public/app/features/correlations/CorrelationsPage.test.tsx +++ b/public/app/features/correlations/CorrelationsPage.test.tsx @@ -173,12 +173,23 @@ const mocks = { reportInteraction: jest.fn(), }; -jest.mock('@grafana/runtime', () => ({ - ...jest.requireActual('@grafana/runtime'), - reportInteraction: (...args: Parameters) => { - mocks.reportInteraction(...args); - }, -})); +jest.mock('@grafana/runtime', () => { + const runtime = jest.requireActual('@grafana/runtime'); + + return { + ...runtime, + config: { + ...runtime.config, + featureToggles: { + ...runtime.config.featureToggles, + topnav: true, + }, + }, + reportInteraction: (...args: Parameters) => { + mocks.reportInteraction(...args); + }, + }; +}); beforeAll(() => { mocks.contextSrv.hasPermission.mockImplementation(() => true); diff --git a/public/app/features/correlations/CorrelationsPage.tsx b/public/app/features/correlations/CorrelationsPage.tsx index 119e5b66452..f04d6d9e1dc 100644 --- a/public/app/features/correlations/CorrelationsPage.tsx +++ b/public/app/features/correlations/CorrelationsPage.tsx @@ -144,22 +144,26 @@ export default function CorrelationsPage() { ); const data = useMemo(() => get.value, [get.value]); - const showEmptyListCTA = data?.length === 0 && !isAdding && !get.error; + const addButton = canWriteCorrelations && data?.length !== 0 && data !== undefined && !isAdding && ( + + ); return ( - +
-
+

Define how data living in different data sources relates to each other.

-
- {canWriteCorrelations && data?.length !== 0 && data !== undefined && !isAdding && ( - - )} + + {addButton}