-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ui: Migrate block viewer to React (#2980)
* ui: Migrate block viewer to React Signed-off-by: Prem Kumar <[email protected]> * ui: Color blocks on basis of resolution Signed-off-by: Prem Kumar <[email protected]> * ui: Block: Allow filtering by time range Signed-off-by: Prem Kumar <[email protected]> * Hanlde errors; use different shades for different compaction levels Signed-off-by: Prem Kumar <[email protected]> * Add minTime and maxTime to query parameters Signed-off-by: Prem Kumar <[email protected]> * Make block details div sticky Signed-off-by: Prem Kumar <[email protected]> * Add tests for Blocks page Signed-off-by: Prem Kumar <[email protected]> * Fix the width of of labels Signed-off-by: Prem Kumar <[email protected]> * Add an entry to CHANGELOG.md Signed-off-by: Prem Kumar <[email protected]> * Fix rendering a gap between consecutive blocks Signed-off-by: Prem Kumar <[email protected]> * Add link to new UI in block viewer Signed-off-by: Prem Kumar <[email protected]> * Use semantic HTML tags Signed-off-by: Prem Kumar <[email protected]>
- Loading branch information
Showing
22 changed files
with
3,081 additions
and
97 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
91 changes: 91 additions & 0 deletions
91
pkg/ui/react-app/src/thanos/pages/blocks/BlockDetails.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import React from 'react'; | ||
import { mount } from 'enzyme'; | ||
import moment from 'moment'; | ||
import { BlockDetails, BlockDetailsProps } from './BlockDetails'; | ||
import { sampleAPIResponse } from './__testdata__/testdata'; | ||
|
||
const sampleBlock = sampleAPIResponse.data.blocks[0]; | ||
const formatTime = (time: number): string => { | ||
return moment.unix(time / 1000).format('LLL'); | ||
}; | ||
|
||
describe('BlockDetails', () => { | ||
const defaultProps: BlockDetailsProps = { | ||
block: sampleBlock, | ||
selectBlock: (): void => { | ||
// do nothing | ||
}, | ||
}; | ||
|
||
const blockDetails = mount(<BlockDetails {...defaultProps} />); | ||
|
||
it('renders a heading with block ulid', () => { | ||
const title = blockDetails.find({ 'data-testid': 'ulid' }); | ||
expect(title).toHaveLength(1); | ||
expect(title.text()).toEqual(sampleBlock.ulid); | ||
}); | ||
|
||
it('renders start time of the block', () => { | ||
const div = blockDetails.find({ 'data-testid': 'start-time' }); | ||
expect(div).toHaveLength(1); | ||
expect(div.find('span').text()).toBe(formatTime(sampleBlock.minTime)); | ||
}); | ||
|
||
it('renders end time of the block', () => { | ||
const div = blockDetails.find({ 'data-testid': 'end-time' }); | ||
expect(div).toHaveLength(1); | ||
expect(div.find('span').text()).toBe(formatTime(sampleBlock.maxTime)); | ||
}); | ||
|
||
it('renders duration of the block', () => { | ||
const div = blockDetails.find({ 'data-testid': 'duration' }); | ||
expect(div).toHaveLength(1); | ||
expect(div.find('span').text()).toBe(moment.duration(sampleBlock.maxTime - sampleBlock.minTime, 'ms').humanize()); | ||
}); | ||
|
||
it('renders total number of series in the block', () => { | ||
const div = blockDetails.find({ 'data-testid': 'series' }); | ||
expect(div).toHaveLength(1); | ||
expect(div.find('span').text()).toBe(sampleBlock.stats.numSeries.toString()); | ||
}); | ||
|
||
it('renders total number of samples in the block', () => { | ||
const div = blockDetails.find({ 'data-testid': 'samples' }); | ||
expect(div).toHaveLength(1); | ||
expect(div.find('span').text()).toBe(sampleBlock.stats.numSamples.toString()); | ||
}); | ||
|
||
it('renders total number of chunks in the block', () => { | ||
const div = blockDetails.find({ 'data-testid': 'chunks' }); | ||
expect(div).toHaveLength(1); | ||
expect(div.find('span').text()).toBe(sampleBlock.stats.numChunks.toString()); | ||
}); | ||
|
||
it('renders downsampling resolution of the block', () => { | ||
const div = blockDetails.find({ 'data-testid': 'resolution' }); | ||
expect(div).toHaveLength(1); | ||
expect(div.find('span').text()).toBe(sampleBlock.thanos.downsample.resolution.toString()); | ||
}); | ||
|
||
it('renders compaction level of the block', () => { | ||
const div = blockDetails.find({ 'data-testid': 'level' }); | ||
expect(div).toHaveLength(1); | ||
expect(div.find('span').text()).toBe(sampleBlock.compaction.level.toString()); | ||
}); | ||
|
||
it('renders source of the block', () => { | ||
const div = blockDetails.find({ 'data-testid': 'source' }); | ||
expect(div).toHaveLength(1); | ||
expect(div.find('span').text()).toBe(sampleBlock.thanos.source); | ||
}); | ||
|
||
it('renders a list of the labels', () => { | ||
const div = blockDetails.find({ 'data-testid': 'labels' }); | ||
const list = div.find('ul'); | ||
expect(div).toHaveLength(1); | ||
expect(list).toHaveLength(1); | ||
|
||
const labels = list.find('li'); | ||
expect(labels).toHaveLength(Object.keys(sampleBlock.thanos.labels).length); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import React, { FC } from 'react'; | ||
import { Block } from './block'; | ||
import styles from './blocks.module.css'; | ||
import moment from 'moment'; | ||
|
||
export interface BlockDetailsProps { | ||
block: Block | undefined; | ||
selectBlock: React.Dispatch<React.SetStateAction<Block | undefined>>; | ||
} | ||
|
||
export const BlockDetails: FC<BlockDetailsProps> = ({ block, selectBlock }) => { | ||
return ( | ||
<div className={`${styles.blockDetails} ${block && styles.open}`}> | ||
{block && ( | ||
<> | ||
<div className={styles.detailsTop}> | ||
<span className={styles.header} data-testid="ulid"> | ||
{block.ulid} | ||
</span> | ||
<button className={styles.closeBtn} onClick={(): void => selectBlock(undefined)}> | ||
× | ||
</button> | ||
</div> | ||
<hr /> | ||
<div data-testid="start-time"> | ||
<b>Start Time:</b> <span>{moment.unix(block.minTime / 1000).format('LLL')}</span> | ||
</div> | ||
<div data-testid="end-time"> | ||
<b>End Time:</b> <span>{moment.unix(block.maxTime / 1000).format('LLL')}</span> | ||
</div> | ||
<div data-testid="duration"> | ||
<b>Duration:</b> <span>{moment.duration(block.maxTime - block.minTime, 'ms').humanize()}</span> | ||
</div> | ||
<hr /> | ||
<div data-testid="series"> | ||
<b>Series:</b> <span>{block.stats.numSeries}</span> | ||
</div> | ||
<div data-testid="samples"> | ||
<b>Samples:</b> <span>{block.stats.numSamples}</span> | ||
</div> | ||
<div data-testid="chunks"> | ||
<b>Chunks:</b> <span>{block.stats.numChunks}</span> | ||
</div> | ||
<hr /> | ||
<div data-testid="resolution"> | ||
<b>Resolution:</b> <span>{block.thanos.downsample.resolution}</span> | ||
</div> | ||
<div data-testid="level"> | ||
<b>Level:</b> <span>{block.compaction.level}</span> | ||
</div> | ||
<div data-testid="source"> | ||
<b>Source:</b> <span>{block.thanos.source}</span> | ||
</div> | ||
<hr /> | ||
<div data-testid="labels"> | ||
<b>Labels:</b> | ||
<ul> | ||
{Object.entries(block.thanos.labels).map(([key, value]) => ( | ||
<li key={key}> | ||
<b>{key}: </b> | ||
{value} | ||
</li> | ||
))} | ||
</ul> | ||
</div> | ||
</> | ||
)} | ||
</div> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import React, { FC } from 'react'; | ||
import { Block } from './block'; | ||
import styles from './blocks.module.css'; | ||
|
||
interface BlockSpanProps { | ||
block: Block; | ||
gridMinTime: number; | ||
gridMaxTime: number; | ||
selectBlock: React.Dispatch<React.SetStateAction<Block | undefined>>; | ||
} | ||
|
||
export const BlockSpan: FC<BlockSpanProps> = ({ block, gridMaxTime, gridMinTime, selectBlock }) => { | ||
const viewWidth = gridMaxTime - gridMinTime; | ||
const spanWidth = ((block.maxTime - block.minTime) / viewWidth) * 100; | ||
const spanOffset = ((block.minTime - gridMinTime) / viewWidth) * 100; | ||
|
||
return ( | ||
<button | ||
onClick={(): void => selectBlock(block)} | ||
className={`${styles.blockSpan} ${styles[`res-${block.thanos.downsample.resolution}`]} ${ | ||
styles[`level-${block.compaction.level}`] | ||
}`} | ||
style={{ | ||
width: `calc(${spanWidth.toFixed(4)}% + 1px)`, | ||
left: `${spanOffset.toFixed(4)}%`, | ||
}} | ||
/> | ||
); | ||
}; |
Oops, something went wrong.