Skip to content

feat(content-answers): Upgrade Content Answers - #3658

Merged
greg-in-a-box merged 21 commits into
box:masterfrom
greg-in-a-box:qa-modal-swap
Oct 2, 2024
Merged

greg-in-a-box merged 21 commits into
box:masterfrom
greg-in-a-box:qa-modal-swap

Conversation

@greg-in-a-box

@greg-in-a-box greg-in-a-box commented Sep 17, 2024 •

Copy link
Copy Markdown
Contributor

Updated Content Answer with the latest features

@greg-in-a-box greg-in-a-box changed the title Qa modal swap feat(content-answers): Upgrade Content Answers Sep 17, 2024
@greg-in-a-box
greg-in-a-box force-pushed the qa-modal-swap branch 4 times, most recently from a6cb441 to fc6826c Compare September 19, 2024 20:31
@greg-in-a-box
greg-in-a-box marked this pull request as ready for review September 19, 2024 20:35
@greg-in-a-box
greg-in-a-box requested review from a team as code owners September 19, 2024 20:35
isCitationsEnabled?: boolean;
isMarkdownEnabled?: boolean;
isResetChatEnabled?: boolean;
onAsk?: () => void;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think onAsk will always exist here since the upper level ContentAnswers.tsx will always pass down handleAsk() as onAsk, also same as onRequestClose

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

its separated like that because a customer can submit onAsk and onRequestClose from the root lvl via contentAnswersProps

isCompleted: true,
isLoading: false,
};
setIsLoading(false);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we don't need this line since setIsLoading(false); will be called after the try catch in handleAsk() function

created_at: q.created_at,
}));

const nextQuestions = [...(isRetry ? questions.slice(0, -1) : questions)];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be more like prevQuestions instead

@greg-in-a-box
greg-in-a-box force-pushed the qa-modal-swap branch 2 times, most recently from 7eff2e4 to f216c82 Compare September 20, 2024 19:58
rustam-e and others added 6 commits September 20, 2024 16:05
…ox#3626)

* fix(docgen-sidebar): add collapsible component to hide nested tags

* fix(docgen-sidebar): nesting of tags in tag tree

* fix(docgen-tags): update styles to use blueprint tokens

* fix(docgen-tags): replace bdl tokens with blueprint tokens

* fix(docgen-tags): convert test from enzyme to rtl

* fix(docgen-tags): remove snapshots, reuse blueprint loader

* fix(docgen-tags): address code review comments

* fix(docgen-tags): remove the sidebar test file

* fix(docgen-tags): file with updated name

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

Co-authored-by: greg-in-a-box <103291617+greg-in-a-box@users.noreply.github.com>

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

Co-authored-by: greg-in-a-box <103291617+greg-in-a-box@users.noreply.github.com>

* fix(docgen-sidebar): address code review comments

* fix(docgen-sidebar): address code review comments

* fix(docgen-sidebar): remove unused mock

---------

Co-authored-by: greg-in-a-box <103291617+greg-in-a-box@users.noreply.github.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Comment thread src/api/Intelligence.js Outdated
Comment on lines 21 to 22
* @param dialogueHistory
* @param {Array<object>} items - Array of items to ask about

@benjamin-shen benjamin-shen Sep 24, 2024 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: switch order to match actual params

Suggested change
* @param dialogueHistory
* @param {Array<object>} items - Array of items to ask about
* @param {Array<object>} items - Array of items to ask about
* @param dialogueHistory

Comment thread src/api/Intelligence.js Outdated
question: QuestionType,
items: Array<BoxItem>,
dialogueHistory: Array<QuestionType> = [],
options: { include_citations: boolean } = {},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this supposed to be

Suggested change
options: { include_citations: boolean } = {},
options: { include_citations?: boolean } = {},

? if it's defaulted to {}

Comment thread src/elements/common/content-answers/ContentAnswersModal.tsx
Comment on lines +92 to +94
const errorMessage = error ? error.message || '' : '';
const isRateLimitingError =
(error && error.response && error.response.status === 429) || rateLimitingRegex.test(errorMessage);

@benjamin-shen benjamin-shen Sep 24, 2024 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could use optional chaining here if you prefer. non-blocking, totally up to you

