diff --git a/package.json b/package.json index 816a7dd5e08..f6ea4315f5b 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,6 @@ "@types/react-grid-layout": "0.16.7", "@types/react-redux": "7.1.2", "@types/react-select": "2.0.15", - "@types/react-table": "6.8.5", "@types/react-test-renderer": "16.9.0", "@types/react-transition-group": "2.0.16", "@types/react-virtualized": "9.18.12", @@ -246,7 +245,6 @@ "react-popper": "1.3.3", "react-redux": "7.1.1", "react-sizeme": "2.5.2", - "react-table": "6.9.2", "react-transition-group": "2.6.1", "react-use": "12.8.0", "react-virtualized": "9.21.0", diff --git a/packages/grafana-ui/package.json b/packages/grafana-ui/package.json index 4a714093835..1888479aeb6 100644 --- a/packages/grafana-ui/package.json +++ b/packages/grafana-ui/package.json @@ -50,6 +50,7 @@ "react-highlight-words": "0.11.0", "react-popper": "1.3.3", "react-storybook-addon-props-combinations": "1.1.0", + "react-table": "7.0.0-rc.4", "react-transition-group": "2.6.1", "react-virtualized": "9.21.0", "slate": "0.47.8", diff --git a/packages/grafana-ui/src/components/Table/NewTable.mdx b/packages/grafana-ui/src/components/Table/NewTable.mdx new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/grafana-ui/src/components/Table/NewTable.story.tsx b/packages/grafana-ui/src/components/Table/NewTable.story.tsx new file mode 100644 index 00000000000..e2f12fad433 --- /dev/null +++ b/packages/grafana-ui/src/components/Table/NewTable.story.tsx @@ -0,0 +1,696 @@ +import React from 'react'; +import { NewTable } from './NewTable'; +import { withCenteredStory } from '../../utils/storybook/withCenteredStory'; +import mdx from './NewTable.mdx'; +import { DataFrame, toDataFrame } from '@grafana/data'; + +export default { + title: 'UI/Table/NewTable', + component: NewTable, + decorators: [withCenteredStory], + parameters: { + docs: { + page: mdx, + }, + }, +}; + +const mockDataFrame: DataFrame = toDataFrame({ + refId: 'A', + fields: [ + { + name: 'Time', + config: {}, + values: [ + 1575951385249, + 1575951415249, + 1575951445249, + 1575951475249, + 1575951505249, + 1575951535249, + 1575951565249, + 1575951595249, + 1575951625249, + 1575951655249, + 1575951685249, + 1575951715249, + 1575951745249, + 1575951775249, + 1575951805249, + 1575951835249, + 1575951865249, + 1575951895249, + 1575951925249, + 1575951955249, + 1575951985249, + 1575952015249, + 1575952045249, + 1575952075249, + 1575952105249, + 1575952135249, + 1575952165249, + 1575952195249, + 1575952225249, + 1575952255249, + 1575952285249, + 1575952315249, + 1575952345249, + 1575952375249, + 1575952405249, + 1575952435249, + 1575952465249, + 1575952495249, + 1575952525249, + 1575952555249, + 1575952585249, + 1575952615249, + 1575952645249, + 1575952675249, + 1575952705249, + 1575952735249, + 1575952765249, + 1575952795249, + 1575952825249, + 1575952855249, + 1575952885249, + 1575952915249, + 1575952945249, + 1575952975249, + 1575953005249, + 1575953035249, + 1575953065249, + 1575953095249, + 1575953125249, + 1575953155249, + 1575953185249, + 1575953215249, + 1575953245249, + 1575953275249, + 1575953305249, + 1575953335249, + 1575953365249, + 1575953395249, + 1575953425249, + 1575953455249, + 1575953485249, + 1575953515249, + 1575953545249, + 1575953575249, + 1575953605249, + 1575953635249, + 1575953665249, + 1575953695249, + 1575953725249, + 1575953755249, + 1575953785249, + 1575953815249, + 1575953845249, + 1575953875249, + 1575953905249, + 1575953935249, + 1575953965249, + 1575953995249, + 1575954025249, + 1575954055249, + 1575954085249, + 1575954115249, + 1575954145249, + 1575954175249, + 1575954205249, + 1575954235249, + 1575954265249, + 1575954295249, + 1575954325249, + 1575954355249, + 1575954385249, + 1575954415249, + 1575954445249, + 1575954475249, + 1575954505249, + 1575954535249, + 1575954565249, + 1575954595249, + 1575954625249, + 1575954655249, + 1575954685249, + 1575954715249, + 1575954745249, + 1575954775249, + 1575954805249, + 1575954835249, + 1575954865249, + 1575954895249, + 1575954925249, + 1575954955249, + 1575954985249, + 1575955015249, + 1575955045249, + 1575955075249, + 1575955105249, + 1575955135249, + ], + type: 'time', + calcs: null, + }, + { + name: 'Value', + config: {}, + values: [ + 1.5000254936150939, + 1.0764011931793371, + 0.909466911538386, + 1.3833044968776655, + 1.6330889934233457, + 1.6709668856700475, + 1.2645776897559702, + 1.4943939986749317, + 1.2437720606210307, + 0.7883544129607633, + 0.5531910797833525, + 1.0457642543811316, + 1.382667661114637, + 1.1568426549305126, + 1.1402599321207862, + 0.8483451908731177, + 1.0317957901350685, + 0.920556237728358, + 0.5119097189376975, + 0.3251335563131686, + 0.7108123250650906, + 0.4363759782763406, + 0.8734924745632022, + 0.7937634746603249, + 0.68612096153115, + 0.18988522007983766, + 0.6576558170343919, + 0.25301964245089764, + 0.5277493691957931, + 0.46587339439817715, + 0.36227094078701305, + 0.778938585446131, + 1.2377378919033881, + 0.9610752640855127, + 1.1282013840938148, + 0.9283745710857219, + 0.674699599869639, + 0.8974372977635153, + 0.4075745811903667, + 0.26659108532003495, + 0.38371243779290887, + 0.7832793846413076, + 0.6259127747122668, + 0.37112598577068756, + 0.0613275616200267, + 0.3576963952766255, + 0.1665079834081022, + 0.38990450556730366, + 0.2214623170358675, + 0.20133177397788687, + 0.3346036375598696, + -0.0192909674691417, + -0.3396463212802728, + -0.4335565435802561, + -0.7225467771514991, + -0.9774803860859353, + -0.5596691380280212, + -0.8782204226162458, + -1.0379530805725203, + -0.6536190918926427, + -0.6041277845604902, + -0.5788054694749062, + -0.2328568877382495, + 0.20668025552369196, + 0.28117302534150723, + 0.05146978829879428, + 0.10096018560031056, + 0.4377253335922943, + 0.15035296580222518, + 0.4462013793721102, + 0.24812034480027956, + 0.2889496029069344, + 0.6625835159165457, + 0.8233581741905972, + 0.8643260393590533, + 0.540435640675776, + 0.22824187203884172, + 0.10870831703051109, + 0.554460912136038, + 0.6954620954703324, + 0.865285151446149, + 0.4590424126447909, + 0.9320188770520323, + 1.2566497496880387, + 1.092195469867563, + 1.1523766803990934, + 1.1257555973909357, + 1.0506441818153138, + 1.5101269591969075, + 1.4541940510170406, + 1.0403320961662672, + 0.6435721407405451, + 0.8163247288505439, + 0.4178187803641794, + 0.09659517321642153, + 0.45565501812146547, + 0.7800744768194877, + 1.1266848495467974, + 1.2708917952148475, + 1.3699637357813657, + 1.119789948351233, + 1.117797940539129, + 1.00138292407247, + -0.5357586740093938, + 0.9678918429118637, + 1.1063917602204467, + 0.6309338947655425, + 0.5056026636112202, + 0.18233746755391178, + 0.48969623185962496, + 0.0383434172496539, + 0.4599318303590843, + 0.5291405395663644, + 0.05265539291739823, + 0.08586187516518445, + 0.3449932185560655, + -0.100853852934114, + 0.3440262305668972, + 0.5284856664940929, + 0.6022770024221724, + 0.9832806104256895, + 0.8094695843624107, + 1.242977252512111, + 0.9569361971250467, + 0.7403221157223554, + 0.5468202659043195, + ], + type: 'number', + calcs: null, + }, + { + name: 'Min', + config: {}, + values: [ + 0.5129631778211692, + 0.7232700407273998, + 0.16692418924289476, + 1.024024764754911, + 0.42165950459107204, + -0.3468147988918235, + -0.3167406429414228, + 0.6210986915363178, + 0.561637573529631, + -1.5683226420805032, + -0.6132946301719228, + -1.2930551884568044, + 0.5969846324236683, + -0.6778519171751289, + 1.0732733813642974, + -0.0485285093265273, + -1.4112601180935862, + 0.4756584208435448, + -1.012064501202603, + -0.5689593016201331, + -1.3031450412933574, + -1.7399086775804553, + -0.4439715146977987, + -0.9065877953376303, + 0.6573706451163406, + -0.444001654734782, + -0.8360433807048399, + -1.52827092245891, + -0.7065563339902561, + -0.865577257288909, + 0.3329923695343399, + 1.5753706037331285, + -1.0567027770721693, + -0.2765803248460174, + -0.5510056746352008, + -0.1362911756340101, + 0.34259473615447383, + -0.862039744634204, + -0.2241446290872408, + 0.0566330081088942, + -1.786862521362104, + -1.2217196242864676, + -0.5525489161472246, + -0.446196522037665, + -1.08755269321232, + -0.991794378457034, + -1.3196225564796082, + 0.3304532639159836, + -0.6722727699185566, + -0.280099872766854, + -0.6484766249626335, + -2.2303404241475, + -1.28856885905242, + -2.07345573739152, + -2.581726910011512, + -2.692554424690295, + -1.899011280327467, + -2.276770149534113, + -2.906877502022452, + -2.07669049858435, + -1.862840289223948, + -1.086158472280301, + -1.57239417318637, + -1.39281505610553, + -1.750680246703866, + -1.528573710879417, + -1.281799433260939, + -0.2599408767486406, + -1.586049427178506, + -1.6670976842241279, + -0.684309922512285, + -0.8545089289126312, + 0.5201061712499606, + -1.6652198364354112, + -0.9509609321749597, + 0.4192582642664334, + -1.196222288493242, + -0.494764687123139, + 1.3552818811266552, + -1.0234990807153594, + 0.46793077094438473, + -1.7338037743327357, + -1.5493688554047804, + 0.255316081309259, + 0.7604396194452134, + -0.9407405613725452, + -0.943211396012134, + 0.8183880116376108, + 1.2329439293285518, + -0.7894319837927357, + -0.1457874726259571, + -1.5441109867114653, + -1.6234083951372025, + 0.06122646291653344, + -0.672772416353517, + -1.855602297194385, + -0.8581361043718111, + -1.2492249157608226, + 0.7605483882403175, + 0.6895236955186106, + 0.5298057924915338, + 0.6476408123812207, + 0.8803790600375676, + -1.6612590895779469, + 0.39253794740960923, + 0.09690185316807232, + 0.6185716145887097, + -1.009104994225043, + -0.231484679530671, + -1.471267622823986, + -0.0833848908404255, + -0.8881970225399474, + -1.8962127836845482, + 0.009913417830268, + -0.134180309099165, + -0.1502931584064166, + -0.91729270269899, + -1.3759721816599786, + 0.16530985337628945, + -1.134691317842945, + -0.2562806810548419, + -0.1965185306373604, + 0.6468148901350499, + -0.5662636534063714, + -1.32379253681237, + -1.702413625201898, + ], + type: 'number', + calcs: null, + }, + { + name: 'Max', + config: {}, + values: [ + 2.8039573837115217, + 2.6926679712037664, + 1.372618545664734, + 0.2330280293445357, + 3.257945982361415, + 3.760438343013144, + 3.059552434000727, + 1.77318515129639, + 0.8064077504286082, + 1.944602327610411, + 1.4599691331772737, + 1.7681328471222444, + 0.577781199251837, + 3.3997499156309985, + 2.9528330295399954, + 2.430318614428602, + 2.3611702277354194, + 2.630203980321264, + 2.6988109280000505, + 2.3242683979742083, + 2.917442297706927, + 2.2740903408929314, + 3.2823972469296656, + 1.5795427555845518, + 0.7227749081990877, + 1.07447590570726, + 1.6951780861683337, + 0.368757195632994, + 2.3404684729518603, + 2.130240287562029, + 1.3899466672909542, + 0.8059006834071859, + 1.3584656113532596, + 3.133274081047666, + 3.002448585766932, + 1.787231129006741, + 1.4640413244581476, + 3.276124198626958, + 2.087269772786033, + 2.30377367911983, + 1.4608814767279665, + 1.8833281423383506, + 1.3067082695296433, + 1.253273064443, + 1.969057793049115, + 1.3339218780045436, + 2.062355825798883, + 1.1698837256390395, + 2.6118487933225496, + 2.03861316329396, + 0.8473854714851563, + 2.2316983803163, + 0.540932137109768, + -0.04956492174973, + -0.450813944553707, + 0.375695756046867, + 0.785759004771657, + 0.0954526298052838, + 0.59597948146765, + 0.6572571584655279, + 0.605149574038378, + 1.870031884630303, + 1.37713543903307, + 0.914062948450487, + 2.649030343824894, + 0.579644863230952, + 1.944602570596079, + 0.7252492696045203, + 1.257270614288119, + 1.9385686341149715, + 1.797919709901303, + 2.5158662971442625, + 1.4514368283700079, + 0.866789052718946, + 1.3049445896679253, + 2.198601835481704, + 1.8277158002442289, + 1.24254816068483, + 0.990008891408783, + 3.181979463237438, + 1.206035697890917, + 1.1511435801749499, + 2.495205603723621, + 0.574817574306939, + 0.383599298845083, + 3.6200850261933706, + 3.5922041855779407, + 1.5478860273718356, + 2.8506619018807706, + 3.4281960853527425, + 3.289980262325269, + 1.103020879390874, + 1.942842426855671, + 0.857081451423311, + 2.112393659806772, + 1.601319369146539, + 2.230806028974528, + 1.5729019554294, + 2.4371495138987163, + 2.2635324007929634, + 0.5089790871644464, + 1.3764330789309522, + 0.535805463302483, + 2.6704309768995014, + 1.605171233903551, + 1.3849464601885664, + 1.4699084469214156, + 1.2065460833969008, + 2.665190338566064, + 2.65455092203953, + 1.33562376437657, + 1.6303855496985555, + 2.2931655808635956, + 0.53289540133395, + 2.42344985717817, + 1.034880799185153, + 2.02710661062796, + 0.589535726373407, + 1.1198114199523561, + 2.3500012113011195, + 2.911904933444892, + 3.271532648889891, + 2.181016258408353, + 3.1900649798681133, + 0.154494474449462, + 2.7911175973201736, + ], + type: 'number', + calcs: null, + }, + { + name: 'Info', + config: {}, + values: [ + 'up', + 'down fast', + 'down', + 'up fast', + 'up', + 'up', + 'down fast', + 'up', + 'down', + 'down fast', + 'down', + 'up fast', + 'up', + 'down', + 'down', + 'down', + 'up', + 'down', + 'down fast', + 'down', + 'up', + 'down', + 'up fast', + 'down', + 'down', + 'down fast', + 'up fast', + 'down fast', + 'up', + 'down', + 'down', + 'up fast', + 'up fast', + 'down', + 'up', + 'down', + 'down', + 'up', + 'down fast', + 'down', + 'up', + 'up', + 'down', + 'down', + 'down fast', + 'up fast', + 'down', + 'up', + 'down', + 'down', + 'up', + 'down', + 'down', + 'down', + 'down', + 'down', + 'up fast', + 'down', + 'down', + 'up', + 'up', + 'up', + 'up', + 'up fast', + 'up', + 'down', + 'up', + 'up', + 'down', + 'up', + 'down', + 'up', + 'up', + 'up', + 'up', + 'down', + 'down', + 'down', + 'up fast', + 'up', + 'up', + 'down fast', + 'up fast', + 'up', + 'down', + 'up', + 'down', + 'down', + 'up fast', + 'down', + 'down fast', + 'down', + 'up', + 'down', + 'down', + 'up', + 'up', + 'up', + 'up', + 'up', + 'down', + 'down', + 'down', + 'down fast', + 'up fast', + 'up', + 'down fast', + 'down', + 'down', + 'up', + 'down fast', + 'up fast', + 'up', + 'down fast', + 'up', + 'up', + 'down fast', + 'up fast', + 'up', + 'up', + 'up', + 'down', + 'up fast', + 'down', + 'down', + 'down', + ], + type: 'string', + calcs: null, + }, + ], +}); + +export const simple = () => { + return ; +}; diff --git a/packages/grafana-ui/src/components/Table/NewTable.tsx b/packages/grafana-ui/src/components/Table/NewTable.tsx new file mode 100644 index 00000000000..ee4ae972426 --- /dev/null +++ b/packages/grafana-ui/src/components/Table/NewTable.tsx @@ -0,0 +1,140 @@ +import React, { useMemo } from 'react'; +import { DataFrame, GrafanaTheme } from '@grafana/data'; +// @ts-ignore +import { useBlockLayout, useSortBy, useTable } from 'react-table'; +import { FixedSizeList } from 'react-window'; +import { css } from 'emotion'; +import { stylesFactory, useTheme, selectThemeVariant as stv } from '../../themes'; + +export interface Props { + data: DataFrame; + width: number; + height: number; + onCellClick?: (key: string, value: string) => void; +} + +const getTableData = (data: DataFrame) => { + const tableData = []; + + for (let i = 0; i < data.length; i++) { + const row: { [key: string]: string | number } = {}; + for (let j = 0; j < data.fields.length; j++) { + const prop = data.fields[j].name; + row[prop] = data.fields[j].values.get(i); + } + tableData.push(row); + } + + return tableData; +}; + +const getColumns = (data: DataFrame) => { + return data.fields.map(field => { + return { + Header: field.name, + accessor: field.name, + field: field, + }; + }); +}; + +const getTableStyles = stylesFactory((theme: GrafanaTheme, columnWidth: number) => { + const colors = theme.colors; + const headerBg = stv({ light: colors.gray6, dark: colors.dark7 }, theme.type); + const padding = 5; + + return { + cellHeight: padding * 2 + 14 * 1.5 + 2, + tableHeader: css` + padding: ${padding}px 10px; + background: ${headerBg}; + + cursor: pointer; + white-space: nowrap; + + color: ${colors.blue}; + border-bottom: 2px solid ${colors.bodyBg}; + `, + tableCell: css` + display: 'table-cell'; + padding: ${padding}px 10px; + + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + width: ${columnWidth}px; + + border-right: 2px solid ${colors.bodyBg}; + border-bottom: 2px solid ${colors.bodyBg}; + `, + }; +}); + +const renderCell = (cell: any, columnWidth: number, cellStyles: string, onCellClick?: any) => { + const filterable = cell.column.field.config.filterable; + const style = { + cursor: `${filterable && onCellClick ? 'pointer' : 'default'}`, + }; + + return ( +
onCellClick(cell.column.Header, cell.value) : undefined} + style={style} + > + {cell.render('Cell')} +
+ ); +}; + +export const NewTable = ({ data, height, onCellClick, width }: Props) => { + const theme = useTheme(); + const columnWidth = Math.floor(width / data.fields.length); + const tableStyles = getTableStyles(theme, columnWidth); + const { getTableProps, headerGroups, rows, prepareRow } = useTable( + { + columns: useMemo(() => getColumns(data), [data]), + data: useMemo(() => getTableData(data), [data]), + }, + useSortBy, + useBlockLayout + ); + + const RenderRow = React.useCallback( + ({ index, style }) => { + const row = rows[index]; + prepareRow(row); + return ( +
+ {row.cells.map((cell: any) => renderCell(cell, columnWidth, tableStyles.tableCell, onCellClick))} +
+ ); + }, + [prepareRow, rows] + ); + + return ( +
+
+ {headerGroups.map((headerGroup: any) => ( +
+ {headerGroup.headers.map((column: any) => ( +
+ {column.render('Header')} + {column.isSorted ? (column.isSortedDesc ? ' 🔽' : ' 🔼') : ''} +
+ ))} +
+ ))} +
+ + {RenderRow} + +
+ ); +}; diff --git a/packages/grafana-ui/src/components/Table/_Table.scss b/packages/grafana-ui/src/components/Table/_Table.scss deleted file mode 100644 index bb82d021b4b..00000000000 --- a/packages/grafana-ui/src/components/Table/_Table.scss +++ /dev/null @@ -1,81 +0,0 @@ -// .ReactVirtualized__Table { -// } - -// .ReactVirtualized__Table__Grid { -// } - -.ReactVirtualized__Table__headerRow { - font-weight: 700; - display: flex; - flex-direction: row; - align-items: left; -} -.ReactVirtualized__Table__row { - display: flex; - flex-direction: row; - align-items: center; - - border-bottom: 2px solid $body-bg; -} - -.ReactVirtualized__Table__headerTruncatedText { - display: inline-block; - max-width: 100%; - white-space: nowrap; - text-overflow: ellipsis; - overflow: hidden; -} - -.ReactVirtualized__Table__headerColumn, -.ReactVirtualized__Table__rowColumn { - margin-right: 10px; - min-width: 0px; -} - -.ReactVirtualized__Table__headerColumn:first-of-type, -.ReactVirtualized__Table__rowColumn:first-of-type { - margin-left: 10px; -} -.ReactVirtualized__Table__sortableHeaderColumn { - cursor: pointer; -} - -.ReactVirtualized__Table__sortableHeaderIconContainer { - align-items: center; -} -.ReactVirtualized__Table__sortableHeaderIcon { - flex: 0 0 24px; - height: 1em; - width: 1em; - fill: currentColor; -} - -.gf-table-header { - padding: 3px 10px; - - background: $list-item-bg; - border-top: 2px solid $body-bg; - border-bottom: 2px solid $body-bg; - - cursor: pointer; - white-space: nowrap; - - color: $blue; -} - -.gf-table-cell { - padding: 3px 10px; - - background: $page-gradient; - - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; - - border-right: 2px solid $body-bg; - border-bottom: 2px solid $body-bg; -} - -.gf-table-fixed-column { - border-right: 1px solid #ccc; -} diff --git a/packages/grafana-ui/src/components/index.scss b/packages/grafana-ui/src/components/index.scss index 9b32d59e278..7337a21ea1e 100644 --- a/packages/grafana-ui/src/components/index.scss +++ b/packages/grafana-ui/src/components/index.scss @@ -9,7 +9,6 @@ @import 'PanelOptionsGroup/PanelOptionsGroup'; @import 'RefreshPicker/RefreshPicker'; @import 'Select/Select'; -@import 'Table/Table'; @import 'Table/TableInputCSV'; @import 'ThresholdsEditor/ThresholdsEditor'; @import 'TimePicker/TimeOfDayPicker'; diff --git a/packages/grafana-ui/src/components/index.ts b/packages/grafana-ui/src/components/index.ts index 6b9ada39e07..50747bc0dd2 100644 --- a/packages/grafana-ui/src/components/index.ts +++ b/packages/grafana-ui/src/components/index.ts @@ -46,7 +46,7 @@ export { QueryField } from './QueryField/QueryField'; // Renderless export { SetInterval } from './SetInterval/SetInterval'; -export { Table } from './Table/Table'; +export { NewTable as Table } from './Table/NewTable'; export { TableInputCSV } from './Table/TableInputCSV'; // Visualizations diff --git a/public/app/features/explore/Explore.tsx b/public/app/features/explore/Explore.tsx index 6a6a2f81b4e..b455516c481 100644 --- a/public/app/features/explore/Explore.tsx +++ b/public/app/features/explore/Explore.tsx @@ -315,7 +315,7 @@ export class Explore extends React.PureComponent { /> )} {mode === ExploreMode.Metrics && ( - + )} {mode === ExploreMode.Logs && ( void; -} - -function prepareRows(rows: any[], columnNames: string[]) { - return rows.map(cells => _.zipObject(columnNames, cells)); -} - -export default class Table extends PureComponent { - getCellProps = (state: any, rowInfo: RowInfo, column: any) => { - return { - onClick: (e: React.SyntheticEvent) => { - // Only handle click on link, not the cell - if (e.target) { - const link = e.target as HTMLElement; - if (link.className === 'link') { - const columnKey = column.Header().props.title; - const rowValue = rowInfo.row[columnKey]; - this.props.onClickCell?.(columnKey, rowValue); - } - } - }, - }; - }; - - render() { - const { data, loading } = this.props; - const tableModel = data || EMPTY_TABLE; - const columnNames = tableModel.columns.map(({ text }) => text); - const columns = tableModel.columns.map(({ filterable, text }) => ({ - Header: () => {text}, - accessor: text, - className: VALUE_REGEX.test(text) ? 'text-right' : '', - show: text !== 'Time', - Cell: (row: any) => ( - - {typeof row.value === 'string' ? row.value : JSON.stringify(row.value)} - - ), - })); - const noDataText = data ? 'The queries returned no data for a table.' : ''; - - return ( - prepareRows(data, columnNames)} - showPagination={Boolean(data)} - /> - ); - } -} diff --git a/public/app/features/explore/TableContainer.tsx b/public/app/features/explore/TableContainer.tsx index c0934532fc0..2e7452ffc81 100644 --- a/public/app/features/explore/TableContainer.tsx +++ b/public/app/features/explore/TableContainer.tsx @@ -1,21 +1,19 @@ import React, { PureComponent } from 'react'; import { hot } from 'react-hot-loader'; import { connect } from 'react-redux'; -import { Collapse } from '@grafana/ui'; - +import { DataFrame } from '@grafana/data'; +import { Table, Collapse } from '@grafana/ui'; import { ExploreId, ExploreItemState } from 'app/types/explore'; import { StoreState } from 'app/types'; - import { toggleTable } from './state/actions'; -import Table from './Table'; -import TableModel from 'app/core/table_model'; interface TableContainerProps { exploreId: ExploreId; loading: boolean; + width: number; onClickCell: (key: string, value: string) => void; showingTable: boolean; - tableResult?: TableModel; + tableResult?: DataFrame; toggleTable: typeof toggleTable; } @@ -24,12 +22,27 @@ export class TableContainer extends PureComponent { this.props.toggleTable(this.props.exploreId, this.props.showingTable); }; + getTableHeight() { + const { tableResult } = this.props; + + if (!tableResult || tableResult.length === 0) { + return 200; + } + + // tries to estimate table height + return Math.max(Math.min(600, tableResult.length * 35) + 35); + } + render() { - const { loading, onClickCell, showingTable, tableResult } = this.props; + const { loading, onClickCell, showingTable, tableResult, width } = this.props; + + const height = this.getTableHeight(); + const paddingWidth = 16; + const tableWidth = width - paddingWidth; return ( - {tableResult && } + {tableResult &&
} ); } @@ -40,7 +53,7 @@ function mapStateToProps(state: StoreState, { exploreId }: { exploreId: string } // @ts-ignore const item: ExploreItemState = explore[exploreId]; const { loading: loadingInState, showingTable, tableResult } = item; - const loading = tableResult && tableResult.rows.length > 0 ? false : loadingInState; + const loading = tableResult && tableResult.length > 0 ? false : loadingInState; return { loading, showingTable, tableResult }; } diff --git a/public/app/features/explore/state/reducers.test.ts b/public/app/features/explore/state/reducers.test.ts index 6e5d33767e7..28d51afa526 100644 --- a/public/app/features/explore/state/reducers.test.ts +++ b/public/app/features/explore/state/reducers.test.ts @@ -24,8 +24,7 @@ import { Reducer } from 'redux'; import { ActionOf } from 'app/core/redux/actionCreatorFactory'; import { updateLocation } from 'app/core/actions/location'; import { serializeStateToUrlParam } from 'app/core/utils/explore'; -import TableModel from 'app/core/table_model'; -import { DataSourceApi, DataQuery, LogsDedupStrategy, dateTime, LoadingState } from '@grafana/data'; +import { DataSourceApi, DataQuery, LogsDedupStrategy, dateTime, LoadingState, toDataFrame } from '@grafana/data'; describe('Explore item reducer', () => { describe('scanning', () => { @@ -174,12 +173,23 @@ describe('Explore item reducer', () => { describe('when toggleTableAction is dispatched', () => { it('then it should set correct state', () => { + const table = toDataFrame({ + name: 'logs', + fields: [ + { + name: 'time', + type: 'number', + values: [1, 2], + }, + ], + }); + reducerTester() - .givenReducer(itemReducer, { tableResult: {} }) + .givenReducer(itemReducer, { tableResult: table }) .whenActionIsDispatched(toggleTableAction({ exploreId: ExploreId.left })) - .thenStateShouldEqual({ showingTable: true, tableResult: {} }) + .thenStateShouldEqual({ showingTable: true, tableResult: table }) .whenActionIsDispatched(toggleTableAction({ exploreId: ExploreId.left })) - .thenStateShouldEqual({ showingTable: false, tableResult: new TableModel() }); + .thenStateShouldEqual({ showingTable: false, tableResult: null }); }); }); }); diff --git a/public/app/features/explore/state/reducers.ts b/public/app/features/explore/state/reducers.ts index 15d16bf048e..9a165c761e6 100644 --- a/public/app/features/explore/state/reducers.ts +++ b/public/app/features/explore/state/reducers.ts @@ -62,7 +62,6 @@ import { import { reducerFactory, ActionOf } from 'app/core/redux'; import { updateLocation } from 'app/core/actions/location'; import { LocationUpdate } from '@grafana/runtime'; -import TableModel from 'app/core/table_model'; import { ResultProcessor } from '../utils/ResultProcessor'; export const DEFAULT_RANGE = { @@ -448,7 +447,7 @@ export const itemReducer = reducerFactory({} as ExploreItemSta return { ...state, showingTable }; } - return { ...state, showingTable, tableResult: new TableModel() }; + return { ...state, showingTable, tableResult: null }; }, }) .addMapper({ diff --git a/public/app/features/explore/utils/ResultProcessor.test.ts b/public/app/features/explore/utils/ResultProcessor.test.ts index 266024df0c1..70349321dcf 100644 --- a/public/app/features/explore/utils/ResultProcessor.test.ts +++ b/public/app/features/explore/utils/ResultProcessor.test.ts @@ -131,21 +131,23 @@ describe('ResultProcessor', () => { it('then it should return correct table result', () => { const { resultProcessor } = testContext(); const theResult = resultProcessor.getTableResult(); + const resultDataFrame = toDataFrame( + new TableModel({ + columns: [ + { text: 'value', type: 'number' }, + { text: 'time', type: 'time' }, + { text: 'message', type: 'string' }, + ], + rows: [ + [4, 100, 'this is a message'], + [5, 200, 'second message'], + [6, 300, 'third'], + ], + type: 'table', + }) + ); - expect(theResult).toEqual({ - columnMap: {}, - columns: [ - { text: 'value', type: 'number', filterable: undefined }, - { text: 'time', type: 'time', filterable: undefined }, - { text: 'message', type: 'string', filterable: undefined }, - ], - rows: [ - [4, 100, 'this is a message'], - [5, 200, 'second message'], - [6, 300, 'third'], - ], - type: 'table', - }); + expect(theResult).toEqual(resultDataFrame); }); }); diff --git a/public/app/features/explore/utils/ResultProcessor.ts b/public/app/features/explore/utils/ResultProcessor.ts index 64ed93f690b..5e290d43b16 100644 --- a/public/app/features/explore/utils/ResultProcessor.ts +++ b/public/app/features/explore/utils/ResultProcessor.ts @@ -1,5 +1,4 @@ -import { LogsModel, GraphSeriesXY, DataFrame, FieldType, TimeZone } from '@grafana/data'; - +import { LogsModel, GraphSeriesXY, DataFrame, FieldType, TimeZone, toDataFrame } from '@grafana/data'; import { ExploreItemState, ExploreMode } from 'app/types/explore'; import TableModel, { mergeTablesIntoModel } from 'app/core/table_model'; import { sortLogsResult, refreshIntervalToSortOrder } from 'app/core/utils/explore'; @@ -34,7 +33,7 @@ export class ResultProcessor { ); } - getTableResult(): TableModel | null { + getTableResult(): DataFrame | null { if (this.state.mode !== ExploreMode.Metrics) { return null; } @@ -75,7 +74,8 @@ export class ResultProcessor { }); }); - return mergeTablesIntoModel(new TableModel(), ...tables); + const mergedTable = mergeTablesIntoModel(new TableModel(), ...tables); + return toDataFrame(mergedTable); } getLogsResult(): LogsModel | null { diff --git a/public/app/plugins/panel/table2/TablePanel.tsx b/public/app/plugins/panel/table2/TablePanel.tsx index 659503661e9..c8fc5155474 100644 --- a/public/app/plugins/panel/table2/TablePanel.tsx +++ b/public/app/plugins/panel/table2/TablePanel.tsx @@ -2,29 +2,27 @@ import React, { Component } from 'react'; // Types -import { ThemeContext } from '@grafana/ui'; +import { Table } from '@grafana/ui'; import { PanelProps } from '@grafana/data'; import { Options } from './types'; -import Table from '@grafana/ui/src/components/Table/Table'; interface Props extends PanelProps {} +// So that the table does not go all the way to the edge of the panel chrome +const paddingBottom = 35; + export class TablePanel extends Component { constructor(props: Props) { super(props); } render() { - const { data, options } = this.props; + const { data, height, width } = this.props; if (data.series.length < 1) { return
No Table Data...
; } - return ( - - {theme =>
} - - ); + return
; } } diff --git a/public/app/types/explore.ts b/public/app/types/explore.ts index 4bd117a8014..829f6551943 100644 --- a/public/app/types/explore.ts +++ b/public/app/types/explore.ts @@ -13,10 +13,10 @@ import { LogsDedupStrategy, AbsoluteTimeRange, GraphSeriesXY, + DataFrame, } from '@grafana/data'; import { Emitter } from 'app/core/core'; -import TableModel from 'app/core/table_model'; export enum ExploreMode { Metrics = 'Metrics', @@ -130,7 +130,7 @@ export interface ExploreItemState { /** * Table model that combines all query table results into a single table. */ - tableResult?: TableModel; + tableResult?: DataFrame; /** * React keys for rendering of QueryRows diff --git a/public/sass/_grafana.scss b/public/sass/_grafana.scss index fe8d2d036de..3bda3c96598 100644 --- a/public/sass/_grafana.scss +++ b/public/sass/_grafana.scss @@ -1,6 +1,3 @@ -// DEPENDENCIES -@import '../../node_modules/react-table/react-table.css'; - // MIXINS @import 'mixins/mixins'; @import 'mixins/animations'; diff --git a/yarn.lock b/yarn.lock index 050c7d28454..a6d43b4b696 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4286,13 +4286,6 @@ dependencies: "@types/react" "*" -"@types/react-table@6.8.5": - version "6.8.5" - resolved "https://registry.yarnpkg.com/@types/react-table/-/react-table-6.8.5.tgz#deb2bf2fcedcfb81e9020edbb7df0d8459ca348b" - integrity sha512-ueCsAadG1IwuuAZM+MWf2SoxbccSWweyQa9YG6xGN5cOVK3SayPOJW4MsUHGpY0V/Q+iZWgohpasliiao29O6g== - dependencies: - "@types/react" "*" - "@types/react-test-renderer@*": version "16.9.1" resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-16.9.1.tgz#9d432c46c515ebe50c45fa92c6fb5acdc22e39c4" @@ -15446,7 +15439,6 @@ npm@6.13.4: cmd-shim "^3.0.3" columnify "~1.5.4" config-chain "^1.1.12" - debuglog "*" detect-indent "~5.0.0" detect-newline "^2.1.0" dezalgo "~1.0.3" @@ -15461,7 +15453,6 @@ npm@6.13.4: has-unicode "~2.0.1" hosted-git-info "^2.8.5" iferr "^1.0.2" - imurmurhash "*" infer-owner "^1.0.4" inflight "~1.0.6" inherits "^2.0.4" @@ -15480,14 +15471,8 @@ npm@6.13.4: libnpx "^10.2.0" lock-verify "^2.1.0" lockfile "^1.0.4" - lodash._baseindexof "*" lodash._baseuniq "~4.6.0" - lodash._bindcallback "*" - lodash._cacheindexof "*" - lodash._createcache "*" - lodash._getnative "*" lodash.clonedeep "~4.5.0" - lodash.restparam "*" lodash.union "~4.6.0" lodash.uniq "~4.5.0" lodash.without "~4.4.0" @@ -18200,12 +18185,10 @@ react-syntax-highlighter@^8.0.1: prismjs "^1.8.4" refractor "^2.4.1" -react-table@6.9.2: - version "6.9.2" - resolved "https://registry.yarnpkg.com/react-table/-/react-table-6.9.2.tgz#6a59adfeb8d5deced288241ed1c7847035b5ec5f" - integrity sha512-sTbNHU8Um0xRtmCd1js873HXnXaMWeBwZoiljuj0l1d44eaqjKyYPK/3HCBbJg1yeE2O5pQJ3Km0tlm9niNL9w== - dependencies: - classnames "^2.2.5" +react-table@7.0.0-rc.4: + version "7.0.0-rc.4" + resolved "https://registry.yarnpkg.com/react-table/-/react-table-7.0.0-rc.4.tgz#88bc61747821f3c3bbbfc7e1a4a088cbe94ed9ee" + integrity sha512-NOYmNmAIvQ9sSZd5xMNSthqiZ/o5h8h28MhFQFSxCu5u3v9J8PNh7x9wYMnk737MTjoKCZWIZT/dMFCPItXzEg== react-test-renderer@16.9.0: version "16.9.0"