Skip to content

Commit fcf335a

Browse files
rustam-egreg-in-a-boxtjuanitas
authored
feat(docgen-sidebar): add polling to tags loading in dogen sidebar (#3858)
* fix(content-explorer): Migrated Date (#3800) * fix(docgen): add auto refetching of tags if they're being processed * fix(docgen): address code review comments * fix(docgen): address code review comments * fix(docgen): update tests * fix(docgen): add extra test case * Update src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx Co-authored-by: Trevor <7311041+tjuanitas@users.noreply.github.com> * Update src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx Co-authored-by: Trevor <7311041+tjuanitas@users.noreply.github.com> * chore(docgen): address code review comments * fix(docgen): bad merge * fix(docgen): address code review comments * fix(docgen): remove Promise.all * fix(docgen): remove timeout --------- Co-authored-by: greg-in-a-box <103291617+greg-in-a-box@users.noreply.github.com> Co-authored-by: Trevor <7311041+tjuanitas@users.noreply.github.com>
1 parent 50d6c3f commit fcf335a

File tree

3 files changed

+101
-40
lines changed

3 files changed

+101
-40
lines changed

src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx

Lines changed: 50 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import * as React from 'react';
1+
import React, { useState, useCallback, useEffect } from 'react';
22
import classNames from 'classnames';
33
import flow from 'lodash/flow';
44
import { useIntl } from 'react-intl';
@@ -33,6 +33,8 @@ import commonMessages from '../../common/messages';
3333
import './DocGenSidebar.scss';
3434
import { DocGenTag, DocGenTemplateTagsResponse, JsonPathsMap } from './types';
3535

36+
const DEFAULT_RETRIES = 10;
37+
3638
type ExternalProps = {
3739
enabled: boolean;
3840
getDocGenTags: () => Promise<DocGenTemplateTagsResponse>;
@@ -55,13 +57,13 @@ type JsonPathsState = {
5557
const DocGenSidebar = ({ getDocGenTags }: Props) => {
5658
const { formatMessage } = useIntl();
5759

58-
const [hasError, setHasError] = React.useState<boolean>(false);
59-
const [isLoading, setIsLoading] = React.useState<boolean>(false);
60-
const [tags, setTags] = React.useState<TagState>({
60+
const [hasError, setHasError] = useState<boolean>(false);
61+
const [isLoading, setIsLoading] = useState<boolean>(false);
62+
const [tags, setTags] = useState<TagState>({
6163
text: [],
6264
image: [],
6365
});
64-
const [jsonPaths, setJsonPaths] = React.useState<JsonPathsState>({
66+
const [jsonPaths, setJsonPaths] = useState<JsonPathsState>({
6567
textTree: {},
6668
imageTree: {},
6769
});
@@ -73,7 +75,7 @@ const DocGenSidebar = ({ getDocGenTags }: Props) => {
7375
}, base);
7476
};
7577

76-
const tagsToJsonPaths = (docGenTags: DocGenTag[]): JsonPathsMap => {
78+
const tagsToJsonPaths = useCallback((docGenTags: DocGenTag[]): JsonPathsMap => {
7779
const jsonPathsMap: JsonPathsMap = {};
7880

7981
docGenTags.forEach(tag => {
@@ -84,49 +86,58 @@ const DocGenSidebar = ({ getDocGenTags }: Props) => {
8486
});
8587

8688
return jsonPathsMap;
87-
};
89+
}, []);
8890

89-
const loadTags = async () => {
90-
setIsLoading(true);
91-
try {
92-
const response: DocGenTemplateTagsResponse = await getDocGenTags();
93-
if (response && !!response.data) {
94-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
95-
// @ts-ignore
96-
const { data } = response || [];
97-
98-
// anything that is not an image tag for this view is treated as a text tag
99-
const textTags = data?.filter(tag => tag.tag_type !== 'image') || [];
100-
const imageTags = data?.filter(tag => tag.tag_type === 'image') || [];
101-
setTags({
102-
text: textTags,
103-
image: imageTags,
104-
});
105-
setJsonPaths({
106-
textTree: tagsToJsonPaths(textTags),
107-
imageTree: tagsToJsonPaths(imageTags),
108-
});
109-
setHasError(false);
110-
} else {
91+
const loadTags = useCallback(
92+
async (attempts = DEFAULT_RETRIES) => {
93+
if (attempts <= 0) {
94+
setIsLoading(false);
95+
return;
96+
}
97+
setIsLoading(true);
98+
try {
99+
const response: DocGenTemplateTagsResponse = await getDocGenTags();
100+
if (response?.message) {
101+
loadTags.call(this, attempts - 1);
102+
} else if (response?.data) {
103+
const { data } = response;
104+
// anything that is not an image tag for this view is treated as a text tag
105+
const textTags = data?.filter(tag => tag.tag_type !== 'image') || [];
106+
const imageTags = data?.filter(tag => tag.tag_type === 'image') || [];
107+
setTags({
108+
text: textTags,
109+
image: imageTags,
110+
});
111+
setJsonPaths({
112+
textTree: tagsToJsonPaths(textTags),
113+
imageTree: tagsToJsonPaths(imageTags),
114+
});
115+
setHasError(false);
116+
setIsLoading(false);
117+
} else {
118+
setHasError(true);
119+
setIsLoading(false);
120+
}
121+
} catch (error) {
111122
setHasError(true);
123+
setIsLoading(false);
112124
}
113-
} catch (error) {
114-
setHasError(true);
115-
}
116-
setIsLoading(false);
117-
};
118-
119-
React.useEffect(() => {
120-
loadTags();
125+
},
126+
// disabling eslint because the getDocGenTags prop is changing very frequently
121127
// eslint-disable-next-line react-hooks/exhaustive-deps
122-
}, []);
128+
[tagsToJsonPaths],
129+
);
130+
131+
useEffect(() => {
132+
loadTags(DEFAULT_RETRIES);
133+
}, [loadTags]);
123134

124135
const isEmpty = tags.image.length + tags.text.length === 0;
125136

126137
return (
127138
<SidebarContent sidebarView={SIDEBAR_VIEW_DOCGEN} title={formatMessage(messages.docGenTags)}>
128139
<div className={classNames('bcs-DocGenSidebar', { center: isEmpty || hasError || isLoading })}>
129-
{hasError && <Error onClick={loadTags} />}
140+
{hasError && <Error onClick={() => loadTags(DEFAULT_RETRIES)} />}
130141
{isLoading && (
131142
<LoadingIndicator
132143
aria-label={formatMessage(commonMessages.loading)}

src/elements/content-sidebar/DocGenSidebar/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export type DocGenTag = {
1010

1111
export type DocGenTemplateTagsResponse = {
1212
data?: DocGenTag[];
13+
message?: string;
1314
pagination?: {
1415
previousMarker?: string;
1516
nextMarker?: string;

src/elements/content-sidebar/__tests__/DocGenSidebar.test.tsx

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,39 @@ const docGenSidebarProps = {
1313
),
1414
};
1515

16+
const processAndResolveMock = jest
17+
.fn()
18+
.mockImplementationOnce(() =>
19+
Promise.resolve({
20+
message: 'Processing tags for this file.',
21+
}),
22+
)
23+
.mockImplementationOnce(() =>
24+
Promise.resolve({
25+
pagination: {},
26+
data: mockData,
27+
}),
28+
);
29+
1630
const noTagsMock = jest.fn().mockReturnValue(Promise.resolve({ data: [] }));
31+
const processingTagsMock = jest.fn().mockReturnValue(
32+
Promise.resolve({
33+
message: 'Processing tags for this file.',
34+
}),
35+
);
1736
const errorTagsMock = jest.fn().mockRejectedValue([]);
1837
const noDataMock = jest.fn().mockReturnValue(Promise.resolve({}));
1938

2039
describe('elements/content-sidebar/DocGenSidebar', () => {
40+
beforeEach(() => {
41+
jest.useFakeTimers();
42+
});
43+
44+
afterEach(() => {
45+
jest.useRealTimers();
46+
jest.clearAllMocks();
47+
});
48+
2149
const renderComponent = (props = {}) =>
2250
render(<DocGenSidebar logger={{ onReadyMetric: jest.fn() }} {...docGenSidebarProps} {...props} />);
2351

@@ -72,11 +100,32 @@ describe('elements/content-sidebar/DocGenSidebar', () => {
72100

73101
expect(await screen.findByRole('status', { name: 'Loading' })).toBeInTheDocument();
74102

103+
jest.advanceTimersByTime(1000);
104+
75105
await waitFor(() => {
76106
expect(screen.queryByRole('status', { name: 'Loading' })).not.toBeInTheDocument();
77107
});
78108
});
79109

110+
test('should re-trigger loadTags if the template is still processing', async () => {
111+
renderComponent({
112+
getDocGenTags: processingTagsMock,
113+
});
114+
115+
await waitFor(() => expect(processingTagsMock).toHaveBeenCalledTimes(10));
116+
});
117+
118+
test('should re-trigger loadTags retrigger and successfully display the tags', async () => {
119+
renderComponent({
120+
getDocGenTags: processAndResolveMock,
121+
});
122+
123+
await waitFor(() => expect(processAndResolveMock).toHaveBeenCalledTimes(2));
124+
const parentTag = await screen.findByText('about');
125+
126+
expect(parentTag).toBeVisible();
127+
});
128+
80129
test('should re-trigger getDocGenTags on click on refresh button', async () => {
81130
renderComponent({
82131
getDocGenTags: errorTagsMock,
@@ -88,7 +137,7 @@ describe('elements/content-sidebar/DocGenSidebar', () => {
88137
const refreshButton = screen.getByRole('button', { name: 'Process document' });
89138
fireEvent.click(refreshButton);
90139

91-
await waitFor(() => expect(errorTagsMock).toBeCalledTimes(2));
140+
await waitFor(() => expect(errorTagsMock).toHaveBeenCalledTimes(2));
92141
});
93142

94143
test('should handle undefined data', async () => {

0 commit comments

Comments
 (0)