Suggested change
const errorMessage = error ? error.message || '' : '';
const isRateLimitingError =
(error && error.response && error.response.status === 429) || rateLimitingRegex.test(errorMessage);
const errorMessage = error?.message || '';
const isRateLimitingError =
(error?.response?.status === 429) || rateLimitingRegex.test(errorMessage);

const handleAsk = useCallback(
async (question: QuestionType, aiAgent: AgentType, isRetry = false) => {
!!onAsk && onAsk();
const id = file && file.id;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when can file be falsy? what happens here if file is falsy?

submitQuestion={handleAsk}
suggestedQuestions={suggestedQuestions || localizedQuestions}
warningNotice={spreadsheetNotice}
warningNoticeAriaLabel={formatMessage(messages.welcomeMessageSpreadsheetNoticeAriaLabel)}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this also be conditional on isSpreadsheet?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warningNotice controls if the ariaLabel is used.

Comment on lines +180 to +183
onModalClose={handleOnRequestClose}
open={isOpen}
onOpenChange={handleOnRequestClose}
onClearAction={handleClearConversation}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

idk if you were going for alphabetizing here, but if so, these should be reordered

);
};

export default withAPIContext(withCurrentUser(ContentAnswersModal));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

withCurrentUser already has withAPIContext, is it needed here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yea we need this here for the name and avatar url

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think we talked about this but can you remind me what's happening here? shouldn't the props forwarded from withAPIContext be passed through withCurrentUser to this component?

Comment thread src/elements/common/content-answers/ContentAnswersOpenButton.scss Outdated
Comment thread src/elements/common/content-answers/ContentAnswersOpenButton.scss Outdated
Comment thread src/elements/common/content-answers/ContentAnswersOpenButton.scss
Comment thread src/elements/common/content-answers/ContentAnswersOpenButton.scss Outdated
Comment thread src/elements/common/content-answers/ContentAnswersModal.tsx Outdated
Comment thread src/elements/common/content-answers/ContentAnswers.tsx Outdated
Comment thread src/elements/common/content-answers/ContentAnswersModal.tsx
Comment thread src/elements/common/content-answers/ContentAnswers.tsx Outdated
@greg-in-a-box
greg-in-a-box force-pushed the qa-modal-swap branch 2 times, most recently from 279f2bc to b4a3884 Compare September 25, 2024 19:46
expect(modal).toBeInTheDocument();

const textArea = screen.getByRole('textbox', { name: 'Ask anything about this doc' });
fireEvent.change(textArea, { target: { value: prompt } });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the prompt here seems not defined in this file.

And could we try using userEvent.type(textArea, 'question prompt'). Same for ones used in ContentAnswersModal.test.tsx file.
Example https://github.com/box/box-ui-elements/blob/master/src/elements/content-explorer/__tests__/RenameDialog.test.tsx#L59-L62


type Props = {
file: BoxItem;
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should the prop types above changed to interface like below

export interface contentAnswersProps extends ContentAnswersModalExternalProps {
    show?: boolean;
    file: BoxItem;
}

Comment on lines 187 to 188
onOpenChange={handleOnRequestClose}
onModalClose={handleOnRequestClose}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

m comes before o in the alphabet!

@greg-in-a-box
greg-in-a-box force-pushed the qa-modal-swap branch 2 times, most recently from 4fc491f to e04c240 Compare September 25, 2024 22:56
Comment thread src/elements/common/content-answers/ContentAnswers.tsx
Comment thread src/elements/common/content-answers/ContentAnswersOpenButton.scss Outdated
Comment thread src/elements/common/content-answers/ContentAnswersOpenButton.scss Outdated
Comment thread src/elements/common/content-answers/ContentAnswersOpenButton.scss Outdated
Comment on lines +1323 to +1325
<Notification.Provider>
<Notification.Viewport />
<TooltipProvider>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

internal apps will likely have these providers already. any concerns with nested providers?

Comment thread src/elements/common/content-answers/ContentAnswers.tsx Outdated
Comment thread src/elements/common/content-answers/ContentAnswers.tsx Outdated

const currentExtension = getProp(file, 'extension');
return (
<div className="bdl-ContentAnswers">

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this class name is no longer used i think. or should be changed to be-ContentAnswers

Comment thread src/elements/content-preview/README.md Outdated
| sharedLink | string | | *See the [developer docs](https://developer.box.com/docs/box-content-preview#section-options).* |
| sharedLinkPassword | string | | *See the [developer docs](https://developer.box.com/docs/box-content-preview#section-options).* |
| showAnnotations | boolean | `true` | *See the [developer docs](https://developer.box.com/docs/box-content-preview#section-options).* |
| shouldProvide | boolean | `true` | Decide if it should wrap the element with the `Notification` and `Tooltip` Provider. Set this to false if you already wrap your app with both of these providers. |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if this is a boolean then maybe hasProviders?


alternate approach:
or what about an array/object that specifies providers? that way it's more configurable depending on the providers that the consumer already has

for example:

// in consuming app
const providers = ['date-picker', 'notification', 'tooltip']; // Blueprint DatePicker has a provider we'll eventually need to include

<ContentPreview providers={providers} ... />


// in new providers component
const ElementProviders = ({ children, providers = ['notification', 'tooltip'] }) => {
    let element = children;

    providers.forEach(provider => {
        switch (provider) {
            case 'date-picker':
                ...
                break;
            case 'notification':
                element = <Notification.Provider>...</Notification.Provider>;
                break;
            case 'tooltip':
                element = <TooltipProvider>{element}</TooltipProvider>;
                break;
        }
    });

    return element;
};

Comment thread src/elements/content-preview/preview-header/PreviewHeader.js Outdated
Comment thread src/api/Intelligence.js Outdated
const [questions, setQuestions] = useState<QuestionType[]>([]);
let localizedQuestions: SuggestedQuestionType[] = [];

if (!suggestedQuestions) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fyi an empty array for suggestedQuestions is truthy so this only enters the if statement for undefined. is that okay?

my guess is that it could render empty space but that seems out of scope for this component and more of something that should be handled by the shared-feature

let localizedQuestions: SuggestedQuestionType[] = [];

if (!suggestedQuestions) {
localizedQuestions = DOCUMENT_SUGGESTED_QUESTIONS.map(question => ({

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this something the webapp is doing for all file extensions?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread src/elements/common/content-answers/ContentAnswersModal.tsx Outdated
);
};

export default withAPIContext(withCurrentUser(ContentAnswersModal));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think we talked about this but can you remind me what's happening here? shouldn't the props forwarded from withAPIContext be passed through withCurrentUser to this component?

Comment thread src/elements/common/content-answers/ContentAnswersOpenButton.scss Outdated
Comment thread .storybook/modes.ts Outdated
Comment on lines +5 to +6
// Note, you can still specify the more
// specific options listed in the section above

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does "section above" refer to?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it looks like this file/object is used once, what about adding viewport property directly to preview.tsx?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Provide sounds a little odd to me as a component because of the verb. Providers sounds a little clearer

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

related to conversation about granular providers, we might need to consider how customers can enable/disable providers like Theme, Router (react-router upgrade)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes for theme, but router no since that is more universal, unless the backwards compat guide says we need one

import { render } from '../../../test-utils/testing-library';
import Provide from '../Provide';

describe('Provide component', () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be path to component

Comment thread src/elements/content-preview/README.md Outdated
| sharedLink | string | | *See the [developer docs](https://developer.box.com/docs/box-content-preview#section-options).* |
| sharedLinkPassword | string | | *See the [developer docs](https://developer.box.com/docs/box-content-preview#section-options).* |
| showAnnotations | boolean | `true` | *See the [developer docs](https://developer.box.com/docs/box-content-preview#section-options).* |
| hasProviders | boolean | `true` | Decide if it should wrap the element with the `Notification` and `Tooltip` Provider. Set this to false if you already wrap your app with both of these providers. |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: 🔤

responseInterceptor?: Function,
sharedLink?: string,
sharedLinkPassword?: string,
hasProviders?: boolean,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: 🔤

expect(screen.getByRole('region', { name: 'Notifications (F8)' })).toBeInTheDocument();
});

test('renders only children when shouldProvide is false', () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
test('renders only children when shouldProvide is false', () => {
test('renders only children when hasProviders is false', () => {

expect(screen.queryByRole('region', { name: 'Notifications (F8)' })).not.toBeInTheDocument();
});

test('throws an error if more than one child is provided when shouldProvide is false', () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
test('throws an error if more than one child is provided when shouldProvide is false', () => {
test('throws an error if more than one child is provided when hasProviders is false', () => {

).toThrow();
});

test('renders children within Notification and TooltipProvider by default when shouldProvide is not provided', () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
test('renders children within Notification and TooltipProvider by default when shouldProvide is not provided', () => {
test('renders children within Notification and TooltipProvider by default when hasProviders is not provided', () => {

renderComponent({ fileExtension: 'invalid', onClick });
expect(onClick).toBeCalledTimes(0);
const button = screen.getByRole('button');
await userEvent.click(button, { pointerEventsCheck: 0 });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's pointerEventsCheck for? is this optimization?

@greg-in-a-box greg-in-a-box Oct 1, 2024 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it throws an error like this Error: Unable to perform pointer interaction as the element has pointer-events: none if that flag isnt there, the button from blueprint set pointer-events to none

test('should display not allowed tooltip', async () => {
renderComponent({ fileExtension: 'invalid' });
const button = screen.getByRole('button', { name: 'Box AI' });
await userEvent.hover(button, { pointerEventsCheck: 0 });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is hover using pointer events?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

tjuanitas
tjuanitas previously approved these changes Oct 2, 2024

@tjuanitas tjuanitas left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

Comment thread src/api/Intelligence.js
Comment on lines +20 to +22
* @param {QuestionType} question - Object should at least contain the prompt, which is the question to ask
* @param {Array<object>} items - Array of items to ask about
* @param dialogueHistory
* @param options
* @param {Array<QuestionType>} dialogueHistory - Array of previous questions object that already have answers

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

naming QuestionType with a -Type suffix instead of Question is like naming something FooInterface instead of Foo, right? is that an existing naming convention in this repo? if not, can we rename QuestionType to Question?

is it possible to make these types stronger, eg. define a QuestionWithAnswer type that is Question & { answer... }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that type is coming from the shared-feature, its the name they are using

Comment thread src/elements/common/Providers.js.flow Outdated
expect(answer).toBeInTheDocument();

expect(modal.getByText('Based on:')).toBeInTheDocument();
expect(modal.queryByText('Based on:')).not.toBeInTheDocument();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where is the text 'Based on:' actually rendered from? does it come from the api response or is it hardcoded in the repo?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

part of the citation feature that appears in the answer if the citations are present via the shared-features

@greg-in-a-box
greg-in-a-box merged commit 002d496 into box:master Oct 2, 2024
jankowiakdawid pushed a commit to jankowiakdawid/box-ui-elements that referenced this pull request Oct 3, 2024
* fix(docgen-sidebar): add collapsible component to hide nested tags (box#3626)

* fix(docgen-sidebar): add collapsible component to hide nested tags

* fix(docgen-sidebar): nesting of tags in tag tree

* fix(docgen-tags): update styles to use blueprint tokens

* fix(docgen-tags): replace bdl tokens with blueprint tokens

* fix(docgen-tags): convert test from enzyme to rtl

* fix(docgen-tags): remove snapshots, reuse blueprint loader

* fix(docgen-tags): address code review comments

* fix(docgen-tags): remove the sidebar test file

* fix(docgen-tags): file with updated name

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

Co-authored-by: greg-in-a-box <103291617+greg-in-a-box@users.noreply.github.com>

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

Co-authored-by: greg-in-a-box <103291617+greg-in-a-box@users.noreply.github.com>

* fix(docgen-sidebar): address code review comments

* fix(docgen-sidebar): address code review comments

* fix(docgen-sidebar): remove unused mock

---------

Co-authored-by: greg-in-a-box <103291617+greg-in-a-box@users.noreply.github.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>

* chore(storybook): Added global types for storybook

* feat(content-answers): Upgrade Content Answers

* feat(content-answers): fix tests

* feat(content-answers): Enabled some new features

* feat(content-answers): Feedback

* chore(content-answers): feedback

* feat(content-answers): Feedback

* feat(content-answers): Feedback

* feat(content-answers): Feedback

* feat(content-answers): vrt width

* feat(content-answers): Feedback

* feat(content-answers): Feedback

* feat(content-answers): Providers

* feat(content-answers): Providers

* feat(content-answers): Feedback

* feat(content-answers): Feedback

* feat(content-answers): tests

* feat(content-answers): Feedback

* feat(content-answers): Feedback

---------

Co-authored-by: rustam-e <6816931+rustam-e@users.noreply.github.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants