Skip to content

Commit

Permalink
feat(metadata-sidebar): Add handler for Autofill button
Browse files Browse the repository at this point in the history
  • Loading branch information
jankowiakdawid committed Oct 9, 2024
1 parent dc074cf commit 21d879a
Show file tree
Hide file tree
Showing 11 changed files with 92 additions and 80 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@
"@box/cldr-data": "^34.2.0",
"@box/frontend": "^10.0.0",
"@box/languages": "^1.0.0",
"@box/metadata-editor": "^0.61.1",
"@box/metadata-editor": "^0.61.2",
"@box/react-virtualized": "9.22.3-rc-box.9",
"@cfaester/enzyme-adapter-react-18": "^0.8.0",
"@chromatic-com/storybook": "^1.6.1",
Expand Down Expand Up @@ -306,7 +306,7 @@
"@box/blueprint-web-assets": "^4.21.0",
"@box/box-ai-content-answers": "^0.50.5",
"@box/cldr-data": ">=34.2.0",
"@box/metadata-editor": "^0.61.1",
"@box/metadata-editor": "^0.61.2",
"@box/react-virtualized": "9.22.3-rc-box.9",
"@hapi/address": "^2.1.4",
"axios": "^0.25.0",
Expand Down
1 change: 1 addition & 0 deletions src/api/Intelligence.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ class Intelligence extends Base {
suggestionsResponse = await this.xhr.post({
url,
data: request,
id: `file_${request.items[0].id}`,
});
} catch (e) {
const { status } = e;
Expand Down
4 changes: 4 additions & 0 deletions src/api/__tests__/Intelligence.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ describe('api/Intelligence', () => {

describe('extractStructured()', () => {
const request = {
items: [{ id: '123', type: 'file' }],
metadata_template: {
type: 'metadata_template',
scope: 'global',
Expand All @@ -124,6 +125,7 @@ describe('api/Intelligence', () => {
expect(suggestions).toEqual(suggestionsFromServer);
expect(intelligence.xhr.post).toHaveBeenCalledWith({
url: `${intelligence.getBaseApiUrl()}/ai/extract_structured`,
id: 'file_123',
data: request,
});
});
Expand All @@ -143,6 +145,7 @@ describe('api/Intelligence', () => {
expect(intelligence.xhr.post).toHaveBeenCalledWith({
url: `${intelligence.getBaseApiUrl()}/ai/extract_structured`,
data: request,
id: 'file_123',
});
});

Expand All @@ -161,6 +164,7 @@ describe('api/Intelligence', () => {
expect(intelligence.xhr.post).toHaveBeenCalledWith({
url: `${intelligence.getBaseApiUrl()}/ai/extract_structured`,
data: request,
id: 'file_123',
});
});
});
Expand Down
40 changes: 14 additions & 26 deletions src/elements/content-sidebar/MetadataInstanceEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {
AutofillContextProvider,
AutofillContextProviderProps,
MetadataInstanceForm,
withApiWrapper,
type FormValues,
type JSONPatchOperations,
type MetadataTemplateInstance,
Expand All @@ -10,7 +9,6 @@ import React from 'react';

export interface MetadataInstanceEditorProps {
areAiSuggestionsAvailable: boolean;
fetchSuggestions: AutofillContextProviderProps['fetchSuggestions'];
isBoxAiSuggestionsEnabled: boolean;
isDeleteButtonDisabled: boolean;
isUnsavedChangesModalOpen: boolean;
Expand All @@ -22,9 +20,8 @@ export interface MetadataInstanceEditorProps {
template: MetadataTemplateInstance;
}

const MetadataInstanceEditor: React.FC<MetadataInstanceEditorProps> = ({
export const MetadataInstanceEditor: React.FC<MetadataInstanceEditorProps> = ({
areAiSuggestionsAvailable,
fetchSuggestions,
isBoxAiSuggestionsEnabled,
isDeleteButtonDisabled,
isUnsavedChangesModalOpen,
Expand All @@ -35,29 +32,20 @@ const MetadataInstanceEditor: React.FC<MetadataInstanceEditorProps> = ({
setIsUnsavedChangesModalOpen,
template,
}) => {
const handleCancel = () => {
onCancel();
};

return (
<AutofillContextProvider
fetchSuggestions={fetchSuggestions}
<MetadataInstanceForm
areAiSuggestionsAvailable={areAiSuggestionsAvailable}
isAiSuggestionsFeatureEnabled={isBoxAiSuggestionsEnabled}
>
<MetadataInstanceForm
areAiSuggestionsAvailable={areAiSuggestionsAvailable}
isAiSuggestionsFeatureEnabled={isBoxAiSuggestionsEnabled}
isDeleteButtonDisabled={isDeleteButtonDisabled}
isUnsavedChangesModalOpen={isUnsavedChangesModalOpen}
onCancel={handleCancel}
onDelete={onDelete}
onDiscardUnsavedChanges={onDiscardUnsavedChanges}
onSubmit={onSubmit}
selectedTemplateInstance={template}
setIsUnsavedChangesModalOpen={setIsUnsavedChangesModalOpen}
/>
</AutofillContextProvider>
isDeleteButtonDisabled={isDeleteButtonDisabled}
isUnsavedChangesModalOpen={isUnsavedChangesModalOpen}
onCancel={onCancel}
onDelete={onDelete}
onDiscardUnsavedChanges={onDiscardUnsavedChanges}
onSubmit={onSubmit}
selectedTemplateInstance={template}
setIsUnsavedChangesModalOpen={setIsUnsavedChangesModalOpen}
/>
);
};

export default MetadataInstanceEditor;
export default withApiWrapper(MetadataInstanceEditor);
20 changes: 8 additions & 12 deletions src/elements/content-sidebar/MetadataSidebarRedesign.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,15 @@ import {
type MetadataTemplateInstance,
type MetadataTemplate,
} from '@box/metadata-editor';
import noop from 'lodash/noop';

import API from '../../api';
import SidebarContent from './SidebarContent';
import { withAPIContext } from '../common/api-context';
import { withErrorBoundary } from '../common/error-boundary';
import { withLogger } from '../common/logger';
import { useFeatureEnabled } from '../common/feature-checking';
import { ORIGIN_METADATA_SIDEBAR_REDESIGN, SIDEBAR_VIEW_METADATA } from '../../constants';
import { EVENT_JS_READY } from '../common/logger/constants';
import { useFeatureEnabled } from '../common/feature-checking';
import { mark } from '../../utils/performance';
import useSidebarMetadataFetcher, { STATUS } from './hooks/useSidebarMetadataFetcher';

Expand All @@ -34,7 +33,7 @@ import { type WithLoggerProps } from '../../common/types/logging';

import messages from '../common/messages';
import './MetadataSidebarRedesign.scss';
import MetadataInstanceEditor, { MetadataInstanceEditorProps } from './MetadataInstanceEditor';
import MetadataInstanceEditor from './MetadataInstanceEditor';
import { convertTemplateToTemplateInstance } from './utils/convertTemplateToTemplateInstance';
import { isExtensionSupportedForMetadataSuggestions } from './utils/isExtensionSupportedForMetadataSuggestions';

Expand Down Expand Up @@ -67,6 +66,7 @@ export interface MetadataSidebarRedesignProps extends PropsWithoutContext, Error

function MetadataSidebarRedesign({ api, elementId, fileId, onError, isFeatureEnabled }: MetadataSidebarRedesignProps) {
const {
extractSuggestions,
file,
handleCreateMetadataInstance,
handleDeleteMetadataInstance,
Expand Down Expand Up @@ -174,13 +174,6 @@ function MetadataSidebarRedesign({ api, elementId, fileId, onError, isFeatureEna
const showEditor = !showEmptyState && editingTemplate;
const showList = !showEditor && templateInstances.length > 0 && !editingTemplate;
const areAiSuggestionsAvailable = isExtensionSupportedForMetadataSuggestions(file?.extension ?? '');
const fetchSuggestions = React.useCallback<MetadataInstanceEditorProps['fetchSuggestions']>(
async (templateKey, fields) => {
// should use getIntelligenceAPI().extractStructured
return fields;
},
[],
);

return (
<SidebarContent
Expand All @@ -199,10 +192,11 @@ function MetadataSidebarRedesign({ api, elementId, fileId, onError, isFeatureEna
{editingTemplate && (
<MetadataInstanceEditor
areAiSuggestionsAvailable={areAiSuggestionsAvailable}
fetchSuggestions={fetchSuggestions}
isAiSuggestionsFeatureEnabled={isBoxAiSuggestionsEnabled}
isBoxAiSuggestionsEnabled={isBoxAiSuggestionsEnabled}
isDeleteButtonDisabled={isDeleteButtonDisabled}
isUnsavedChangesModalOpen={isUnsavedChangesModalOpen}
fetchSuggestions={extractSuggestions}
onCancel={handleCancel}
onDelete={handleDeleteInstance}
onDiscardUnsavedChanges={handleDiscardUnsavedChanges}
Expand All @@ -218,7 +212,9 @@ function MetadataSidebarRedesign({ api, elementId, fileId, onError, isFeatureEna
setEditingTemplate(templateInstance);
setIsDeleteButtonDisabled(false);
}}
onEditWithAutofill={noop}
onEditWithAutofill={templateInstance => {
setEditingTemplate(templateInstance);
}}
templateInstances={templateInstances}
/>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react';
import { type MetadataTemplateInstance } from '@box/metadata-editor';
import userEvent from '@testing-library/user-event';
import { screen, render } from '../../../test-utils/testing-library';
import MetadataInstanceEditor, { MetadataInstanceEditorProps } from '../MetadataInstanceEditor';
import { MetadataInstanceEditor, MetadataInstanceEditorProps } from '../MetadataInstanceEditor';

const mockOnCancel = jest.fn();
const mockOnDiscardUnsavedChanges = jest.fn();
Expand Down Expand Up @@ -50,7 +50,6 @@ describe('MetadataInstanceEditor', () => {

const defaultProps: MetadataInstanceEditorProps = {
areAiSuggestionsAvailable: true,
fetchSuggestions: jest.fn(),
isBoxAiSuggestionsEnabled: true,
isDeleteButtonDisabled: false,
isUnsavedChangesModalOpen: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,9 @@ describe('elements/content-sidebar/Metadata/MetadataSidebarRedesign', () => {

beforeEach(() => {
mockUseSidebarMetadataFetcher.mockReturnValue({
handleDeleteMetadataInstance: jest.fn(),
extractSuggestions: jest.fn(),
handleCreateMetadataInstance: jest.fn(),
handleDeleteMetadataInstance: jest.fn(),
handleUpdateMetadataInstance: jest.fn(),
templates: mockTemplates,
templateInstances: [],
Expand Down Expand Up @@ -113,8 +114,9 @@ describe('elements/content-sidebar/Metadata/MetadataSidebarRedesign', () => {

test('should render metadata sidebar with error', async () => {
mockUseSidebarMetadataFetcher.mockReturnValue({
handleDeleteMetadataInstance: jest.fn(),
extractSuggestions: jest.fn(),
handleCreateMetadataInstance: jest.fn(),
handleDeleteMetadataInstance: jest.fn(),
handleUpdateMetadataInstance: jest.fn(),
templateInstances: [],
templates: [],
Expand All @@ -135,6 +137,7 @@ describe('elements/content-sidebar/Metadata/MetadataSidebarRedesign', () => {

test('should render metadata sidebar with loading indicator', async () => {
mockUseSidebarMetadataFetcher.mockReturnValue({
extractSuggestions: jest.fn(),
handleCreateMetadataInstance: jest.fn(),
handleDeleteMetadataInstance: jest.fn(),
handleUpdateMetadataInstance: jest.fn(),
Expand Down Expand Up @@ -163,7 +166,10 @@ describe('elements/content-sidebar/Metadata/MetadataSidebarRedesign', () => {
});

test('should correctly render empty state when AI feature is disabled', () => {
renderComponent({ isBoxAiSuggestionsEnabled: false });
renderComponent(
{ isBoxAiSuggestionsEnabled: false },
{ wrapperProps: { features: { 'metadata.aiSuggestions.enabled': false } } },
);
expect(screen.getByRole('heading', { level: 2, name: 'Add Metadata Templates' })).toBeInTheDocument();
expect(
screen.getByText('Add Metadata to your file to support business operations, workflows, and more!'),
Expand All @@ -172,8 +178,9 @@ describe('elements/content-sidebar/Metadata/MetadataSidebarRedesign', () => {

test('should render metadata instance list when templates are present', () => {
mockUseSidebarMetadataFetcher.mockReturnValue({
handleDeleteMetadataInstance: jest.fn(),
extractSuggestions: jest.fn(),
handleCreateMetadataInstance: jest.fn(),
handleDeleteMetadataInstance: jest.fn(),
handleUpdateMetadataInstance: jest.fn(),
templateInstances: [mockCustomTemplateInstance],
templates: mockTemplates,
Expand Down
40 changes: 39 additions & 1 deletion src/elements/content-sidebar/hooks/useSidebarMetadataFetcher.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import * as React from 'react';
import getProp from 'lodash/get';
import { type MessageDescriptor } from 'react-intl';
import { type JSONPatchOperations, type MetadataTemplate, type MetadataTemplateInstance } from '@box/metadata-editor';
import {
type JSONPatchOperations,
type MetadataTemplate,
type MetadataTemplateInstance,
type MetadataTemplateField,
} from '@box/metadata-editor';
import API from '../../../api';
import { type ElementsXhrError } from '../../../common/types/api';
import { isUserCorrectableError } from '../../../utils/error';
Expand All @@ -20,6 +25,7 @@ export enum STATUS {
}
interface DataFetcher {
errorMessage: MessageDescriptor | null;
extractSuggestions: (templateKey: string, fields: MetadataTemplateField[]) => Promise<MetadataTemplateField[]>;
file: BoxItem | null;
handleCreateMetadataInstance: (
templateInstance: MetadataTemplateInstance,
Expand Down Expand Up @@ -174,6 +180,37 @@ function useSidebarMetadataFetcher(
[api, file, onApiError],
);

const extractSuggestions = React.useCallback(
async (templateKey: string, fields: MetadataTemplateField[]) => {
const aiAPI = api.getIntelligenceAPI();
let answer = {};
try {
answer = await aiAPI.extractStructured({
items: [file],
metadata_template: {
scope: 'enterprise',
template_key: templateKey,
type: 'metadata_template',
},
});
} catch (e) {
// eslint-disable-next-line no-console
}

return fields.map(field => {
const value = answer[field.key];
if (!value) {
return field;
}
return {
...field,
aiSuggestion: value,
};
});
},
[api, file],
);

React.useEffect(() => {
if (status === STATUS.IDLE) {
setStatus(STATUS.LOADING);
Expand All @@ -185,6 +222,7 @@ function useSidebarMetadataFetcher(
}, [api, fetchFileErrorCallback, fetchFileSuccessCallback, fileId, status]);

return {
extractSuggestions,
handleCreateMetadataInstance,
handleDeleteMetadataInstance,
handleUpdateMetadataInstance,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ export const MetadataInstanceEditorCancelChanges: StoryObj<typeof MetadataSideba

headlines = await canvas.findAllByRole('heading', { level: 1 });
expect(headlines).toHaveLength(1);
expect(headlines.map(heading => heading.textContent)).toEqual(expect.arrayContaining(['Custom Metadata']));
expect(headlines.map(heading => heading.textContent)).toEqual(expect.arrayContaining(['My Template']));

// cancel editing - back to list view
const cancelButton = await canvas.findByRole('button', { name: 'Cancel' });
Expand Down
7 changes: 4 additions & 3 deletions src/test-utils/testing-library.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,21 @@ import { render, type RenderOptions } from '@testing-library/react';
// Data Providers
import { TooltipProvider } from '@box/blueprint-web';
import { IntlProvider } from 'react-intl';
import { AutofillContextProvider } from '@box/metadata-editor';
import { AutofillContextProvider, MetadataTemplateField } from '@box/metadata-editor';

import { FeatureProvider } from '../elements/common/feature-checking';

jest.unmock('react-intl');

const Wrapper = ({
children,
features = {},
isAiSuggestionsFeatureEnabled = false,
fetchSuggestions = () => Promise.resolve([]),
isAiSuggestionsFeatureEnabled = false,
}) => (
<AutofillContextProvider
isAiSuggestionsFeatureEnabled={isAiSuggestionsFeatureEnabled}
fetchSuggestions={fetchSuggestions}
isAiSuggestionsFeatureEnabled={isAiSuggestionsFeatureEnabled}
>
<FeatureProvider features={features}>
<TooltipProvider>
Expand Down
Loading

0 comments on commit 21d879a

Please sign in to comment.