Merge pull request #11858 from keithjgrant/11409-empty-job-output

add JobOutput screens for empty content
This commit is contained in:
Sarah Akus
2022-03-11 09:14:53 -05:00
committed by GitHub
4 changed files with 101 additions and 36 deletions

View File

@@ -1,6 +1,5 @@
import React from 'react'; import React from 'react';
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import { import {
Title, Title,
EmptyState, EmptyState,
@@ -9,9 +8,14 @@ import {
} from '@patternfly/react-core'; } from '@patternfly/react-core';
import { CubesIcon } from '@patternfly/react-icons'; import { CubesIcon } from '@patternfly/react-icons';
const ContentEmpty = ({ title = '', message = '' }) => ( const ContentEmpty = ({
<EmptyState variant="full"> title = '',
<EmptyStateIcon icon={CubesIcon} /> message = '',
icon = CubesIcon,
className = '',
}) => (
<EmptyState variant="full" className={className}>
<EmptyStateIcon icon={icon} />
<Title size="lg" headingLevel="h3"> <Title size="lg" headingLevel="h3">
{title || t`No items found.`} {title || t`No items found.`}
</Title> </Title>

View File

@@ -0,0 +1,36 @@
import React, { useEffect } from 'react';
import 'styled-components/macro';
import { t } from '@lingui/macro';
import { SearchIcon } from '@patternfly/react-icons';
import ContentEmpty from 'components/ContentEmpty';
export default function EmptyOutput({
hasQueryParams,
isJobRunning,
onUnmount,
}) {
let title;
let message;
let icon;
useEffect(() => onUnmount);
if (hasQueryParams) {
title = t`The search filter did not produce any results…`;
message = t`Please try another search using the filter above`;
icon = SearchIcon;
} else if (isJobRunning) {
title = t`Waiting for job output…`;
} else {
title = t`No output found for this job.`;
}
return (
<ContentEmpty
css="height: 100%"
title={title}
message={message}
icon={icon}
/>
);
}

View File

@@ -17,11 +17,15 @@ function JobEvent({
isCollapsed, isCollapsed,
onToggleCollapsed, onToggleCollapsed,
hasChildren, hasChildren,
jobStatus,
}) { }) {
const numOutputLines = lineTextHtml?.length || 0; const numOutputLines = lineTextHtml?.length || 0;
useEffect(() => { useEffect(() => {
measure(); const timeout = setTimeout(measure, 0);
}, [numOutputLines, isCollapsed, measure]); return () => {
clearTimeout(timeout);
};
}, [numOutputLines, isCollapsed, measure, jobStatus]);
let toggleLineIndex = -1; let toggleLineIndex = -1;
if (hasChildren) { if (hasChildren) {

View File

@@ -30,6 +30,7 @@ import JobEventSkeleton from './JobEventSkeleton';
import PageControls from './PageControls'; import PageControls from './PageControls';
import HostEventModal from './HostEventModal'; import HostEventModal from './HostEventModal';
import JobOutputSearch from './JobOutputSearch'; import JobOutputSearch from './JobOutputSearch';
import EmptyOutput from './EmptyOutput';
import { HostStatusBar, OutputToolbar } from './shared'; import { HostStatusBar, OutputToolbar } from './shared';
import getLineTextHtml from './getLineTextHtml'; import getLineTextHtml from './getLineTextHtml';
import connectJobSocket, { closeWebSocket } from './connectJobSocket'; import connectJobSocket, { closeWebSocket } from './connectJobSocket';
@@ -220,6 +221,7 @@ function JobOutput({ job, eventRelatedSearchableKeys, eventSearchableKeys }) {
...Object.values(siblingRequests.current || {}), ...Object.values(siblingRequests.current || {}),
...Object.values(numEventsRequests.current || {}), ...Object.values(numEventsRequests.current || {}),
]; ];
setHasContentLoading(true); // prevents "no content found" screen from flashing
Promise.all(pendingRequests).then(() => { Promise.all(pendingRequests).then(() => {
setRemoteRowCount(0); setRemoteRowCount(0);
clearLoadedEvents(); clearLoadedEvents();
@@ -509,6 +511,7 @@ function JobOutput({ job, eventRelatedSearchableKeys, eventSearchableKeys }) {
onToggleCollapsed={() => { onToggleCollapsed={() => {
toggleNodeIsCollapsed(event.uuid, !node.isCollapsed); toggleNodeIsCollapsed(event.uuid, !node.isCollapsed);
}} }}
jobStatus={jobStatus}
/> />
) : ( ) : (
<JobEventSkeleton <JobEventSkeleton
@@ -715,36 +718,54 @@ function JobOutput({ job, eventRelatedSearchableKeys, eventSearchableKeys }) {
rowCount={totalNonCollapsedRows + wsEvents.length} rowCount={totalNonCollapsedRows + wsEvents.length}
minimumBatchSize={50} minimumBatchSize={50}
> >
{({ onRowsRendered, registerChild }) => ( {({ onRowsRendered, registerChild }) => {
<AutoSizer nonce={window.NONCE_ID} onResize={handleResize}> if (
{({ width, height }) => ( !hasContentLoading &&
<> remoteRowCount + wsEvents.length === 0
{hasContentLoading ? ( ) {
<div style={{ width }}> return (
<ContentLoading /> <EmptyOutput
</div> hasQueryParams={location.search.length > 1}
) : ( isJobRunning={isJobRunning(jobStatus)}
<List onUnmount={() => {
ref={(ref) => { if (listRef.current?.recomputeRowHeights) {
registerChild(ref); listRef.current.recomputeRowHeights();
listRef.current = ref; }
}} }}
deferredMeasurementCache={cache} />
height={height || 1} );
onRowsRendered={onRowsRendered} }
rowCount={totalNonCollapsedRows + wsEvents.length} return (
rowHeight={cache.rowHeight} <AutoSizer nonce={window.NONCE_ID} onResize={handleResize}>
rowRenderer={rowRenderer} {({ width, height }) => (
scrollToAlignment="start" <>
width={width || 1} {hasContentLoading ? (
overscanRowCount={20} <div style={{ width }}>
onScroll={handleScroll} <ContentLoading />
/> </div>
)} ) : (
</> <List
)} ref={(ref) => {
</AutoSizer> registerChild(ref);
)} listRef.current = ref;
}}
deferredMeasurementCache={cache}
height={height || 1}
onRowsRendered={onRowsRendered}
rowCount={totalNonCollapsedRows + wsEvents.length}
rowHeight={cache.rowHeight}
rowRenderer={rowRenderer}
scrollToAlignment="start"
width={width || 1}
overscanRowCount={20}
onScroll={handleScroll}
/>
)}
</>
)}
</AutoSizer>
);
}}
</InfiniteLoader> </InfiniteLoader>
<OutputFooter /> <OutputFooter />
</OutputWrapper> </OutputWrapper>