Commit cd39c2bd by Ivana Huckova Committed by GitHub

Explore: Refactor log details table (#21044)

parent a187500c
...@@ -37,7 +37,7 @@ describe('LogDetails', () => { ...@@ -37,7 +37,7 @@ describe('LogDetails', () => {
describe('when labels are present', () => { describe('when labels are present', () => {
it('should render heading', () => { it('should render heading', () => {
const wrapper = setup(undefined, { labels: { key1: 'label1', key2: 'label2' } }); const wrapper = setup(undefined, { labels: { key1: 'label1', key2: 'label2' } });
expect(wrapper.find({ 'aria-label': 'Log labels' })).toHaveLength(1); expect(wrapper.find({ 'aria-label': 'Log Labels' })).toHaveLength(1);
}); });
it('should render labels', () => { it('should render labels', () => {
const wrapper = setup(undefined, { labels: { key1: 'label1', key2: 'label2' } }); const wrapper = setup(undefined, { labels: { key1: 'label1', key2: 'label2' } });
...@@ -47,7 +47,7 @@ describe('LogDetails', () => { ...@@ -47,7 +47,7 @@ describe('LogDetails', () => {
describe('when row entry has parsable fields', () => { describe('when row entry has parsable fields', () => {
it('should render heading ', () => { it('should render heading ', () => {
const wrapper = setup(undefined, { entry: 'test=successful' }); const wrapper = setup(undefined, { entry: 'test=successful' });
expect(wrapper.find({ 'aria-label': 'Parsed fields' })).toHaveLength(1); expect(wrapper.find({ title: 'Ad-hoc statistics' })).toHaveLength(1);
}); });
it('should render parsed fields', () => { it('should render parsed fields', () => {
const wrapper = setup(undefined, { entry: 'test=successful' }); const wrapper = setup(undefined, { entry: 'test=successful' });
...@@ -57,8 +57,8 @@ describe('LogDetails', () => { ...@@ -57,8 +57,8 @@ describe('LogDetails', () => {
describe('when row entry have parsable fields and labels are present', () => { describe('when row entry have parsable fields and labels are present', () => {
it('should render all headings', () => { it('should render all headings', () => {
const wrapper = setup(undefined, { entry: 'test=successful', labels: { key: 'label' } }); const wrapper = setup(undefined, { entry: 'test=successful', labels: { key: 'label' } });
expect(wrapper.find({ 'aria-label': 'Log labels' })).toHaveLength(1); expect(wrapper.find({ 'aria-label': 'Log Labels' })).toHaveLength(1);
expect(wrapper.find({ 'aria-label': 'Parsed fields' })).toHaveLength(1); expect(wrapper.find({ 'aria-label': 'Parsed Fields' })).toHaveLength(1);
}); });
it('should render all labels and parsed fields', () => { it('should render all labels and parsed fields', () => {
const wrapper = setup(undefined, { const wrapper = setup(undefined, {
......
...@@ -106,18 +106,20 @@ class UnThemedLogDetails extends PureComponent<Props> { ...@@ -106,18 +106,20 @@ class UnThemedLogDetails extends PureComponent<Props> {
const style = getLogRowStyles(theme, row.logLevel); const style = getLogRowStyles(theme, row.logLevel);
const labels = row.labels ? row.labels : {}; const labels = row.labels ? row.labels : {};
const labelsAvailable = Object.keys(labels).length > 0; const labelsAvailable = Object.keys(labels).length > 0;
const fields = this.getAllFields(row); const fields = this.getAllFields(row);
const parsedFieldsAvailable = fields && fields.length > 0; const parsedFieldsAvailable = fields && fields.length > 0;
return ( return (
<div className={style.logsRowDetailsTable}> <div className={style.logDetailsContainer}>
{labelsAvailable && ( <table className={style.logDetailsTable}>
<div className={style.logsRowDetailsSectionTable}> <tbody>
<div className={style.logsRowDetailsHeading} aria-label="Log labels"> {labelsAvailable && (
Log Labels: <tr>
</div> <td colSpan={5} className={style.logDetailsHeading} aria-label="Log Labels">
Log Labels:
</td>
</tr>
)}
{Object.keys(labels).map(key => { {Object.keys(labels).map(key => {
const value = labels[key]; const value = labels[key];
return ( return (
...@@ -132,14 +134,14 @@ class UnThemedLogDetails extends PureComponent<Props> { ...@@ -132,14 +134,14 @@ class UnThemedLogDetails extends PureComponent<Props> {
/> />
); );
})} })}
</div>
)}
{parsedFieldsAvailable && ( {parsedFieldsAvailable && (
<div className={style.logsRowDetailsSectionTable}> <tr>
<div className={style.logsRowDetailsHeading} aria-label="Parsed fields"> <td colSpan={5} className={style.logDetailsHeading} aria-label="Parsed Fields">
Parsed fields: Parsed Fields:
</div> </td>
</tr>
)}
{fields.map(field => { {fields.map(field => {
const { key, value, links, fieldIndex } = field; const { key, value, links, fieldIndex } = field;
return ( return (
...@@ -156,9 +158,15 @@ class UnThemedLogDetails extends PureComponent<Props> { ...@@ -156,9 +158,15 @@ class UnThemedLogDetails extends PureComponent<Props> {
/> />
); );
})} })}
</div> {!parsedFieldsAvailable && !labelsAvailable && (
)} <tr>
{!parsedFieldsAvailable && !labelsAvailable && <div aria-label="No details">No details available</div>} <td colSpan={5} aria-label="No details">
No details available
</td>
</tr>
)}
</tbody>
</table>
</div> </div>
); );
} }
......
...@@ -66,7 +66,7 @@ describe('LogDetailsRow', () => { ...@@ -66,7 +66,7 @@ describe('LogDetailsRow', () => {
}); });
expect(wrapper.find(LogLabelStats).length).toBe(0); expect(wrapper.find(LogLabelStats).length).toBe(0);
wrapper.find('[aria-label="Field stats"]').simulate('click'); wrapper.find({ title: 'Ad-hoc statistics' }).simulate('click');
expect(wrapper.find(LogLabelStats).length).toBe(1); expect(wrapper.find(LogLabelStats).length).toBe(1);
expect(wrapper.find(LogLabelStats).contains('another value')).toBeTruthy(); expect(wrapper.find(LogLabelStats).contains('another value')).toBeTruthy();
}); });
......
...@@ -28,12 +28,16 @@ interface State { ...@@ -28,12 +28,16 @@ interface State {
const getStyles = stylesFactory((theme: GrafanaTheme) => { const getStyles = stylesFactory((theme: GrafanaTheme) => {
return { return {
noHoverEffect: css` noHoverBackground: css`
label: noHoverEffect; label: noHoverBackground;
:hover { :hover {
background-color: transparent; background-color: transparent;
} }
`, `,
hoverCursor: css`
label: hoverCursor;
cursor: pointer;
`,
}; };
}); });
...@@ -82,37 +86,24 @@ class UnThemedLogDetailsRow extends PureComponent<Props, State> { ...@@ -82,37 +86,24 @@ class UnThemedLogDetailsRow extends PureComponent<Props, State> {
const styles = getStyles(theme); const styles = getStyles(theme);
const style = getLogRowStyles(theme); const style = getLogRowStyles(theme);
return ( return (
<div className={cx(style.logsRowDetailsValue, { [styles.noHoverEffect]: showFieldsStats })}> <tr className={cx(style.logDetailsValue, { [styles.noHoverBackground]: showFieldsStats })}>
{/* Action buttons - show stats/filter results */} {/* Action buttons - show stats/filter results */}
<div <td title="Ad-hoc statistics" onClick={this.showStats} className={style.logsDetailsIcon}>
title="Ad-hoc statistics" <i className={`fa fa-signal ${styles.hoverCursor}`} />
onClick={this.showStats} </td>
aria-label={'Field stats'}
className={style.logsRowDetailsIcon} <td title="Filter for value" onClick={() => isLabel && this.filterLabel()} className={style.logsDetailsIcon}>
> {isLabel && <i className={`fa fa-search-plus ${styles.hoverCursor}`} />}
<i className={'fa fa-signal'} /> </td>
</div>
{isLabel ? ( <td title="Filter out value" onClick={() => isLabel && this.filterOutLabel()} className={style.logsDetailsIcon}>
<div title="Filter for value" onClick={() => this.filterLabel()} className={style.logsRowDetailsIcon}> {isLabel && <i className={`fa fa-search-minus ${styles.hoverCursor}`} />}
<i className={'fa fa-search-plus'} /> </td>
</div>
) : (
<div className={style.logsRowDetailsIcon} />
)}
{isLabel ? (
<div title="Filter out value" onClick={() => this.filterOutLabel()} className={style.logsRowDetailsIcon}>
<i className={'fa fa-search-minus'} />
</div>
) : (
<div className={style.logsRowDetailsIcon} />
)}
{/* Key - value columns */} {/* Key - value columns */}
<div className={style.logsRowDetailsLabel}> <td className={style.logDetailsLabel}>{parsedKey}</td>
<span>{parsedKey}</span> <td className={style.logsRowCell}>
</div> {parsedValue}
<div className={style.logsRowCell}>
<span>{parsedValue}</span>
{links && {links &&
links.map(link => { links.map(link => {
return ( return (
...@@ -125,18 +116,16 @@ class UnThemedLogDetailsRow extends PureComponent<Props, State> { ...@@ -125,18 +116,16 @@ class UnThemedLogDetailsRow extends PureComponent<Props, State> {
); );
})} })}
{showFieldsStats && ( {showFieldsStats && (
<div className={style.logsRowCell}> <LogLabelStats
<LogLabelStats stats={fieldStats!}
stats={fieldStats!} label={parsedKey}
label={parsedKey} value={parsedValue}
value={parsedValue} rowCount={fieldCount}
rowCount={fieldCount} isLabel={isLabel}
isLabel={isLabel} />
/>
</div>
)} )}
</div> </td>
</div> </tr>
); );
} }
} }
......
...@@ -23,10 +23,10 @@ const getStyles = stylesFactory((theme: GrafanaTheme) => { ...@@ -23,10 +23,10 @@ const getStyles = stylesFactory((theme: GrafanaTheme) => {
return { return {
logsStats: css` logsStats: css`
label: logs-stats; label: logs-stats;
display: table-cell;
column-span: 2; column-span: 2;
background: inherit; background: inherit;
color: ${theme.colors.text}; color: ${theme.colors.text};
word-break: break-all;
`, `,
logsStatsHeader: css` logsStatsHeader: css`
label: logs-stats__header; label: logs-stats__header;
...@@ -82,7 +82,7 @@ class UnThemedLogLabelStats extends PureComponent<Props> { ...@@ -82,7 +82,7 @@ class UnThemedLogLabelStats extends PureComponent<Props> {
const otherProportion = otherCount / total; const otherProportion = otherCount / total;
return ( return (
<div className={style.logsStats}> <td className={style.logsStats}>
<div className={style.logsStatsHeader}> <div className={style.logsStatsHeader}>
<div className={style.logsStatsTitle}> <div className={style.logsStatsTitle}>
{label}: {total} of {rowCount} rows have that {isLabel ? 'label' : 'field'} {label}: {total} of {rowCount} rows have that {isLabel ? 'label' : 'field'}
...@@ -97,7 +97,7 @@ class UnThemedLogLabelStats extends PureComponent<Props> { ...@@ -97,7 +97,7 @@ class UnThemedLogLabelStats extends PureComponent<Props> {
<LogLabelStatsRow key="__OTHERS__" count={otherCount} value="Other" proportion={otherProportion} /> <LogLabelStatsRow key="__OTHERS__" count={otherCount} value="Other" proportion={otherProportion} />
)} )}
</div> </div>
</div> </td>
); );
} }
} }
......
...@@ -45,7 +45,6 @@ export const getLogRowStyles = stylesFactory((theme: GrafanaTheme, logLevel?: Lo ...@@ -45,7 +45,6 @@ export const getLogRowStyles = stylesFactory((theme: GrafanaTheme, logLevel?: Lo
label: logs-row__match-highlight; label: logs-row__match-highlight;
background: inherit; background: inherit;
padding: inherit; padding: inherit;
color: ${theme.colors.yellow}; color: ${theme.colors.yellow};
background-color: rgba(${theme.colors.yellow}, 0.1); background-color: rgba(${theme.colors.yellow}, 0.1);
`, `,
...@@ -119,14 +118,13 @@ export const getLogRowStyles = stylesFactory((theme: GrafanaTheme, logLevel?: Lo ...@@ -119,14 +118,13 @@ export const getLogRowStyles = stylesFactory((theme: GrafanaTheme, logLevel?: Lo
`, `,
logsRowCell: css` logsRowCell: css`
label: logs-row-cell; label: logs-row-cell;
display: table-cell;
word-break: break-all; word-break: break-all;
padding-right: ${theme.spacing.sm};
`, `,
logsRowToggleDetails: css` logsRowToggleDetails: css`
label: logs-row-toggle-details__level; label: logs-row-toggle-details__level;
position: relative; position: relative;
width: 15px; width: 15px;
padding-right: ${theme.spacing.sm};
font-size: 9px; font-size: 9px;
`, `,
logsRowLocalTime: css` logsRowLocalTime: css`
...@@ -153,59 +151,43 @@ export const getLogRowStyles = stylesFactory((theme: GrafanaTheme, logLevel?: Lo ...@@ -153,59 +151,43 @@ export const getLogRowStyles = stylesFactory((theme: GrafanaTheme, logLevel?: Lo
margin: 5px 0; margin: 5px 0;
`, `,
//Log details sepcific CSS //Log details sepcific CSS
logsRowDetailsTable: css` logDetailsContainer: css`
label: logs-row-details-table; label: logs-row-details-table;
display: table;
border: 1px solid ${borderColor}; border: 1px solid ${borderColor};
padding: 0 ${theme.spacing.sm} ${theme.spacing.sm};
border-radius: 3px; border-radius: 3px;
margin: 20px 0; margin: 20px 0;
padding: ${theme.spacing.sm};
padding-top: 0;
width: 100%;
cursor: default; cursor: default;
`, `,
logsRowDetailsSectionTable: css` logDetailsTable: css`
label: logs-row-details-table__section; label: logs-row-details-table;
display: table;
table-layout: fixed;
margin: 0;
width: 100%; width: 100%;
&:first-of-type {
margin-bottom: ${theme.spacing.xs};
}
`, `,
logsRowDetailsIcon: css` logsDetailsIcon: css`
label: logs-row-details__icon; label: logs-row-details__icon;
display: table-cell;
position: relative; position: relative;
width: 22px;
padding-right: ${theme.spacing.sm}; padding-right: ${theme.spacing.sm};
color: ${theme.colors.gray3}; color: ${theme.colors.gray3};
&:hover {
cursor: pointer;
}
`, `,
logsRowDetailsLabel: css` logDetailsLabel: css`
label: logs-row-details__label; label: logs-row-details__label;
display: table-cell; max-width: 25em;
padding: 0 ${theme.spacing.md} 0 ${theme.spacing.md}; min-width: 12em;
width: 14em; padding: 0 ${theme.spacing.sm};
word-break: break-all; word-break: break-all;
`, `,
logsRowDetailsHeading: css` logDetailsHeading: css`
label: logs-row-details__heading; label: logs-row-details__heading;
display: table-caption;
margin: ${theme.spacing.sm} 0 ${theme.spacing.xs};
font-weight: ${theme.typography.weight.bold}; font-weight: ${theme.typography.weight.bold};
padding: ${theme.spacing.sm} 0 ${theme.spacing.xs};
`, `,
logsRowDetailsValue: css` logDetailsValue: css`
label: logs-row-details__row; label: logs-row-details__row;
display: table-row;
line-height: 2; line-height: 2;
padding: 0 ${theme.spacing.xl} 0 ${theme.spacing.md}; padding: ${theme.spacing.sm};
position: relative; position: relative;
vertical-align: top;
cursor: default; cursor: default;
&:hover { &:hover {
background-color: ${bgColor}; background-color: ${bgColor};
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment