Skip to content

Commit

Permalink
feat(web-scraping): support rendering markdown in content trackers UI
Browse files Browse the repository at this point in the history
  • Loading branch information
azasypkin committed Dec 9, 2023
1 parent 3ddca9a commit dd9c07b
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 25 deletions.
Original file line number Diff line number Diff line change
@@ -1,26 +1,60 @@
import { EuiCodeBlock } from '@elastic/eui';
import { EuiCodeBlock, EuiMarkdownFormat } from '@elastic/eui';

import type { WebPageContentRevision } from './web_page_data_revision';
import type { WebPageTrackerHistoryMode } from './web_page_tracker_history';

export interface WebPageContentTrackerRevisionProps {
revision: WebPageContentRevision;
showDiff?: boolean;
mode: WebPageTrackerHistoryMode;
}

export function WebPageContentTrackerRevision({ revision, showDiff }: WebPageContentTrackerRevisionProps) {
function containHTMLTags(data: string) {
try {
const doc = new DOMParser().parseFromString(data, 'text/html');
return Array.from(doc.body.childNodes).some((node) => node.nodeType === Node.ELEMENT_NODE);
} catch {
return false;
}
}

export function WebPageContentTrackerRevision({ revision, mode }: WebPageContentTrackerRevisionProps) {
let dataToRender;
let codeBlockType = null;
try {
dataToRender = JSON.parse(revision.data) as string | object;
if (typeof dataToRender !== 'string') {
if (typeof dataToRender === 'object' && dataToRender) {
dataToRender = JSON.stringify(dataToRender, null, 2);
codeBlockType = 'json';
} else if (typeof dataToRender !== 'string') {
dataToRender = JSON.stringify(dataToRender, null, 2);
} else if (containHTMLTags(dataToRender)) {
codeBlockType = 'html';
}
} catch {
dataToRender = revision.data;
}

return (
<EuiCodeBlock fontSize={'m'} language={showDiff && dataToRender.startsWith('@@') ? 'diff' : 'json'} isCopyable>
{dataToRender}
</EuiCodeBlock>
);
if (mode === 'source' || (mode === 'default' && codeBlockType)) {
return (
<EuiCodeBlock
fontSize="m"
transparentBackground
isCopyable
paddingSize={'none'}
language={codeBlockType ?? undefined}
>
{dataToRender}
</EuiCodeBlock>
);
}

if (mode === 'diff' && dataToRender.startsWith('@@')) {
return (
<EuiCodeBlock fontSize="m" transparentBackground isCopyable paddingSize={'s'} language={'diff'}>
{dataToRender}
</EuiCodeBlock>
);
}

return <EuiMarkdownFormat textSize={'s'}>{dataToRender}</EuiMarkdownFormat>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,8 @@ export default function WebPageContentTrackers() {
} else {
itemIdToExpandedRowMapValues[tracker.name] = (
<WebPageTrackerHistory kind={'content'} tracker={tracker}>
{(revision, showDiff) => (
<WebPageContentTrackerRevision revision={revision as WebPageContentRevision} showDiff={showDiff} />
{(revision, mode) => (
<WebPageContentTrackerRevision revision={revision as WebPageContentRevision} mode={mode} />
)}
</WebPageTrackerHistory>
);
Expand Down
59 changes: 45 additions & 14 deletions src/pages/workspace/utils/web_scraping/web_page_tracker_history.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import type { ReactNode } from 'react';

import {
EuiButton,
EuiButtonGroup,
EuiConfirmModal,
EuiEmptyPrompt,
EuiFlexGroup,
EuiFlexItem,
EuiIcon,
EuiPanel,
EuiSelect,
EuiSwitch,
} from '@elastic/eui';
import { css } from '@emotion/react';
import axios from 'axios';
Expand All @@ -25,20 +25,35 @@ import { useWorkspaceContext } from '../../hooks';
export interface WebPageTrackerHistoryProps {
tracker: WebPageTracker;
kind: 'content' | 'resources';
children: (revision: WebPageDataRevision, showDiff: boolean) => ReactNode;
children: (revision: WebPageDataRevision, mode: WebPageTrackerHistoryMode) => ReactNode;
}

export type WebPageTrackerHistoryMode = 'default' | 'diff' | 'source';

export function WebPageTrackerHistory({ kind, tracker, children }: WebPageTrackerHistoryProps) {
const { uiState, addToast } = useWorkspaceContext();

const [showDiff, setShowDiff] = useState<boolean>(true);
const [revisions, setRevisions] = useState<AsyncData<WebPageContentRevision[], WebPageContentRevision[] | null>>({
status: 'pending',
state: null,
});
const [revisionIndex, setRevisionIndex] = useState<number | null>(null);

const modes = [
{ id: 'default' as const, label: 'Default', isDisabled: revisions.status !== 'succeeded' },
{ id: 'diff' as const, label: 'Diff', isDisabled: revisionIndex === 0 || revisions.status !== 'succeeded' },
...(kind === 'content'
? [{ id: 'source' as const, label: 'Source', isDisabled: revisions.status !== 'succeeded' }]
: []),
];
const [mode, setMode] = useState<WebPageTrackerHistoryMode>('default');

const fetchHistory = useCallback(
({ refresh }: { refresh: boolean } = { refresh: false }) => {
(
{ refresh, forceMode }: { refresh: boolean; forceMode?: WebPageTrackerHistoryMode } = {
refresh: false,
},
) => {
setRevisions((currentRevisions) =>
currentRevisions.status === 'succeeded'
? { status: 'pending', state: currentRevisions.data }
Expand All @@ -47,7 +62,7 @@ export function WebPageTrackerHistory({ kind, tracker, children }: WebPageTracke
axios
.post<WebPageContentRevision[]>(
getApiUrl(`/api/utils/web_scraping/${kind}/${encodeURIComponent(tracker.id)}/history`),
{ refresh, calculateDiff: showDiff },
{ refresh, calculateDiff: (forceMode ?? mode) === 'diff' },
getApiRequestConfig(),
)
.then(
Expand All @@ -58,6 +73,12 @@ export function WebPageTrackerHistory({ kind, tracker, children }: WebPageTracke
if (refresh || revisionIndex === null || revisionIndex >= response.data.length) {
setRevisionIndex(response.data.length > 0 ? response.data.length - 1 : null);
}

if (response.data.length < 2) {
setMode('default');
} else if (forceMode && forceMode !== mode) {
setMode(forceMode);
}
},
(err: Error) => {
setRevisions((currentRevisions) => ({
Expand All @@ -69,7 +90,7 @@ export function WebPageTrackerHistory({ kind, tracker, children }: WebPageTracke
},
);
},
[getApiUrl, revisionIndex, showDiff],
[getApiUrl, revisionIndex, mode],
);

useEffect(() => {
Expand All @@ -78,7 +99,7 @@ export function WebPageTrackerHistory({ kind, tracker, children }: WebPageTracke
}

fetchHistory();
}, [uiState, tracker, showDiff]);
}, [uiState, tracker]);

const onRevisionChange = useCallback(
(revisionId: string) => {
Expand Down Expand Up @@ -163,7 +184,7 @@ export function WebPageTrackerHistory({ kind, tracker, children }: WebPageTracke
/>
);
} else if (revisionIndex !== null) {
history = children(revisions.data[revisionIndex], showDiff);
history = children(revisions.data[revisionIndex], mode);
} else {
const updateButton = (
<EuiButton
Expand Down Expand Up @@ -227,13 +248,23 @@ export function WebPageTrackerHistory({ kind, tracker, children }: WebPageTracke
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiSwitch
label="Show diff"
disabled={
revisions.status !== 'succeeded' || (revisions.status === 'succeeded' && revisions.data.length < 2)
<EuiButtonGroup
legend="View mode"
options={modes}
idSelected={mode}
isDisabled={
revisions.status !== 'succeeded' ||
(kind === 'resources' && revisions.status === 'succeeded' && revisions.data.length < 2)
}
checked={showDiff}
onChange={(e) => setShowDiff(e.target.checked)}
onChange={(id) => {
const newMode = id as WebPageTrackerHistoryMode;
setMode(newMode);

const shouldFetch = (mode === 'diff' && id !== 'diff') || (mode !== 'diff' && id === 'diff');
if (shouldFetch) {
fetchHistory({ forceMode: newMode, refresh: false });
}
}}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
Expand Down

0 comments on commit dd9c07b

Please sign in to comment.