box-ui-elements 23.5.0-beta.3 → 24.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/explorer.css +1 -1
- package/dist/explorer.js +1 -1
- package/dist/picker.js +1 -1
- package/dist/preview.css +1 -1
- package/dist/preview.js +1 -1
- package/dist/sidebar.css +1 -1
- package/dist/sidebar.js +1 -1
- package/es/constants.js +13 -0
- package/es/constants.js.flow +13 -0
- package/es/constants.js.map +1 -1
- package/es/elements/common/content-answers/ContentAnswersModal.js +1 -3
- package/es/elements/common/content-answers/ContentAnswersModal.js.map +1 -1
- package/es/elements/common/sub-header/SubHeader.js +3 -0
- package/es/elements/common/sub-header/SubHeader.js.map +1 -1
- package/es/elements/common/sub-header/SubHeaderLeftV2.js +3 -23
- package/es/elements/common/sub-header/SubHeaderLeftV2.js.map +1 -1
- package/es/elements/common/sub-header/SubHeaderRight.js +6 -2
- package/es/elements/common/sub-header/SubHeaderRight.js.map +1 -1
- package/es/elements/content-explorer/ContentExplorer.js +55 -10
- package/es/elements/content-explorer/ContentExplorer.js.map +1 -1
- package/es/elements/content-explorer/ContentExplorer.scss +12 -0
- package/es/elements/content-explorer/MetadataSidePanel.js +92 -0
- package/es/elements/content-explorer/MetadataSidePanel.js.map +1 -0
- package/es/elements/content-explorer/MetadataSidePanel.scss +12 -0
- package/es/elements/content-explorer/stories/tests/MetadataView-visual.stories.js +54 -3
- package/es/elements/content-explorer/stories/tests/MetadataView-visual.stories.js.map +1 -1
- package/es/elements/content-explorer/utils.js +67 -0
- package/es/elements/content-explorer/utils.js.map +1 -0
- package/es/elements/content-sidebar/BoxAISidebar.js.map +1 -1
- package/es/elements/content-sidebar/BoxAISidebarContent.js +2 -4
- package/es/elements/content-sidebar/BoxAISidebarContent.js.map +1 -1
- package/es/elements/content-sidebar/stories/BoxAISidebar.stories.js +0 -1
- package/es/elements/content-sidebar/stories/BoxAISidebar.stories.js.map +1 -1
- package/es/elements/content-sidebar/stories/tests/BoxAISidebar-visual.stories.js +0 -1
- package/es/elements/content-sidebar/stories/tests/BoxAISidebar-visual.stories.js.map +1 -1
- package/es/src/elements/common/content-answers/ContentAnswersModal.d.ts +0 -1
- package/es/src/elements/common/sub-header/SubHeader.d.ts +2 -1
- package/es/src/elements/common/sub-header/SubHeaderLeftV2.d.ts +1 -1
- package/es/src/elements/common/sub-header/SubHeaderRight.d.ts +4 -1
- package/es/src/elements/content-explorer/ContentExplorer.d.ts +15 -0
- package/es/src/elements/content-explorer/MetadataSidePanel.d.ts +13 -0
- package/es/src/elements/content-explorer/__tests__/MetadataSidePanel.test.d.ts +1 -0
- package/es/src/elements/content-explorer/stories/tests/MetadataView-visual.stories.d.ts +2 -0
- package/es/src/elements/content-explorer/utils.d.ts +22 -0
- package/es/src/elements/content-sidebar/BoxAISidebar.d.ts +0 -1
- package/es/src/elements/content-sidebar/stories/BoxAISidebar.stories.d.ts +0 -1
- package/package.json +2 -2
- package/src/constants.js +13 -0
- package/src/elements/common/content-answers/ContentAnswersModal.tsx +0 -3
- package/src/elements/common/content-answers/__tests__/ContentAnswersModal.test.tsx +7 -2
- package/src/elements/common/sub-header/SubHeader.tsx +4 -0
- package/src/elements/common/sub-header/SubHeaderLeftV2.tsx +3 -22
- package/src/elements/common/sub-header/SubHeaderRight.tsx +8 -2
- package/src/elements/content-explorer/ContentExplorer.scss +12 -0
- package/src/elements/content-explorer/ContentExplorer.tsx +135 -77
- package/src/elements/content-explorer/MetadataSidePanel.scss +12 -0
- package/src/elements/content-explorer/MetadataSidePanel.tsx +126 -0
- package/src/elements/content-explorer/__tests__/ContentExplorer.test.tsx +80 -16
- package/src/elements/content-explorer/__tests__/MetadataSidePanel.test.tsx +127 -0
- package/src/elements/content-explorer/stories/tests/MetadataView-visual.stories.tsx +43 -3
- package/src/elements/content-explorer/utils.ts +58 -0
- package/src/elements/content-sidebar/BoxAISidebar.tsx +0 -1
- package/src/elements/content-sidebar/BoxAISidebarContent.tsx +1 -3
- package/src/elements/content-sidebar/__tests__/BoxAISidebar.test.tsx +0 -8
- package/src/elements/content-sidebar/stories/BoxAISidebar.stories.tsx +0 -1
- package/src/elements/content-sidebar/stories/tests/BoxAISidebar-visual.stories.tsx +0 -1
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import userEvent from '@testing-library/user-event';
|
|
3
|
+
import { render, screen } from '../../../test-utils/testing-library';
|
|
4
|
+
import MetadataSidePanel, { type MetadataSidePanelProps } from '../MetadataSidePanel';
|
|
5
|
+
|
|
6
|
+
// Mock scrollTo method
|
|
7
|
+
Object.defineProperty(Element.prototype, 'scrollTo', {
|
|
8
|
+
value: jest.fn(),
|
|
9
|
+
writable: true,
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
const mockCollection = {
|
|
13
|
+
items: [
|
|
14
|
+
{
|
|
15
|
+
id: '1',
|
|
16
|
+
name: 'Test File 1.pdf',
|
|
17
|
+
type: 'file',
|
|
18
|
+
metadata: {
|
|
19
|
+
enterprise_123: {
|
|
20
|
+
mockTemplate: {
|
|
21
|
+
alias: 'mock-alias-1',
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
id: '2',
|
|
28
|
+
name: 'Test File 2.docx',
|
|
29
|
+
type: 'file',
|
|
30
|
+
metadata: {
|
|
31
|
+
enterprise_123: {
|
|
32
|
+
mockTemplate: {
|
|
33
|
+
alias: 'mock-alias-2',
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
nextMarker: null,
|
|
40
|
+
offset: 0,
|
|
41
|
+
totalCount: 2,
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const mockMetadataTemplate = {
|
|
45
|
+
id: 'template-id',
|
|
46
|
+
displayName: 'Mock Template',
|
|
47
|
+
scope: 'enterprise_123',
|
|
48
|
+
templateKey: 'mockTemplate',
|
|
49
|
+
type: 'metadata_template',
|
|
50
|
+
hidden: false,
|
|
51
|
+
fields: [
|
|
52
|
+
{
|
|
53
|
+
id: '123',
|
|
54
|
+
key: 'alias',
|
|
55
|
+
displayName: 'Alias',
|
|
56
|
+
type: 'string',
|
|
57
|
+
hidden: false,
|
|
58
|
+
options: [],
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const mockOnClose = jest.fn();
|
|
64
|
+
|
|
65
|
+
describe('elements/content-explorer/MetadataSidePanel', () => {
|
|
66
|
+
const defaultProps: MetadataSidePanelProps = {
|
|
67
|
+
currentCollection: mockCollection,
|
|
68
|
+
onClose: mockOnClose,
|
|
69
|
+
metadataTemplate: mockMetadataTemplate,
|
|
70
|
+
selectedItemIds: new Set(['1']),
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const renderComponent = (props: Partial<MetadataSidePanelProps> = {}) =>
|
|
74
|
+
render(<MetadataSidePanel {...defaultProps} {...props} />);
|
|
75
|
+
|
|
76
|
+
test('renders the metadata title', () => {
|
|
77
|
+
renderComponent();
|
|
78
|
+
expect(screen.getByText('Metadata')).toBeInTheDocument();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test('renders the close button with proper aria-label', () => {
|
|
82
|
+
renderComponent();
|
|
83
|
+
const closeButton = screen.getByLabelText('Close');
|
|
84
|
+
expect(closeButton).toBeInTheDocument();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test('renders the selected item text', () => {
|
|
88
|
+
renderComponent();
|
|
89
|
+
expect(screen.getByText('Test File 1.pdf')).toBeInTheDocument();
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test('renders metadata instance (view mode) by default', () => {
|
|
93
|
+
renderComponent();
|
|
94
|
+
const editTemplateButton = screen.getByLabelText('Edit Mock Template');
|
|
95
|
+
expect(editTemplateButton).toBeInTheDocument();
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test('renders field value of selected item', () => {
|
|
99
|
+
renderComponent();
|
|
100
|
+
const fieldValue = screen.getByText('mock-alias-1');
|
|
101
|
+
expect(fieldValue).toBeInTheDocument();
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test('call onClose when close button is clicked', async () => {
|
|
105
|
+
renderComponent();
|
|
106
|
+
const closeButton = screen.getByLabelText('Close');
|
|
107
|
+
await userEvent.click(closeButton);
|
|
108
|
+
expect(mockOnClose).toHaveBeenCalledTimes(1);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test('render correct subtitle when multiple items are selected', () => {
|
|
112
|
+
renderComponent({ selectedItemIds: new Set(['1', '2']) });
|
|
113
|
+
const subtitle = screen.getByText('2 files selected');
|
|
114
|
+
expect(subtitle).toBeInTheDocument();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test('render cancel and submit button when in edit mode', async () => {
|
|
118
|
+
renderComponent();
|
|
119
|
+
const editTemplateButton = screen.getByLabelText('Edit Mock Template');
|
|
120
|
+
await userEvent.click(editTemplateButton);
|
|
121
|
+
|
|
122
|
+
const cancelButton = screen.getByRole('button', { name: 'Cancel' });
|
|
123
|
+
expect(cancelButton).toBeInTheDocument();
|
|
124
|
+
const submitButton = screen.getByRole('button', { name: 'Save' });
|
|
125
|
+
expect(submitButton).toBeInTheDocument();
|
|
126
|
+
});
|
|
127
|
+
});
|
|
@@ -21,7 +21,7 @@ const metadataFieldNamePrefix = `metadata.${metadataScopeAndKey}`;
|
|
|
21
21
|
const metadataQuery = {
|
|
22
22
|
from: metadataScopeAndKey,
|
|
23
23
|
ancestor_folder_id: '0',
|
|
24
|
-
|
|
24
|
+
order_by: [
|
|
25
25
|
{
|
|
26
26
|
field_key: `${metadataFieldNamePrefix}.${mockSchema.fields[0].key}`, // Default to sorting by the first field in the schema
|
|
27
27
|
direction: 'asc',
|
|
@@ -49,7 +49,7 @@ const columns = [
|
|
|
49
49
|
textValue: 'Name',
|
|
50
50
|
id: 'name',
|
|
51
51
|
type: 'string',
|
|
52
|
-
|
|
52
|
+
allowsSorting: true,
|
|
53
53
|
minWidth: 150,
|
|
54
54
|
maxWidth: 150,
|
|
55
55
|
},
|
|
@@ -57,7 +57,7 @@ const columns = [
|
|
|
57
57
|
textValue: field.displayName,
|
|
58
58
|
id: `${metadataFieldNamePrefix}.${field.key}`,
|
|
59
59
|
type: field.type,
|
|
60
|
-
|
|
60
|
+
allowsSorting: true,
|
|
61
61
|
minWidth: 150,
|
|
62
62
|
maxWidth: 150,
|
|
63
63
|
})),
|
|
@@ -94,6 +94,20 @@ export const metadataViewV2: Story = {
|
|
|
94
94
|
args: metadataViewV2ElementProps,
|
|
95
95
|
};
|
|
96
96
|
|
|
97
|
+
// @TODO Assert that rows are actually sorted in a different order, once handleSortChange is implemented
|
|
98
|
+
export const metadataViewV2SortsFromHeader: Story = {
|
|
99
|
+
args: metadataViewV2ElementProps,
|
|
100
|
+
play: async ({ canvas }) => {
|
|
101
|
+
await waitFor(() => {
|
|
102
|
+
expect(canvas.getByRole('row', { name: /Industry/i })).toBeInTheDocument();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const firstRow = canvas.getByRole('row', { name: /Industry/i });
|
|
106
|
+
const industryHeader = within(firstRow).getByRole('columnheader', { name: 'Industry' });
|
|
107
|
+
userEvent.click(industryHeader);
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
|
|
97
111
|
export const metadataViewV2WithCustomActions: Story = {
|
|
98
112
|
args: {
|
|
99
113
|
...metadataViewV2ElementProps,
|
|
@@ -164,6 +178,32 @@ export const metadataViewV2WithInitialFilterValues: Story = {
|
|
|
164
178
|
},
|
|
165
179
|
};
|
|
166
180
|
|
|
181
|
+
export const sidePanelOpenWithSingleItemSelected: Story = {
|
|
182
|
+
args: {
|
|
183
|
+
...metadataViewV2ElementProps,
|
|
184
|
+
metadataViewProps: {
|
|
185
|
+
columns,
|
|
186
|
+
tableProps: {
|
|
187
|
+
isSelectAllEnabled: true,
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
|
|
192
|
+
play: async ({ canvas }) => {
|
|
193
|
+
await waitFor(() => {
|
|
194
|
+
expect(canvas.getByRole('row', { name: /Child 2/i })).toBeInTheDocument();
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
// Select the first row by clicking its checkbox
|
|
198
|
+
const firstRow = canvas.getByRole('row', { name: /Child 2/i });
|
|
199
|
+
const checkbox = within(firstRow).getByRole('checkbox');
|
|
200
|
+
await userEvent.click(checkbox);
|
|
201
|
+
|
|
202
|
+
const metadataButton = canvas.getByRole('button', { name: 'Metadata' });
|
|
203
|
+
await userEvent.click(metadataButton);
|
|
204
|
+
},
|
|
205
|
+
};
|
|
206
|
+
|
|
167
207
|
const meta: Meta<typeof ContentExplorer> = {
|
|
168
208
|
title: 'Elements/ContentExplorer/tests/MetadataView/visual',
|
|
169
209
|
component: ContentExplorer,
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
import { useIntl } from 'react-intl';
|
|
3
|
+
|
|
4
|
+
import type { MetadataTemplate } from '@box/metadata-editor';
|
|
5
|
+
import type { Selection } from 'react-aria-components';
|
|
6
|
+
import type { BoxItem, Collection } from '../../common/types/core';
|
|
7
|
+
|
|
8
|
+
import messages from '../common/messages';
|
|
9
|
+
|
|
10
|
+
// Get selected item text
|
|
11
|
+
export function useSelectedItemText(currentCollection: Collection, selectedItemIds: Selection): string {
|
|
12
|
+
const { formatMessage } = useIntl();
|
|
13
|
+
|
|
14
|
+
return useMemo(() => {
|
|
15
|
+
const selectedCount = selectedItemIds === 'all' ? currentCollection.items.length : selectedItemIds.size;
|
|
16
|
+
if (selectedCount === 0) return '';
|
|
17
|
+
|
|
18
|
+
// Case 1: Single selected item - show item name
|
|
19
|
+
if (selectedCount === 1) {
|
|
20
|
+
const selectedKey =
|
|
21
|
+
selectedItemIds === 'all' ? currentCollection.items[0].id : selectedItemIds.values().next().value;
|
|
22
|
+
const selectedItem = currentCollection.items.find(item => item.id === selectedKey);
|
|
23
|
+
return selectedItem?.name ?? '';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Case 2: Multiple selected items - show count
|
|
27
|
+
return formatMessage(messages.numFilesSelected, { numSelected: selectedCount });
|
|
28
|
+
}, [currentCollection.items, formatMessage, selectedItemIds]);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Get template instance based on metadata template and selected items
|
|
32
|
+
export function getTemplateInstance(metadataTemplate: MetadataTemplate, selectedItems: BoxItem[]) {
|
|
33
|
+
const { displayName, fields, hidden, id, scope, templateKey, type } = metadataTemplate;
|
|
34
|
+
|
|
35
|
+
const selectedItemsFields = fields.map(
|
|
36
|
+
({ displayName: fieldDisplayName, hidden: fieldHidden, id: fieldId, key, options, type: fieldType }) => ({
|
|
37
|
+
displayName: fieldDisplayName,
|
|
38
|
+
hidden: fieldHidden,
|
|
39
|
+
id: fieldId,
|
|
40
|
+
key,
|
|
41
|
+
options,
|
|
42
|
+
type: fieldType,
|
|
43
|
+
// TODO: Add support for multiple selected items
|
|
44
|
+
value: selectedItems[0].metadata[scope][templateKey][key],
|
|
45
|
+
}),
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
canEdit: true,
|
|
50
|
+
displayName,
|
|
51
|
+
hidden,
|
|
52
|
+
id,
|
|
53
|
+
fields: selectedItemsFields,
|
|
54
|
+
scope,
|
|
55
|
+
templateKey,
|
|
56
|
+
type,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
@@ -45,7 +45,6 @@ export interface BoxAISidebarProps {
|
|
|
45
45
|
isFeedbackFormEnabled: boolean;
|
|
46
46
|
isIntelligentQueryMode: boolean;
|
|
47
47
|
isMarkdownEnabled: boolean;
|
|
48
|
-
isResetChatEnabled: boolean;
|
|
49
48
|
isStopResponseEnabled?: boolean;
|
|
50
49
|
isStreamingEnabled: boolean;
|
|
51
50
|
items: Array<ItemType>;
|
|
@@ -50,7 +50,6 @@ function BoxAISidebarContent(
|
|
|
50
50
|
hostAppName,
|
|
51
51
|
isAIStudioAgentSelectorEnabled,
|
|
52
52
|
isLoading,
|
|
53
|
-
isResetChatEnabled,
|
|
54
53
|
onSelectAgent,
|
|
55
54
|
questions,
|
|
56
55
|
shouldShowLandingPage,
|
|
@@ -191,7 +190,7 @@ function BoxAISidebarContent(
|
|
|
191
190
|
const renderActions = () => (
|
|
192
191
|
<>
|
|
193
192
|
{renderBoxAISidebarTitle()}
|
|
194
|
-
|
|
193
|
+
<ClearConversationButton onClick={onClearAction} />
|
|
195
194
|
<Tooltip content={formatMessage(messages.sidebarBoxAISwitchToModalView)} variant="standard">
|
|
196
195
|
<IconButton
|
|
197
196
|
aria-label={formatMessage(messages.sidebarBoxAISwitchToModalView)}
|
|
@@ -248,7 +247,6 @@ function BoxAISidebarContent(
|
|
|
248
247
|
isAIStudioAgentSelectorEnabled={isAIStudioAgentSelectorEnabled}
|
|
249
248
|
isFeedbackEnabled={isFeedbackEnabled}
|
|
250
249
|
isFeedbackFormEnabled={isFeedbackFormEnabled}
|
|
251
|
-
isResetChatEnabled={isResetChatEnabled}
|
|
252
250
|
isStopResponseEnabled={isStopResponseEnabled}
|
|
253
251
|
items={items}
|
|
254
252
|
itemSize={itemSize}
|
|
@@ -36,7 +36,6 @@ jest.mock('@box/box-ai-content-answers', () => ({
|
|
|
36
36
|
isMarkdownEnabled={props.isMarkdownEnabled}
|
|
37
37
|
isLoading={props.isLoading}
|
|
38
38
|
isOpen
|
|
39
|
-
isResetChatEnabled={props.isResetChatEnabled}
|
|
40
39
|
isStreamingEnabled={props.isStreamingEnabled}
|
|
41
40
|
itemID={props.itemID}
|
|
42
41
|
itemIDs={props.itemIDs}
|
|
@@ -120,7 +119,6 @@ describe('elements/content-sidebar/BoxAISidebar', () => {
|
|
|
120
119
|
isFeedbackFormEnabled: true,
|
|
121
120
|
isIntelligentQueryMode: true,
|
|
122
121
|
isMarkdownEnabled: true,
|
|
123
|
-
isResetChatEnabled: true,
|
|
124
122
|
isStopResponseEnabled: true,
|
|
125
123
|
isStreamingEnabled: true,
|
|
126
124
|
onFeedbackFormSubmit: jest.fn(),
|
|
@@ -186,12 +184,6 @@ describe('elements/content-sidebar/BoxAISidebar', () => {
|
|
|
186
184
|
expect(screen.getByRole('button', { name: 'Clear conversation' })).toBeInTheDocument();
|
|
187
185
|
});
|
|
188
186
|
|
|
189
|
-
test('should not have accessible "Clear" button if isResetChatEnabled is false', async () => {
|
|
190
|
-
await renderComponent({ isResetChatEnabled: false });
|
|
191
|
-
|
|
192
|
-
expect(screen.queryByRole('button', { name: 'Clear' })).not.toBeInTheDocument();
|
|
193
|
-
});
|
|
194
|
-
|
|
195
187
|
test('should call recordAction on load if provided', async () => {
|
|
196
188
|
const mockRecordAction = jest.fn();
|
|
197
189
|
await renderComponent({ recordAction: mockRecordAction });
|
|
@@ -43,7 +43,6 @@ export default {
|
|
|
43
43
|
isFeedbackEnabled: true,
|
|
44
44
|
isIntelligentQueryMode: false,
|
|
45
45
|
isMarkdownEnabled: true,
|
|
46
|
-
isResetChatEnabled: true,
|
|
47
46
|
isStopResponseEnabled: true,
|
|
48
47
|
isStreamingEnabled: false,
|
|
49
48
|
items: [{ id: '123', name: 'Document (PDF).pdf', type: 'file', fileType: 'pdf', status: 'supported' }],
|
|
@@ -49,7 +49,6 @@ const meta: Meta<typeof ContentSidebar> & { parameters: { msw: { handlers: HttpH
|
|
|
49
49
|
isDebugModeEnabled: true,
|
|
50
50
|
isIntelligentQueryMode: false,
|
|
51
51
|
isMarkdownEnabled: true,
|
|
52
|
-
isResetChatEnabled: true,
|
|
53
52
|
isStopResponseEnabled: true,
|
|
54
53
|
isStreamingEnabled: false,
|
|
55
54
|
items: [{ id: '123', name: 'Document (PDF).pdf', type: 'file', fileType: 'pdf', status: 'supported' }],
|