From e147979f1670cf951b936c1878d95eac1a62fe61 Mon Sep 17 00:00:00 2001 From: greg-in-a-box <103291617+greg-in-a-box@users.noreply.github.com> Date: Thu, 19 Dec 2024 16:40:43 -0500 Subject: [PATCH 01/13] fix(content-explorer): Migrated Date (#3800) --- i18n/en-US.properties | 24 +++++++++++ src/elements/content-explorer/Date.js.flow | 41 +++++++++++++++++++ .../content-explorer/{Date.js => Date.tsx} | 11 +++-- .../content-explorer/__tests__/Date.test.tsx | 37 +++++++++++++++++ 4 files changed, 107 insertions(+), 6 deletions(-) create mode 100644 src/elements/content-explorer/Date.js.flow rename src/elements/content-explorer/{Date.js => Date.tsx} (88%) create mode 100644 src/elements/content-explorer/__tests__/Date.test.tsx diff --git a/i18n/en-US.properties b/i18n/en-US.properties index d69a621051..8e9c7cd984 100644 --- a/i18n/en-US.properties +++ b/i18n/en-US.properties @@ -946,6 +946,12 @@ boxui.collaboratorAvatars.viewAdditionalPeopleText = View additional people boxui.collapsiblesidebar.collapseBtnLabel = Collapse # Aria label for toggle button that expands/collapses sidebar (collapsed state) boxui.collapsiblesidebar.expandBtnLabel = Expand +# Content Answers submit input button text +boxui.contentAnswers.ask = Ask +# Content Answers submit input button disabled tooltip text when answer is generating +boxui.contentAnswers.askDisabledTooltip = You can submit another question once Box AI has finished responding +# Content Answers modal input placeholder +boxui.contentAnswers.askQuestionPlaceholder = Ask anything about this document # Content Answers feature name shown on menu item and modal title boxui.contentAnswers.contentAnswersTitle = Box AI # Default tooltip message for Content Answers entry point button @@ -970,12 +976,30 @@ boxui.contentAnswers.documentSuggestedQuestionPrompt3 = How can this document be boxui.contentAnswers.documentSuggestedQuestionPrompt4 = Are there any next steps defined? # Existing questions tooltip message for Content Answers entry point button boxui.contentAnswers.hasQuestionsTooltip = Return to Box AI +# Content Answers error message when the service fails +boxui.contentAnswers.inlineErrorText = The Box AI service was unavailable. +# Box AI Q&A service unavailable error description +boxui.contentAnswers.intelligenceUnavailableDescription = The Box AI service is not responding. +# Box AI Q&A service unavailable error title +boxui.contentAnswers.intelligenceUnavailableHeading = Box AI is unavailable +# Box AI Q&A service unavailable error try again later description +boxui.contentAnswers.intelligenceUnavailableTryAgain = Please try again later. +# Error tooltip to show inside text area if the user reached the character limit +boxui.contentAnswers.maxCharactersReachedError = Maximum of {characterLimit} characters reached +# Retry button label to send again the question to the service +boxui.contentAnswers.retryResponse = Retry +# Content Answers welcome message for asking questions +boxui.contentAnswers.welcomeAskQuestionText = Ask questions about {name} +# Content Answers welcome message for clearing the chat +boxui.contentAnswers.welcomeClearChatText = This chat will be cleared when you close this document # Content Answers welcome message spreadsheet supported by Intelligent Query notification boxui.contentAnswers.welcomeMessageIntelligentQueryNotice = You can ask Box AI both simple and complex questions in your spreadsheet: total counts, averages, advanced comparisons, trend analyses and so on. Try it out today! # Content Answers welcome message spreadsheet notification boxui.contentAnswers.welcomeMessageSpreadsheetNotice = Spreadsheet support works best for text dense files # Aria label for the icon inside spreadsheet notification boxui.contentAnswers.welcomeMessageSpreadsheetNoticeAriaLabel = spreadsheet support notification banner +# Content Answers welcome message title +boxui.contentAnswers.welcomeMessageTitle = Welcome to Box AI # Aria label for the folder breadcrumb boxui.contentExplorer.breadcrumb = Breadcrumb # Text shown on button used to close the content explorer diff --git a/src/elements/content-explorer/Date.js.flow b/src/elements/content-explorer/Date.js.flow new file mode 100644 index 0000000000..6c2d7f0c8c --- /dev/null +++ b/src/elements/content-explorer/Date.js.flow @@ -0,0 +1,41 @@ +import * as React from 'react'; +import { FormattedMessage } from 'react-intl'; +import Datefield from '../common/date'; +import messages from '../common/messages'; +import { FIELD_INTERACTED_AT } from '../../constants'; +import type { BoxItem } from '../../common/types/core'; + +type Props = { + dataKey: string, + item: BoxItem +}; + +const Date = ({ + dataKey, + item, +}: Props) => { + const { + modified_at = '', + interacted_at = '', + modified_by, + }: BoxItem = item; + const modifiedBy: string = modified_by ? modified_by.name || '' : ''; + const isRecents: boolean = dataKey === FIELD_INTERACTED_AT; + const date: string = isRecents ? interacted_at || modified_at : modified_at; + const DateValue = ; + + if (isRecents || !modifiedBy) { + return DateValue; + } + + return ( + + ); +}; +export default Date; diff --git a/src/elements/content-explorer/Date.js b/src/elements/content-explorer/Date.tsx similarity index 88% rename from src/elements/content-explorer/Date.js rename to src/elements/content-explorer/Date.tsx index 6d9033fa3e..f7a1f08010 100644 --- a/src/elements/content-explorer/Date.js +++ b/src/elements/content-explorer/Date.tsx @@ -1,4 +1,3 @@ -// @flow import * as React from 'react'; import { FormattedMessage } from 'react-intl'; import Datefield from '../common/date'; @@ -6,12 +5,12 @@ import messages from '../common/messages'; import { FIELD_INTERACTED_AT } from '../../constants'; import type { BoxItem } from '../../common/types/core'; -type Props = { - dataKey: string, - item: BoxItem, -}; +export interface DateProps { + dataKey: string; + item: BoxItem; +} -const Date = ({ dataKey, item }: Props) => { +const Date = ({ dataKey, item }: DateProps) => { const { modified_at = '', interacted_at = '', modified_by }: BoxItem = item; const modifiedBy: string = modified_by ? modified_by.name || '' : ''; const isRecents: boolean = dataKey === FIELD_INTERACTED_AT; diff --git a/src/elements/content-explorer/__tests__/Date.test.tsx b/src/elements/content-explorer/__tests__/Date.test.tsx new file mode 100644 index 0000000000..23f93f6cc1 --- /dev/null +++ b/src/elements/content-explorer/__tests__/Date.test.tsx @@ -0,0 +1,37 @@ +import * as React from 'react'; +import { render, screen } from '../../../test-utils/testing-library'; + +import Date, { DateProps } from '../Date'; +import { FIELD_INTERACTED_AT } from '../../../constants'; + +const itemWithModifiedBy = { + modified_at: '2023-10-01T12:00:00Z', + interacted_at: '2023-10-02T12:00:00Z', + modified_by: { name: 'John Doe' }, +}; + +const itemWithoutModifiedBy = { + modified_at: '2023-10-01T12:00:00Z', + interacted_at: '2023-10-02T12:00:00Z', +}; + +describe('elements/content-explorer/Date', () => { + const renderComponent = (props: DateProps) => { + return render(); + }; + + test('renders date with modified_by name when dataKey is not FIELD_INTERACTED_AT', () => { + renderComponent({ dataKey: 'someKey', item: itemWithModifiedBy }); + expect(screen.getByText(/Sun Oct 1 2023\s+by John Doe/)).toBeInTheDocument(); + }); + + test('renders date without modified_by name when dataKey is FIELD_INTERACTED_AT', () => { + renderComponent({ dataKey: FIELD_INTERACTED_AT, item: itemWithoutModifiedBy }); + expect(screen.getByText('Mon Oct 2 2023')).toBeInTheDocument(); + }); + + test('renders date without modified_by name when modified_by is not provided', () => { + renderComponent({ dataKey: 'someKey', item: itemWithoutModifiedBy }); + expect(screen.getByText('Sun Oct 1 2023')).toBeInTheDocument(); + }); +}); From 6256bd25a531847899e4f2535c506479c5571490 Mon Sep 17 00:00:00 2001 From: rustam-e Date: Tue, 21 Jan 2025 15:35:07 +0100 Subject: [PATCH 02/13] fix(docgen): add auto refetching of tags if they're being processed --- i18n/en-US.properties | 24 ------------------- .../DocGenSidebar/DocGenSidebar.tsx | 22 ++++++++++++----- .../content-sidebar/DocGenSidebar/types.ts | 1 + .../__tests__/DocGenSidebar.test.tsx | 14 ++++++++++- 4 files changed, 30 insertions(+), 31 deletions(-) diff --git a/i18n/en-US.properties b/i18n/en-US.properties index 8e9c7cd984..d69a621051 100644 --- a/i18n/en-US.properties +++ b/i18n/en-US.properties @@ -946,12 +946,6 @@ boxui.collaboratorAvatars.viewAdditionalPeopleText = View additional people boxui.collapsiblesidebar.collapseBtnLabel = Collapse # Aria label for toggle button that expands/collapses sidebar (collapsed state) boxui.collapsiblesidebar.expandBtnLabel = Expand -# Content Answers submit input button text -boxui.contentAnswers.ask = Ask -# Content Answers submit input button disabled tooltip text when answer is generating -boxui.contentAnswers.askDisabledTooltip = You can submit another question once Box AI has finished responding -# Content Answers modal input placeholder -boxui.contentAnswers.askQuestionPlaceholder = Ask anything about this document # Content Answers feature name shown on menu item and modal title boxui.contentAnswers.contentAnswersTitle = Box AI # Default tooltip message for Content Answers entry point button @@ -976,30 +970,12 @@ boxui.contentAnswers.documentSuggestedQuestionPrompt3 = How can this document be boxui.contentAnswers.documentSuggestedQuestionPrompt4 = Are there any next steps defined? # Existing questions tooltip message for Content Answers entry point button boxui.contentAnswers.hasQuestionsTooltip = Return to Box AI -# Content Answers error message when the service fails -boxui.contentAnswers.inlineErrorText = The Box AI service was unavailable. -# Box AI Q&A service unavailable error description -boxui.contentAnswers.intelligenceUnavailableDescription = The Box AI service is not responding. -# Box AI Q&A service unavailable error title -boxui.contentAnswers.intelligenceUnavailableHeading = Box AI is unavailable -# Box AI Q&A service unavailable error try again later description -boxui.contentAnswers.intelligenceUnavailableTryAgain = Please try again later. -# Error tooltip to show inside text area if the user reached the character limit -boxui.contentAnswers.maxCharactersReachedError = Maximum of {characterLimit} characters reached -# Retry button label to send again the question to the service -boxui.contentAnswers.retryResponse = Retry -# Content Answers welcome message for asking questions -boxui.contentAnswers.welcomeAskQuestionText = Ask questions about {name} -# Content Answers welcome message for clearing the chat -boxui.contentAnswers.welcomeClearChatText = This chat will be cleared when you close this document # Content Answers welcome message spreadsheet supported by Intelligent Query notification boxui.contentAnswers.welcomeMessageIntelligentQueryNotice = You can ask Box AI both simple and complex questions in your spreadsheet: total counts, averages, advanced comparisons, trend analyses and so on. Try it out today! # Content Answers welcome message spreadsheet notification boxui.contentAnswers.welcomeMessageSpreadsheetNotice = Spreadsheet support works best for text dense files # Aria label for the icon inside spreadsheet notification boxui.contentAnswers.welcomeMessageSpreadsheetNoticeAriaLabel = spreadsheet support notification banner -# Content Answers welcome message title -boxui.contentAnswers.welcomeMessageTitle = Welcome to Box AI # Aria label for the folder breadcrumb boxui.contentExplorer.breadcrumb = Breadcrumb # Text shown on button used to close the content explorer diff --git a/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx b/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx index 3a76b9c7a0..1128a3a842 100644 --- a/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx +++ b/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx @@ -86,13 +86,20 @@ const DocGenSidebar = ({ getDocGenTags }: Props) => { return jsonPathsMap; }; - const loadTags = async () => { + + const loadTags = async (attempts = 10) => { + if(attempts <= 0){ + return + } setIsLoading(true); try { const response: DocGenTemplateTagsResponse = await getDocGenTags(); - if (response && !!response.data) { + if(response.message){ + loadTags(attempts - 1); + } else if (response && !!response.data) { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore + const { data } = response || []; // anything that is not an image tag for this view is treated as a text tag @@ -107,20 +114,23 @@ const DocGenSidebar = ({ getDocGenTags }: Props) => { imageTree: tagsToJsonPaths(imageTags), }); setHasError(false); + setIsLoading(false); } else { setHasError(true); + setIsLoading(false); } } catch (error) { setHasError(true); + setIsLoading(false); } - setIsLoading(false); - }; - + } + React.useEffect(() => { - loadTags(); + loadTags(10); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + const isEmpty = tags.image.length + tags.text.length === 0; return ( diff --git a/src/elements/content-sidebar/DocGenSidebar/types.ts b/src/elements/content-sidebar/DocGenSidebar/types.ts index 6107d39153..2730d827e0 100644 --- a/src/elements/content-sidebar/DocGenSidebar/types.ts +++ b/src/elements/content-sidebar/DocGenSidebar/types.ts @@ -10,6 +10,7 @@ export type DocGenTag = { export type DocGenTemplateTagsResponse = { data?: DocGenTag[]; + message?: string; pagination?: { previousMarker?: string; nextMarker?: string; diff --git a/src/elements/content-sidebar/__tests__/DocGenSidebar.test.tsx b/src/elements/content-sidebar/__tests__/DocGenSidebar.test.tsx index 1387e54e6f..c1fbcf0691 100644 --- a/src/elements/content-sidebar/__tests__/DocGenSidebar.test.tsx +++ b/src/elements/content-sidebar/__tests__/DocGenSidebar.test.tsx @@ -14,6 +14,9 @@ const docGenSidebarProps = { }; const noTagsMock = jest.fn().mockReturnValue(Promise.resolve({ data: [] })); +const processingTagsMock = jest.fn().mockReturnValue(Promise.resolve({ + "message": "Processing tags for this file." +})); const errorTagsMock = jest.fn().mockRejectedValue([]); const noDataMock = jest.fn().mockReturnValue(Promise.resolve({})); @@ -77,6 +80,15 @@ describe('elements/content-sidebar/DocGenSidebar', () => { }); }); + + test('should re-trigger loadTags if the tempalte is still processing', async () => { + renderComponent({ + getDocGenTags: processingTagsMock, + }); + + await waitFor(() => expect(processingTagsMock).toHaveBeenCalledTimes(10)); + }); + test('should re-trigger getDocGenTags on click on refresh button', async () => { renderComponent({ getDocGenTags: errorTagsMock, @@ -88,7 +100,7 @@ describe('elements/content-sidebar/DocGenSidebar', () => { const refreshButton = screen.getByRole('button', { name: 'Refresh' }); fireEvent.click(refreshButton); - await waitFor(() => expect(errorTagsMock).toBeCalledTimes(2)); + await waitFor(() => expect(errorTagsMock).toHaveBeenCalledTimes(2)); }); test('should handle undefined data', async () => { From 279c0cce61226267f65377e1cf5fef5371f1b0d8 Mon Sep 17 00:00:00 2001 From: rustam-e Date: Fri, 24 Jan 2025 14:21:45 +0100 Subject: [PATCH 03/13] fix(docgen): address code review comments --- .../DocGenSidebar/DocGenSidebar.tsx | 34 +++++++++++-------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx b/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx index 1128a3a842..6190d68811 100644 --- a/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx +++ b/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx @@ -1,4 +1,4 @@ -import * as React from 'react'; +import React, { useState, useCallback, useEffect } from 'react'; import classNames from 'classnames'; import flow from 'lodash/flow'; import { useIntl } from 'react-intl'; @@ -55,13 +55,13 @@ type JsonPathsState = { const DocGenSidebar = ({ getDocGenTags }: Props) => { const { formatMessage } = useIntl(); - const [hasError, setHasError] = React.useState(false); - const [isLoading, setIsLoading] = React.useState(false); - const [tags, setTags] = React.useState({ + const [hasError, setHasError] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const [tags, setTags] = useState({ text: [], image: [], }); - const [jsonPaths, setJsonPaths] = React.useState({ + const [jsonPaths, setJsonPaths] = useState({ textTree: {}, imageTree: {}, }); @@ -73,7 +73,7 @@ const DocGenSidebar = ({ getDocGenTags }: Props) => { }, base); }; - const tagsToJsonPaths = (docGenTags: DocGenTag[]): JsonPathsMap => { + const tagsToJsonPaths = useCallback((docGenTags: DocGenTag[]): JsonPathsMap => { const jsonPathsMap: JsonPathsMap = {}; docGenTags.forEach(tag => { @@ -84,10 +84,11 @@ const DocGenSidebar = ({ getDocGenTags }: Props) => { }); return jsonPathsMap; - }; + }, []); - const loadTags = async (attempts = 10) => { + const loadTags = useCallback(async (attempts = 10) => { + console.log({attempts}) if(attempts <= 0){ return } @@ -95,7 +96,9 @@ const DocGenSidebar = ({ getDocGenTags }: Props) => { try { const response: DocGenTemplateTagsResponse = await getDocGenTags(); if(response.message){ - loadTags(attempts - 1); + setTimeout(() => { + loadTags.call(this, attempts - 1); + }, 1000); } else if (response && !!response.data) { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore @@ -123,12 +126,13 @@ const DocGenSidebar = ({ getDocGenTags }: Props) => { setHasError(true); setIsLoading(false); } - } - - React.useEffect(() => { - loadTags(10); + // disabling eslint because the getDocGenTags prop is changing very frequently // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, [tagsToJsonPaths]); + + useEffect(() => { + loadTags(10); + }, [loadTags]); const isEmpty = tags.image.length + tags.text.length === 0; @@ -136,7 +140,7 @@ const DocGenSidebar = ({ getDocGenTags }: Props) => { return (
- {hasError && } + {hasError && loadTags(10)} />} {isLoading && ( Date: Fri, 24 Jan 2025 14:29:17 +0100 Subject: [PATCH 04/13] fix(docgen): address code review comments --- src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx b/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx index 6190d68811..5a014d3dfa 100644 --- a/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx +++ b/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx @@ -88,8 +88,8 @@ const DocGenSidebar = ({ getDocGenTags }: Props) => { const loadTags = useCallback(async (attempts = 10) => { - console.log({attempts}) if(attempts <= 0){ + setIsLoading(false); return } setIsLoading(true); @@ -124,7 +124,6 @@ const DocGenSidebar = ({ getDocGenTags }: Props) => { } } catch (error) { setHasError(true); - setIsLoading(false); } // disabling eslint because the getDocGenTags prop is changing very frequently // eslint-disable-next-line react-hooks/exhaustive-deps From 4ffbd60260ca5910bd701456b3a8b23a1a7be88a Mon Sep 17 00:00:00 2001 From: rustam-e Date: Fri, 24 Jan 2025 15:06:14 +0100 Subject: [PATCH 05/13] fix(docgen): update tests --- .../DocGenSidebar/DocGenSidebar.tsx | 2 -- .../__tests__/DocGenSidebar.test.tsx | 26 +++++++++++++++++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx b/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx index 5a014d3dfa..bea3813399 100644 --- a/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx +++ b/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx @@ -86,7 +86,6 @@ const DocGenSidebar = ({ getDocGenTags }: Props) => { return jsonPathsMap; }, []); - const loadTags = useCallback(async (attempts = 10) => { if(attempts <= 0){ setIsLoading(false); @@ -133,7 +132,6 @@ const DocGenSidebar = ({ getDocGenTags }: Props) => { loadTags(10); }, [loadTags]); - const isEmpty = tags.image.length + tags.text.length === 0; return ( diff --git a/src/elements/content-sidebar/__tests__/DocGenSidebar.test.tsx b/src/elements/content-sidebar/__tests__/DocGenSidebar.test.tsx index 122545a61a..53550e8213 100644 --- a/src/elements/content-sidebar/__tests__/DocGenSidebar.test.tsx +++ b/src/elements/content-sidebar/__tests__/DocGenSidebar.test.tsx @@ -21,6 +21,15 @@ const errorTagsMock = jest.fn().mockRejectedValue([]); const noDataMock = jest.fn().mockReturnValue(Promise.resolve({})); describe('elements/content-sidebar/DocGenSidebar', () => { + beforeEach(() => { + jest.useFakeTimers(); + }); + + afterEach(() => { + jest.useRealTimers(); + jest.clearAllMocks(); + }); + const renderComponent = (props = {}) => render(); @@ -75,20 +84,33 @@ describe('elements/content-sidebar/DocGenSidebar', () => { expect(await screen.findByRole('status', { name: 'Loading' })).toBeInTheDocument(); + jest.advanceTimersByTime(1000); + await waitFor(() => { expect(screen.queryByRole('status', { name: 'Loading' })).not.toBeInTheDocument(); }); }); - - test('should re-trigger loadTags if the tempalte is still processing', async () => { + test('should re-trigger loadTags if the template is still processing', async () => { renderComponent({ getDocGenTags: processingTagsMock, }); + await jest.advanceTimersByTime(1000); + await jest.advanceTimersByTime(1000); + await jest.advanceTimersByTime(1000); + await jest.advanceTimersByTime(1000); + await jest.advanceTimersByTime(1000); + await jest.advanceTimersByTime(1000); + await jest.advanceTimersByTime(1000); + await jest.advanceTimersByTime(1000); + await jest.advanceTimersByTime(1000); + await jest.advanceTimersByTime(1000); + await waitFor(() => expect(processingTagsMock).toHaveBeenCalledTimes(10)); }); + test('should re-trigger getDocGenTags on click on refresh button', async () => { renderComponent({ getDocGenTags: errorTagsMock, From 621c2314f8e73ef39e87afb65cee42d4427b7d1a Mon Sep 17 00:00:00 2001 From: rustam-e Date: Fri, 24 Jan 2025 15:16:02 +0100 Subject: [PATCH 06/13] fix(docgen): add extra test case --- .../DocGenSidebar/DocGenSidebar.tsx | 1 - .../__tests__/DocGenSidebar.test.tsx | 23 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx b/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx index bea3813399..ac18482d30 100644 --- a/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx +++ b/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx @@ -103,7 +103,6 @@ const DocGenSidebar = ({ getDocGenTags }: Props) => { // @ts-ignore const { data } = response || []; - // anything that is not an image tag for this view is treated as a text tag const textTags = data?.filter(tag => tag.tag_type !== 'image') || []; const imageTags = data?.filter(tag => tag.tag_type === 'image') || []; diff --git a/src/elements/content-sidebar/__tests__/DocGenSidebar.test.tsx b/src/elements/content-sidebar/__tests__/DocGenSidebar.test.tsx index 53550e8213..1b072ffe41 100644 --- a/src/elements/content-sidebar/__tests__/DocGenSidebar.test.tsx +++ b/src/elements/content-sidebar/__tests__/DocGenSidebar.test.tsx @@ -13,6 +13,15 @@ const docGenSidebarProps = { ), }; +const processAndResolveMock = jest.fn() +.mockImplementationOnce(() => Promise.resolve({ + message: "Processing tags for this file." +})) +.mockImplementationOnce(() => Promise.resolve({ + pagination: {}, + data: mockData, +})); + const noTagsMock = jest.fn().mockReturnValue(Promise.resolve({ data: [] })); const processingTagsMock = jest.fn().mockReturnValue(Promise.resolve({ "message": "Processing tags for this file." @@ -110,6 +119,20 @@ describe('elements/content-sidebar/DocGenSidebar', () => { await waitFor(() => expect(processingTagsMock).toHaveBeenCalledTimes(10)); }); + test('should re-trigger loadTags retrigger and successfully display the tags', async () => { + renderComponent({ + getDocGenTags: processAndResolveMock, + }); + + await jest.advanceTimersByTime(1000); + await jest.advanceTimersByTime(1000); + + await waitFor(() => expect(processAndResolveMock).toHaveBeenCalledTimes(2)); + const parentTag = await screen.findByText('about'); + + expect(parentTag).toBeVisible(); + }); + test('should re-trigger getDocGenTags on click on refresh button', async () => { renderComponent({ From c9bbe186cfb263aa09505ca49b5d695f7aa2812f Mon Sep 17 00:00:00 2001 From: rustam-e <6816931+rustam-e@users.noreply.github.com> Date: Tue, 28 Jan 2025 13:09:55 +0100 Subject: [PATCH 07/13] Update src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx Co-authored-by: Trevor <7311041+tjuanitas@users.noreply.github.com> --- src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx b/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx index ac18482d30..65cc6d58bc 100644 --- a/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx +++ b/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx @@ -87,9 +87,9 @@ const DocGenSidebar = ({ getDocGenTags }: Props) => { }, []); const loadTags = useCallback(async (attempts = 10) => { - if(attempts <= 0){ + if (attempts <= 0) { setIsLoading(false); - return + return; } setIsLoading(true); try { From 84a3be5b1804c27188076431555c378bd2537a9e Mon Sep 17 00:00:00 2001 From: rustam-e <6816931+rustam-e@users.noreply.github.com> Date: Tue, 28 Jan 2025 13:13:10 +0100 Subject: [PATCH 08/13] Update src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx Co-authored-by: Trevor <7311041+tjuanitas@users.noreply.github.com> --- src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx b/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx index 65cc6d58bc..1e5facf323 100644 --- a/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx +++ b/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx @@ -102,7 +102,7 @@ const DocGenSidebar = ({ getDocGenTags }: Props) => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - const { data } = response || []; + const { data } = response; // anything that is not an image tag for this view is treated as a text tag const textTags = data?.filter(tag => tag.tag_type !== 'image') || []; const imageTags = data?.filter(tag => tag.tag_type === 'image') || []; From f60cd60b73c691da7871dc8ef091be0f9d2bb81f Mon Sep 17 00:00:00 2001 From: rustam-e Date: Tue, 28 Jan 2025 13:14:58 +0100 Subject: [PATCH 09/13] chore(docgen): address code review comments --- .../DocGenSidebar/DocGenSidebar.tsx | 79 ++++++++++--------- 1 file changed, 41 insertions(+), 38 deletions(-) diff --git a/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx b/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx index 1e5facf323..d15f4b455e 100644 --- a/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx +++ b/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx @@ -33,6 +33,8 @@ import commonMessages from '../../common/messages'; import './DocGenSidebar.scss'; import { DocGenTag, DocGenTemplateTagsResponse, JsonPathsMap } from './types'; +const DEFAULT_RETRIES = 10; + type ExternalProps = { enabled: boolean; getDocGenTags: () => Promise; @@ -86,49 +88,50 @@ const DocGenSidebar = ({ getDocGenTags }: Props) => { return jsonPathsMap; }, []); - const loadTags = useCallback(async (attempts = 10) => { - if (attempts <= 0) { - setIsLoading(false); - return; - } - setIsLoading(true); - try { - const response: DocGenTemplateTagsResponse = await getDocGenTags(); - if(response.message){ - setTimeout(() => { - loadTags.call(this, attempts - 1); - }, 1000); - } else if (response && !!response.data) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - - const { data } = response; - // anything that is not an image tag for this view is treated as a text tag - const textTags = data?.filter(tag => tag.tag_type !== 'image') || []; - const imageTags = data?.filter(tag => tag.tag_type === 'image') || []; - setTags({ - text: textTags, - image: imageTags, - }); - setJsonPaths({ - textTree: tagsToJsonPaths(textTags), - imageTree: tagsToJsonPaths(imageTags), - }); - setHasError(false); + const loadTags = useCallback( + async (attempts = 10) => { + if (attempts <= 0) { setIsLoading(false); - } else { + return; + } + setIsLoading(true); + try { + const response: DocGenTemplateTagsResponse = await getDocGenTags(); + if (response.message) { + setTimeout(() => { + loadTags.call(this, attempts - 1); + }, 1000); + } else if (response && !!response.data) { + const { data } = response; + // anything that is not an image tag for this view is treated as a text tag + const textTags = data?.filter(tag => tag.tag_type !== 'image') || []; + const imageTags = data?.filter(tag => tag.tag_type === 'image') || []; + setTags({ + text: textTags, + image: imageTags, + }); + setJsonPaths({ + textTree: tagsToJsonPaths(textTags), + imageTree: tagsToJsonPaths(imageTags), + }); + setHasError(false); + setIsLoading(false); + } else { + setHasError(true); + setIsLoading(false); + } + } catch (error) { setHasError(true); setIsLoading(false); } - } catch (error) { - setHasError(true); - } - // disabling eslint because the getDocGenTags prop is changing very frequently - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [tagsToJsonPaths]); + // disabling eslint because the getDocGenTags prop is changing very frequently + // eslint-disable-next-line react-hooks/exhaustive-deps + }, + [tagsToJsonPaths], + ); useEffect(() => { - loadTags(10); + loadTags(DEFAULT_RETRIES); }, [loadTags]); const isEmpty = tags.image.length + tags.text.length === 0; @@ -136,7 +139,7 @@ const DocGenSidebar = ({ getDocGenTags }: Props) => { return (
- {hasError && loadTags(10)} />} + {hasError && loadTags(DEFAULT_RETRIES)} />} {isLoading && ( Date: Tue, 28 Jan 2025 13:17:36 +0100 Subject: [PATCH 10/13] fix(docgen): bad merge --- src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx b/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx index d15f4b455e..0a9def3134 100644 --- a/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx +++ b/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx @@ -124,9 +124,9 @@ const DocGenSidebar = ({ getDocGenTags }: Props) => { setHasError(true); setIsLoading(false); } - // disabling eslint because the getDocGenTags prop is changing very frequently - // eslint-disable-next-line react-hooks/exhaustive-deps }, + // disabling eslint because the getDocGenTags prop is changing very frequently + // eslint-disable-next-line react-hooks/exhaustive-deps [tagsToJsonPaths], ); From d90c991f9de9b2f5d30d21168da2490c4152a6b5 Mon Sep 17 00:00:00 2001 From: rustam-e Date: Mon, 3 Feb 2025 13:54:23 +0100 Subject: [PATCH 11/13] fix(docgen): address code review comments --- .../DocGenSidebar/DocGenSidebar.tsx | 6 +-- .../__tests__/DocGenSidebar.test.tsx | 45 ++++++++++--------- 2 files changed, 26 insertions(+), 25 deletions(-) diff --git a/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx b/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx index 0a9def3134..9030587f97 100644 --- a/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx +++ b/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx @@ -89,7 +89,7 @@ const DocGenSidebar = ({ getDocGenTags }: Props) => { }, []); const loadTags = useCallback( - async (attempts = 10) => { + async (attempts = DEFAULT_RETRIES) => { if (attempts <= 0) { setIsLoading(false); return; @@ -97,11 +97,11 @@ const DocGenSidebar = ({ getDocGenTags }: Props) => { setIsLoading(true); try { const response: DocGenTemplateTagsResponse = await getDocGenTags(); - if (response.message) { + if (response?.message) { setTimeout(() => { loadTags.call(this, attempts - 1); }, 1000); - } else if (response && !!response.data) { + } else if (response?.data) { const { data } = response; // anything that is not an image tag for this view is treated as a text tag const textTags = data?.filter(tag => tag.tag_type !== 'image') || []; diff --git a/src/elements/content-sidebar/__tests__/DocGenSidebar.test.tsx b/src/elements/content-sidebar/__tests__/DocGenSidebar.test.tsx index 1b072ffe41..3ad31de282 100644 --- a/src/elements/content-sidebar/__tests__/DocGenSidebar.test.tsx +++ b/src/elements/content-sidebar/__tests__/DocGenSidebar.test.tsx @@ -13,19 +13,26 @@ const docGenSidebarProps = { ), }; -const processAndResolveMock = jest.fn() -.mockImplementationOnce(() => Promise.resolve({ - message: "Processing tags for this file." -})) -.mockImplementationOnce(() => Promise.resolve({ - pagination: {}, - data: mockData, -})); +const processAndResolveMock = jest + .fn() + .mockImplementationOnce(() => + Promise.resolve({ + message: 'Processing tags for this file.', + }), + ) + .mockImplementationOnce(() => + Promise.resolve({ + pagination: {}, + data: mockData, + }), + ); const noTagsMock = jest.fn().mockReturnValue(Promise.resolve({ data: [] })); -const processingTagsMock = jest.fn().mockReturnValue(Promise.resolve({ - "message": "Processing tags for this file." -})); +const processingTagsMock = jest.fn().mockReturnValue( + Promise.resolve({ + message: 'Processing tags for this file.', + }), +); const errorTagsMock = jest.fn().mockRejectedValue([]); const noDataMock = jest.fn().mockReturnValue(Promise.resolve({})); @@ -105,16 +112,11 @@ describe('elements/content-sidebar/DocGenSidebar', () => { getDocGenTags: processingTagsMock, }); - await jest.advanceTimersByTime(1000); - await jest.advanceTimersByTime(1000); - await jest.advanceTimersByTime(1000); - await jest.advanceTimersByTime(1000); - await jest.advanceTimersByTime(1000); - await jest.advanceTimersByTime(1000); - await jest.advanceTimersByTime(1000); - await jest.advanceTimersByTime(1000); - await jest.advanceTimersByTime(1000); - await jest.advanceTimersByTime(1000); + const promises = []; + for (let i = 0; i < 10; i + 1) { + promises.push(jest.advanceTimersByTime(1000)); + } + await Promise.all(promises); await waitFor(() => expect(processingTagsMock).toHaveBeenCalledTimes(10)); }); @@ -133,7 +135,6 @@ describe('elements/content-sidebar/DocGenSidebar', () => { expect(parentTag).toBeVisible(); }); - test('should re-trigger getDocGenTags on click on refresh button', async () => { renderComponent({ getDocGenTags: errorTagsMock, From c48a5172fe04f0b27b276580c2ab98e68c09e46e Mon Sep 17 00:00:00 2001 From: rustam-e Date: Mon, 3 Feb 2025 14:06:20 +0100 Subject: [PATCH 12/13] fix(docgen): remove Promise.all --- .../__tests__/DocGenSidebar.test.tsx | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/elements/content-sidebar/__tests__/DocGenSidebar.test.tsx b/src/elements/content-sidebar/__tests__/DocGenSidebar.test.tsx index 3ad31de282..96f2531b9e 100644 --- a/src/elements/content-sidebar/__tests__/DocGenSidebar.test.tsx +++ b/src/elements/content-sidebar/__tests__/DocGenSidebar.test.tsx @@ -112,11 +112,16 @@ describe('elements/content-sidebar/DocGenSidebar', () => { getDocGenTags: processingTagsMock, }); - const promises = []; - for (let i = 0; i < 10; i + 1) { - promises.push(jest.advanceTimersByTime(1000)); - } - await Promise.all(promises); + await jest.advanceTimersByTime(1000); + await jest.advanceTimersByTime(1000); + await jest.advanceTimersByTime(1000); + await jest.advanceTimersByTime(1000); + await jest.advanceTimersByTime(1000); + await jest.advanceTimersByTime(1000); + await jest.advanceTimersByTime(1000); + await jest.advanceTimersByTime(1000); + await jest.advanceTimersByTime(1000); + await jest.advanceTimersByTime(1000); await waitFor(() => expect(processingTagsMock).toHaveBeenCalledTimes(10)); }); From e351348c90c4055587f4e116dd1024b06b0d6c04 Mon Sep 17 00:00:00 2001 From: rustam-e Date: Tue, 4 Feb 2025 14:13:30 +0100 Subject: [PATCH 13/13] fix(docgen): remove timeout --- .../DocGenSidebar/DocGenSidebar.tsx | 4 +--- .../__tests__/DocGenSidebar.test.tsx | 14 -------------- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx b/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx index 9030587f97..3f35cddd7a 100644 --- a/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx +++ b/src/elements/content-sidebar/DocGenSidebar/DocGenSidebar.tsx @@ -98,9 +98,7 @@ const DocGenSidebar = ({ getDocGenTags }: Props) => { try { const response: DocGenTemplateTagsResponse = await getDocGenTags(); if (response?.message) { - setTimeout(() => { - loadTags.call(this, attempts - 1); - }, 1000); + loadTags.call(this, attempts - 1); } else if (response?.data) { const { data } = response; // anything that is not an image tag for this view is treated as a text tag diff --git a/src/elements/content-sidebar/__tests__/DocGenSidebar.test.tsx b/src/elements/content-sidebar/__tests__/DocGenSidebar.test.tsx index 96f2531b9e..8539a66f68 100644 --- a/src/elements/content-sidebar/__tests__/DocGenSidebar.test.tsx +++ b/src/elements/content-sidebar/__tests__/DocGenSidebar.test.tsx @@ -112,17 +112,6 @@ describe('elements/content-sidebar/DocGenSidebar', () => { getDocGenTags: processingTagsMock, }); - await jest.advanceTimersByTime(1000); - await jest.advanceTimersByTime(1000); - await jest.advanceTimersByTime(1000); - await jest.advanceTimersByTime(1000); - await jest.advanceTimersByTime(1000); - await jest.advanceTimersByTime(1000); - await jest.advanceTimersByTime(1000); - await jest.advanceTimersByTime(1000); - await jest.advanceTimersByTime(1000); - await jest.advanceTimersByTime(1000); - await waitFor(() => expect(processingTagsMock).toHaveBeenCalledTimes(10)); }); @@ -131,9 +120,6 @@ describe('elements/content-sidebar/DocGenSidebar', () => { getDocGenTags: processAndResolveMock, }); - await jest.advanceTimersByTime(1000); - await jest.advanceTimersByTime(1000); - await waitFor(() => expect(processAndResolveMock).toHaveBeenCalledTimes(2)); const parentTag = await screen.findByText('about');