box-ui-elements 23.5.0-beta.3 → 23.5.0-beta.4

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.
Files changed (41) hide show
  1. package/dist/explorer.css +1 -1
  2. package/dist/explorer.js +1 -1
  3. package/dist/picker.js +1 -1
  4. package/dist/preview.js +1 -1
  5. package/dist/sidebar.js +1 -1
  6. package/es/elements/common/sub-header/SubHeader.js +3 -0
  7. package/es/elements/common/sub-header/SubHeader.js.map +1 -1
  8. package/es/elements/common/sub-header/SubHeaderLeftV2.js +3 -23
  9. package/es/elements/common/sub-header/SubHeaderLeftV2.js.map +1 -1
  10. package/es/elements/common/sub-header/SubHeaderRight.js +6 -2
  11. package/es/elements/common/sub-header/SubHeaderRight.js.map +1 -1
  12. package/es/elements/content-explorer/ContentExplorer.js +44 -5
  13. package/es/elements/content-explorer/ContentExplorer.js.map +1 -1
  14. package/es/elements/content-explorer/ContentExplorer.scss +12 -0
  15. package/es/elements/content-explorer/MetadataSidePanel.js +92 -0
  16. package/es/elements/content-explorer/MetadataSidePanel.js.map +1 -0
  17. package/es/elements/content-explorer/MetadataSidePanel.scss +12 -0
  18. package/es/elements/content-explorer/stories/tests/MetadataView-visual.stories.js +30 -0
  19. package/es/elements/content-explorer/stories/tests/MetadataView-visual.stories.js.map +1 -1
  20. package/es/elements/content-explorer/utils.js +67 -0
  21. package/es/elements/content-explorer/utils.js.map +1 -0
  22. package/es/src/elements/common/sub-header/SubHeader.d.ts +2 -1
  23. package/es/src/elements/common/sub-header/SubHeaderLeftV2.d.ts +1 -1
  24. package/es/src/elements/common/sub-header/SubHeaderRight.d.ts +4 -1
  25. package/es/src/elements/content-explorer/ContentExplorer.d.ts +15 -0
  26. package/es/src/elements/content-explorer/MetadataSidePanel.d.ts +13 -0
  27. package/es/src/elements/content-explorer/__tests__/MetadataSidePanel.test.d.ts +1 -0
  28. package/es/src/elements/content-explorer/stories/tests/MetadataView-visual.stories.d.ts +1 -0
  29. package/es/src/elements/content-explorer/utils.d.ts +22 -0
  30. package/package.json +1 -1
  31. package/src/elements/common/sub-header/SubHeader.tsx +4 -0
  32. package/src/elements/common/sub-header/SubHeaderLeftV2.tsx +3 -22
  33. package/src/elements/common/sub-header/SubHeaderRight.tsx +8 -2
  34. package/src/elements/content-explorer/ContentExplorer.scss +12 -0
  35. package/src/elements/content-explorer/ContentExplorer.tsx +120 -71
  36. package/src/elements/content-explorer/MetadataSidePanel.scss +12 -0
  37. package/src/elements/content-explorer/MetadataSidePanel.tsx +126 -0
  38. package/src/elements/content-explorer/__tests__/ContentExplorer.test.tsx +80 -16
  39. package/src/elements/content-explorer/__tests__/MetadataSidePanel.test.tsx +127 -0
  40. package/src/elements/content-explorer/stories/tests/MetadataView-visual.stories.tsx +26 -0
  41. package/src/elements/content-explorer/utils.ts +58 -0
@@ -0,0 +1,92 @@
1
+ import React, { useState } from 'react';
2
+ import { useIntl } from 'react-intl';
3
+ import { IconButton, SidePanel, Text } from '@box/blueprint-web';
4
+ import { XMark } from '@box/blueprint-web-assets/icons/Fill/index';
5
+ import { FileDefault } from '@box/blueprint-web-assets/icons/Line/index';
6
+ import { AutofillContextProvider, MetadataInstance, MetadataInstanceForm } from '@box/metadata-editor';
7
+ import { getTemplateInstance, useSelectedItemText } from './utils';
8
+ import messages from '../common/messages';
9
+ import './MetadataSidePanel.scss';
10
+ const MetadataSidePanel = ({
11
+ currentCollection,
12
+ onClose,
13
+ selectedItemIds,
14
+ metadataTemplate
15
+ }) => {
16
+ const {
17
+ formatMessage
18
+ } = useIntl();
19
+ const [isEditing, setIsEditing] = useState(false);
20
+ const [isUnsavedChangesModalOpen, setIsUnsavedChangesModalOpen] = useState(false);
21
+ const selectedItemText = useSelectedItemText(currentCollection, selectedItemIds);
22
+ const selectedItems = selectedItemIds === 'all' ? currentCollection.items : currentCollection.items.filter(item => selectedItemIds.has(item.id));
23
+ const templateInstance = getTemplateInstance(metadataTemplate, selectedItems);
24
+ const handleMetadataInstanceEdit = () => {
25
+ setIsEditing(true);
26
+ };
27
+ const handleMetadataInstanceFormCancel = () => {
28
+ setIsEditing(false);
29
+ };
30
+
31
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
32
+ const handleMetadataInstanceFormChange = values => {
33
+ // TODO: Implement on form change
34
+ };
35
+ const handleMetadataInstanceFormDiscardUnsavedChanges = () => {
36
+ setIsUnsavedChangesModalOpen(false);
37
+ setIsEditing(false);
38
+ };
39
+
40
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
41
+ const handleMetadataInstanceFormSubmit = async (values, operations) => {
42
+ // TODO: Implement onSave callback
43
+ };
44
+ return /*#__PURE__*/React.createElement(SidePanel, {
45
+ variant: "persistent"
46
+ }, /*#__PURE__*/React.createElement(SidePanel.Header, null, /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(Text, {
47
+ as: "span",
48
+ variant: "titleLarge"
49
+ }, formatMessage(messages.sidebarMetadataTitle)), /*#__PURE__*/React.createElement("div", {
50
+ className: "bce-MetadataSidePanel-subtitle"
51
+ }, /*#__PURE__*/React.createElement(FileDefault, null), /*#__PURE__*/React.createElement(Text, {
52
+ as: "span",
53
+ color: "textOnLightSecondary",
54
+ variant: "subtitle"
55
+ }, selectedItemText))), /*#__PURE__*/React.createElement(IconButton, {
56
+ "aria-label": formatMessage(messages.close),
57
+ icon: XMark,
58
+ onClick: onClose,
59
+ size: "large"
60
+ })), /*#__PURE__*/React.createElement(SidePanel.ScrollableContainer, null, /*#__PURE__*/React.createElement("div", {
61
+ className: "bce-MetadataSidePanel-content"
62
+ }, /*#__PURE__*/React.createElement(AutofillContextProvider, {
63
+ fetchSuggestions: null,
64
+ isAiSuggestionsFeatureEnabled: false
65
+ }, isEditing ? /*#__PURE__*/React.createElement(MetadataInstanceForm, {
66
+ areAiSuggestionsAvailable: false,
67
+ isAiSuggestionsFeatureEnabled: false,
68
+ isBetaLanguageEnabled: false,
69
+ isDeleteButtonDisabled: true,
70
+ isDeleteConfirmationModalCheckboxEnabled: false,
71
+ isLargeFile: false,
72
+ isMultilevelTaxonomyFieldEnabled: false,
73
+ isUnsavedChangesModalOpen: isUnsavedChangesModalOpen,
74
+ selectedTemplateInstance: templateInstance,
75
+ onCancel: handleMetadataInstanceFormCancel,
76
+ onChange: handleMetadataInstanceFormChange,
77
+ onDelete: null,
78
+ onDiscardUnsavedChanges: handleMetadataInstanceFormDiscardUnsavedChanges,
79
+ onSubmit: handleMetadataInstanceFormSubmit,
80
+ setIsUnsavedChangesModalOpen: setIsUnsavedChangesModalOpen,
81
+ taxonomyOptionsFetcher: null
82
+ }) : /*#__PURE__*/React.createElement(MetadataInstance, {
83
+ areAiSuggestionsAvailable: false,
84
+ isAiSuggestionsFeatureEnabled: false,
85
+ isBetaLanguageEnabled: false,
86
+ onEdit: handleMetadataInstanceEdit,
87
+ templateInstance: templateInstance,
88
+ taxonomyNodeFetcher: null
89
+ })))));
90
+ };
91
+ export default MetadataSidePanel;
92
+ //# sourceMappingURL=MetadataSidePanel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MetadataSidePanel.js","names":["React","useState","useIntl","IconButton","SidePanel","Text","XMark","FileDefault","AutofillContextProvider","MetadataInstance","MetadataInstanceForm","getTemplateInstance","useSelectedItemText","messages","MetadataSidePanel","currentCollection","onClose","selectedItemIds","metadataTemplate","formatMessage","isEditing","setIsEditing","isUnsavedChangesModalOpen","setIsUnsavedChangesModalOpen","selectedItemText","selectedItems","items","filter","item","has","id","templateInstance","handleMetadataInstanceEdit","handleMetadataInstanceFormCancel","handleMetadataInstanceFormChange","values","handleMetadataInstanceFormDiscardUnsavedChanges","handleMetadataInstanceFormSubmit","operations","createElement","variant","Header","as","sidebarMetadataTitle","className","color","close","icon","onClick","size","ScrollableContainer","fetchSuggestions","isAiSuggestionsFeatureEnabled","areAiSuggestionsAvailable","isBetaLanguageEnabled","isDeleteButtonDisabled","isDeleteConfirmationModalCheckboxEnabled","isLargeFile","isMultilevelTaxonomyFieldEnabled","selectedTemplateInstance","onCancel","onChange","onDelete","onDiscardUnsavedChanges","onSubmit","taxonomyOptionsFetcher","onEdit","taxonomyNodeFetcher"],"sources":["../../../src/elements/content-explorer/MetadataSidePanel.tsx"],"sourcesContent":["import React, { useState } from 'react';\nimport { useIntl } from 'react-intl';\n\nimport { IconButton, SidePanel, Text } from '@box/blueprint-web';\nimport { XMark } from '@box/blueprint-web-assets/icons/Fill/index';\nimport { FileDefault } from '@box/blueprint-web-assets/icons/Line/index';\nimport {\n AutofillContextProvider,\n FormValues,\n JSONPatchOperations,\n MetadataInstance,\n MetadataInstanceForm,\n} from '@box/metadata-editor';\n\nimport type { Selection } from 'react-aria-components';\nimport type { Collection } from '../../common/types/core';\nimport type { MetadataTemplate } from '../../common/types/metadata';\nimport { getTemplateInstance, useSelectedItemText } from './utils';\n\nimport messages from '../common/messages';\n\nimport './MetadataSidePanel.scss';\n\nexport interface MetadataSidePanelProps {\n currentCollection: Collection;\n onClose: () => void;\n metadataTemplate: MetadataTemplate;\n selectedItemIds: Selection;\n}\n\nconst MetadataSidePanel = ({\n currentCollection,\n onClose,\n selectedItemIds,\n metadataTemplate,\n}: MetadataSidePanelProps) => {\n const { formatMessage } = useIntl();\n const [isEditing, setIsEditing] = useState<boolean>(false);\n const [isUnsavedChangesModalOpen, setIsUnsavedChangesModalOpen] = useState<boolean>(false);\n\n const selectedItemText = useSelectedItemText(currentCollection, selectedItemIds);\n const selectedItems =\n selectedItemIds === 'all'\n ? currentCollection.items\n : currentCollection.items.filter(item => selectedItemIds.has(item.id));\n const templateInstance = getTemplateInstance(metadataTemplate, selectedItems);\n\n const handleMetadataInstanceEdit = () => {\n setIsEditing(true);\n };\n\n const handleMetadataInstanceFormCancel = () => {\n setIsEditing(false);\n };\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const handleMetadataInstanceFormChange = (values: FormValues) => {\n // TODO: Implement on form change\n };\n\n const handleMetadataInstanceFormDiscardUnsavedChanges = () => {\n setIsUnsavedChangesModalOpen(false);\n setIsEditing(false);\n };\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const handleMetadataInstanceFormSubmit = async (values: FormValues, operations: JSONPatchOperations) => {\n // TODO: Implement onSave callback\n };\n\n return (\n <SidePanel variant=\"persistent\">\n <SidePanel.Header>\n <div>\n <Text as=\"span\" variant=\"titleLarge\">\n {formatMessage(messages.sidebarMetadataTitle)}\n </Text>\n <div className=\"bce-MetadataSidePanel-subtitle\">\n <FileDefault />\n <Text as=\"span\" color=\"textOnLightSecondary\" variant=\"subtitle\">\n {selectedItemText}\n </Text>\n </div>\n </div>\n <IconButton aria-label={formatMessage(messages.close)} icon={XMark} onClick={onClose} size=\"large\" />\n </SidePanel.Header>\n <SidePanel.ScrollableContainer>\n <div className=\"bce-MetadataSidePanel-content\">\n <AutofillContextProvider fetchSuggestions={null} isAiSuggestionsFeatureEnabled={false}>\n {isEditing ? (\n <MetadataInstanceForm\n areAiSuggestionsAvailable={false}\n isAiSuggestionsFeatureEnabled={false}\n isBetaLanguageEnabled={false}\n isDeleteButtonDisabled={true}\n isDeleteConfirmationModalCheckboxEnabled={false}\n isLargeFile={false}\n isMultilevelTaxonomyFieldEnabled={false}\n isUnsavedChangesModalOpen={isUnsavedChangesModalOpen}\n selectedTemplateInstance={templateInstance}\n onCancel={handleMetadataInstanceFormCancel}\n onChange={handleMetadataInstanceFormChange}\n onDelete={null}\n onDiscardUnsavedChanges={handleMetadataInstanceFormDiscardUnsavedChanges}\n onSubmit={handleMetadataInstanceFormSubmit}\n setIsUnsavedChangesModalOpen={setIsUnsavedChangesModalOpen}\n taxonomyOptionsFetcher={null}\n />\n ) : (\n <MetadataInstance\n areAiSuggestionsAvailable={false}\n isAiSuggestionsFeatureEnabled={false}\n isBetaLanguageEnabled={false}\n onEdit={handleMetadataInstanceEdit}\n templateInstance={templateInstance}\n taxonomyNodeFetcher={null}\n />\n )}\n </AutofillContextProvider>\n </div>\n </SidePanel.ScrollableContainer>\n </SidePanel>\n );\n};\n\nexport default MetadataSidePanel;\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,12 @@
1
+ .bce-MetadataSidePanel-subtitle {
2
+ display: flex;
3
+ align-items: center;
4
+ }
5
+
6
+ .bce-MetadataSidePanel-content {
7
+ padding: var(--space-4);
8
+
9
+ [data-target-id='TextButton-deleteButton'] {
10
+ visibility: hidden;
11
+ }
12
+ }
@@ -177,6 +177,36 @@ export const metadataViewV2WithInitialFilterValues = {
177
177
  expect(fileTypeChip).toHaveTextContent(/\+2/);
178
178
  }
179
179
  };
180
+ export const sidePanelOpenWithSingleItemSelected = {
181
+ args: _objectSpread(_objectSpread({}, metadataViewV2ElementProps), {}, {
182
+ metadataViewProps: {
183
+ columns,
184
+ tableProps: {
185
+ isSelectAllEnabled: true
186
+ }
187
+ }
188
+ }),
189
+ play: async ({
190
+ canvas
191
+ }) => {
192
+ await waitFor(() => {
193
+ expect(canvas.getByRole('row', {
194
+ name: /Child 2/i
195
+ })).toBeInTheDocument();
196
+ });
197
+
198
+ // Select the first row by clicking its checkbox
199
+ const firstRow = canvas.getByRole('row', {
200
+ name: /Child 2/i
201
+ });
202
+ const checkbox = within(firstRow).getByRole('checkbox');
203
+ await userEvent.click(checkbox);
204
+ const metadataButton = canvas.getByRole('button', {
205
+ name: 'Metadata'
206
+ });
207
+ await userEvent.click(metadataButton);
208
+ }
209
+ };
180
210
  const meta = {
181
211
  title: 'Elements/ContentExplorer/tests/MetadataView/visual',
182
212
  component: ContentExplorer,
@@ -1 +1 @@
1
- {"version":3,"file":"MetadataView-visual.stories.js","names":["http","HttpResponse","expect","userEvent","waitFor","within","Download","SignMeOthers","Sign","noop","ContentExplorer","DEFAULT_HOSTNAME_API","mockMetadata","mockSchema","mockRootFolder","scope","templateScope","templateKey","metadataScopeAndKey","metadataFieldNamePrefix","metadataQuery","from","ancestor_folder_id","sort_by","field_key","fields","key","direction","map","field","fieldsToShow","canEdit","displayName","columns","textValue","id","type","allowSorting","minWidth","maxWidth","defaultView","metadataView","args","metadataViewV2ElementProps","metadataViewProps","features","contentExplorer","metadataViewV2","metadataViewV2WithCustomActions","_objectSpread","tableProps","isSelectAllEnabled","itemActionMenuProps","actions","label","onClick","icon","subMenuTrigger","subMenuActions","play","canvas","getByRole","name","toBeInTheDocument","firstRow","ellipsesButton","click","initialFilterActionBarProps","initialFilterValues","value","metadataViewV2WithInitialFilterValues","actionBarProps","toHaveTextContent","contactRoleChip","fileTypeChip","meta","title","component","global","FEATURE_FLAGS","rootFolderId","FOLDER_ID","token","TOKEN","parameters","msw","handlers","post","json","get"],"sources":["../../../../../src/elements/content-explorer/stories/tests/MetadataView-visual.stories.tsx"],"sourcesContent":["import type { Meta, StoryObj } from '@storybook/react';\nimport { http, HttpResponse } from 'msw';\nimport { expect, userEvent, waitFor, within } from 'storybook/test';\nimport { Download, SignMeOthers } from '@box/blueprint-web-assets/icons/Fill/index';\nimport { Sign } from '@box/blueprint-web-assets/icons/Line';\nimport noop from 'lodash/noop';\n\nimport ContentExplorer from '../../ContentExplorer';\nimport { DEFAULT_HOSTNAME_API } from '../../../../constants';\nimport { mockMetadata, mockSchema } from '../../../common/__mocks__/mockMetadata';\nimport { mockRootFolder } from '../../../common/__mocks__/mockRootFolder';\n\n// The intent behind relying on mockMetadata is to allow a developer to paste in their own metadata template schema for use with live API calls.\nconst { scope: templateScope, templateKey } = mockSchema;\n\nconst metadataScopeAndKey = `${templateScope}.${templateKey}`;\nconst metadataFieldNamePrefix = `metadata.${metadataScopeAndKey}`;\n\n// This is the body of the metadata query API call.\n// https://developer.box.com/guides/metadata/queries/syntax/\nconst metadataQuery = {\n from: metadataScopeAndKey,\n ancestor_folder_id: '0',\n sort_by: [\n {\n field_key: `${metadataFieldNamePrefix}.${mockSchema.fields[0].key}`, // Default to sorting by the first field in the schema\n direction: 'asc',\n },\n ],\n fields: [\n // Default to returning all fields in the metadata template schema, and name as a standalone (non-metadata) field\n ...mockSchema.fields.map(field => `${metadataFieldNamePrefix}.${field.key}`),\n 'name',\n ],\n};\n\n// Used for metadata view v1\nconst fieldsToShow = [\n { key: `${metadataFieldNamePrefix}.name`, canEdit: false, displayName: 'Alias' },\n { key: `${metadataFieldNamePrefix}.industry`, canEdit: true },\n { key: `${metadataFieldNamePrefix}.last_contacted_at`, canEdit: true },\n { key: `${metadataFieldNamePrefix}.role`, canEdit: true },\n];\n\n// Used for metadata view v2\nconst columns = [\n {\n // Always include the name column\n textValue: 'Name',\n id: 'name',\n type: 'string',\n allowSorting: true,\n minWidth: 150,\n maxWidth: 150,\n },\n ...mockSchema.fields.map(field => ({\n textValue: field.displayName,\n id: `${metadataFieldNamePrefix}.${field.key}`,\n type: field.type,\n allowSorting: true,\n minWidth: 150,\n maxWidth: 150,\n })),\n];\n\n// Switches ContentExplorer to use Metadata View over standard, folder-based view.\nconst defaultView = 'metadata';\n\ntype Story = StoryObj<typeof ContentExplorer>;\n\nexport const metadataView: Story = {\n args: {\n metadataQuery,\n fieldsToShow,\n defaultView,\n },\n};\n\nconst metadataViewV2ElementProps = {\n metadataViewProps: {\n columns,\n },\n metadataQuery,\n fieldsToShow,\n defaultView,\n features: {\n contentExplorer: {\n metadataViewV2: true,\n },\n },\n};\n\nexport const metadataViewV2: Story = {\n args: metadataViewV2ElementProps,\n};\n\nexport const metadataViewV2WithCustomActions: Story = {\n args: {\n ...metadataViewV2ElementProps,\n metadataViewProps: {\n columns,\n tableProps: {\n isSelectAllEnabled: true,\n },\n itemActionMenuProps: {\n actions: [\n {\n label: 'Download',\n onClick: noop,\n icon: Download,\n },\n ],\n subMenuTrigger: {\n label: 'Sign',\n icon: Sign,\n },\n subMenuActions: [\n {\n label: 'Request Signature',\n onClick: noop,\n icon: SignMeOthers,\n },\n ],\n },\n },\n },\n play: async ({ canvas }) => {\n await waitFor(() => {\n expect(canvas.getByRole('row', { name: /Child 2/i })).toBeInTheDocument();\n });\n const firstRow = canvas.getByRole('row', { name: /Child 2/i });\n const ellipsesButton = within(firstRow).getByRole('button', { name: 'Action menu' });\n userEvent.click(ellipsesButton);\n },\n};\n\nconst initialFilterActionBarProps = {\n initialFilterValues: {\n 'industry-filter': { value: ['Legal'] },\n 'mimetype-filter': { value: ['boxnoteType', 'documentType', 'threedType'] },\n 'role-filter': { value: ['Developer', 'Business Owner', 'Marketing'] },\n },\n};\n\nexport const metadataViewV2WithInitialFilterValues: Story = {\n args: {\n ...metadataViewV2ElementProps,\n metadataViewProps: {\n columns,\n actionBarProps: initialFilterActionBarProps,\n },\n },\n play: async ({ canvas }) => {\n // Wait for chips to update with initial values\n await waitFor(() => {\n expect(canvas.getByRole('button', { name: /Industry/i })).toHaveTextContent(/\\(1\\)/);\n });\n // Other chips should reflect initialized values\n const contactRoleChip = canvas.getByRole('button', { name: /Contact Role/i });\n expect(contactRoleChip).toHaveTextContent(/\\(3\\)/);\n\n const fileTypeChip = canvas.getByRole('button', { name: /Box Note/i });\n expect(fileTypeChip).toHaveTextContent(/\\+2/);\n },\n};\n\nconst meta: Meta<typeof ContentExplorer> = {\n title: 'Elements/ContentExplorer/tests/MetadataView/visual',\n component: ContentExplorer,\n args: {\n features: global.FEATURE_FLAGS,\n rootFolderId: global.FOLDER_ID,\n token: global.TOKEN,\n },\n parameters: {\n msw: {\n handlers: [\n http.post(`${DEFAULT_HOSTNAME_API}/2.0/metadata_queries/execute_read`, () => {\n return HttpResponse.json(mockMetadata);\n }),\n http.get(`${DEFAULT_HOSTNAME_API}/2.0/metadata_templates/enterprise/templateName/schema`, () => {\n return HttpResponse.json(mockSchema);\n }),\n http.get(`${DEFAULT_HOSTNAME_API}/2.0/folders/:id`, () => {\n return HttpResponse.json(mockRootFolder);\n }),\n ],\n },\n },\n};\n\nexport default meta;\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"MetadataView-visual.stories.js","names":["http","HttpResponse","expect","userEvent","waitFor","within","Download","SignMeOthers","Sign","noop","ContentExplorer","DEFAULT_HOSTNAME_API","mockMetadata","mockSchema","mockRootFolder","scope","templateScope","templateKey","metadataScopeAndKey","metadataFieldNamePrefix","metadataQuery","from","ancestor_folder_id","sort_by","field_key","fields","key","direction","map","field","fieldsToShow","canEdit","displayName","columns","textValue","id","type","allowSorting","minWidth","maxWidth","defaultView","metadataView","args","metadataViewV2ElementProps","metadataViewProps","features","contentExplorer","metadataViewV2","metadataViewV2WithCustomActions","_objectSpread","tableProps","isSelectAllEnabled","itemActionMenuProps","actions","label","onClick","icon","subMenuTrigger","subMenuActions","play","canvas","getByRole","name","toBeInTheDocument","firstRow","ellipsesButton","click","initialFilterActionBarProps","initialFilterValues","value","metadataViewV2WithInitialFilterValues","actionBarProps","toHaveTextContent","contactRoleChip","fileTypeChip","sidePanelOpenWithSingleItemSelected","checkbox","metadataButton","meta","title","component","global","FEATURE_FLAGS","rootFolderId","FOLDER_ID","token","TOKEN","parameters","msw","handlers","post","json","get"],"sources":["../../../../../src/elements/content-explorer/stories/tests/MetadataView-visual.stories.tsx"],"sourcesContent":["import type { Meta, StoryObj } from '@storybook/react';\nimport { http, HttpResponse } from 'msw';\nimport { expect, userEvent, waitFor, within } from 'storybook/test';\nimport { Download, SignMeOthers } from '@box/blueprint-web-assets/icons/Fill/index';\nimport { Sign } from '@box/blueprint-web-assets/icons/Line';\nimport noop from 'lodash/noop';\n\nimport ContentExplorer from '../../ContentExplorer';\nimport { DEFAULT_HOSTNAME_API } from '../../../../constants';\nimport { mockMetadata, mockSchema } from '../../../common/__mocks__/mockMetadata';\nimport { mockRootFolder } from '../../../common/__mocks__/mockRootFolder';\n\n// The intent behind relying on mockMetadata is to allow a developer to paste in their own metadata template schema for use with live API calls.\nconst { scope: templateScope, templateKey } = mockSchema;\n\nconst metadataScopeAndKey = `${templateScope}.${templateKey}`;\nconst metadataFieldNamePrefix = `metadata.${metadataScopeAndKey}`;\n\n// This is the body of the metadata query API call.\n// https://developer.box.com/guides/metadata/queries/syntax/\nconst metadataQuery = {\n from: metadataScopeAndKey,\n ancestor_folder_id: '0',\n sort_by: [\n {\n field_key: `${metadataFieldNamePrefix}.${mockSchema.fields[0].key}`, // Default to sorting by the first field in the schema\n direction: 'asc',\n },\n ],\n fields: [\n // Default to returning all fields in the metadata template schema, and name as a standalone (non-metadata) field\n ...mockSchema.fields.map(field => `${metadataFieldNamePrefix}.${field.key}`),\n 'name',\n ],\n};\n\n// Used for metadata view v1\nconst fieldsToShow = [\n { key: `${metadataFieldNamePrefix}.name`, canEdit: false, displayName: 'Alias' },\n { key: `${metadataFieldNamePrefix}.industry`, canEdit: true },\n { key: `${metadataFieldNamePrefix}.last_contacted_at`, canEdit: true },\n { key: `${metadataFieldNamePrefix}.role`, canEdit: true },\n];\n\n// Used for metadata view v2\nconst columns = [\n {\n // Always include the name column\n textValue: 'Name',\n id: 'name',\n type: 'string',\n allowSorting: true,\n minWidth: 150,\n maxWidth: 150,\n },\n ...mockSchema.fields.map(field => ({\n textValue: field.displayName,\n id: `${metadataFieldNamePrefix}.${field.key}`,\n type: field.type,\n allowSorting: true,\n minWidth: 150,\n maxWidth: 150,\n })),\n];\n\n// Switches ContentExplorer to use Metadata View over standard, folder-based view.\nconst defaultView = 'metadata';\n\ntype Story = StoryObj<typeof ContentExplorer>;\n\nexport const metadataView: Story = {\n args: {\n metadataQuery,\n fieldsToShow,\n defaultView,\n },\n};\n\nconst metadataViewV2ElementProps = {\n metadataViewProps: {\n columns,\n },\n metadataQuery,\n fieldsToShow,\n defaultView,\n features: {\n contentExplorer: {\n metadataViewV2: true,\n },\n },\n};\n\nexport const metadataViewV2: Story = {\n args: metadataViewV2ElementProps,\n};\n\nexport const metadataViewV2WithCustomActions: Story = {\n args: {\n ...metadataViewV2ElementProps,\n metadataViewProps: {\n columns,\n tableProps: {\n isSelectAllEnabled: true,\n },\n itemActionMenuProps: {\n actions: [\n {\n label: 'Download',\n onClick: noop,\n icon: Download,\n },\n ],\n subMenuTrigger: {\n label: 'Sign',\n icon: Sign,\n },\n subMenuActions: [\n {\n label: 'Request Signature',\n onClick: noop,\n icon: SignMeOthers,\n },\n ],\n },\n },\n },\n play: async ({ canvas }) => {\n await waitFor(() => {\n expect(canvas.getByRole('row', { name: /Child 2/i })).toBeInTheDocument();\n });\n const firstRow = canvas.getByRole('row', { name: /Child 2/i });\n const ellipsesButton = within(firstRow).getByRole('button', { name: 'Action menu' });\n userEvent.click(ellipsesButton);\n },\n};\n\nconst initialFilterActionBarProps = {\n initialFilterValues: {\n 'industry-filter': { value: ['Legal'] },\n 'mimetype-filter': { value: ['boxnoteType', 'documentType', 'threedType'] },\n 'role-filter': { value: ['Developer', 'Business Owner', 'Marketing'] },\n },\n};\n\nexport const metadataViewV2WithInitialFilterValues: Story = {\n args: {\n ...metadataViewV2ElementProps,\n metadataViewProps: {\n columns,\n actionBarProps: initialFilterActionBarProps,\n },\n },\n play: async ({ canvas }) => {\n // Wait for chips to update with initial values\n await waitFor(() => {\n expect(canvas.getByRole('button', { name: /Industry/i })).toHaveTextContent(/\\(1\\)/);\n });\n // Other chips should reflect initialized values\n const contactRoleChip = canvas.getByRole('button', { name: /Contact Role/i });\n expect(contactRoleChip).toHaveTextContent(/\\(3\\)/);\n\n const fileTypeChip = canvas.getByRole('button', { name: /Box Note/i });\n expect(fileTypeChip).toHaveTextContent(/\\+2/);\n },\n};\n\nexport const sidePanelOpenWithSingleItemSelected: Story = {\n args: {\n ...metadataViewV2ElementProps,\n metadataViewProps: {\n columns,\n tableProps: {\n isSelectAllEnabled: true,\n },\n },\n },\n\n play: async ({ canvas }) => {\n await waitFor(() => {\n expect(canvas.getByRole('row', { name: /Child 2/i })).toBeInTheDocument();\n });\n\n // Select the first row by clicking its checkbox\n const firstRow = canvas.getByRole('row', { name: /Child 2/i });\n const checkbox = within(firstRow).getByRole('checkbox');\n await userEvent.click(checkbox);\n\n const metadataButton = canvas.getByRole('button', { name: 'Metadata' });\n await userEvent.click(metadataButton);\n },\n};\n\nconst meta: Meta<typeof ContentExplorer> = {\n title: 'Elements/ContentExplorer/tests/MetadataView/visual',\n component: ContentExplorer,\n args: {\n features: global.FEATURE_FLAGS,\n rootFolderId: global.FOLDER_ID,\n token: global.TOKEN,\n },\n parameters: {\n msw: {\n handlers: [\n http.post(`${DEFAULT_HOSTNAME_API}/2.0/metadata_queries/execute_read`, () => {\n return HttpResponse.json(mockMetadata);\n }),\n http.get(`${DEFAULT_HOSTNAME_API}/2.0/metadata_templates/enterprise/templateName/schema`, () => {\n return HttpResponse.json(mockSchema);\n }),\n http.get(`${DEFAULT_HOSTNAME_API}/2.0/folders/:id`, () => {\n return HttpResponse.json(mockRootFolder);\n }),\n ],\n },\n },\n};\n\nexport default meta;\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,67 @@
1
+ import { useMemo } from 'react';
2
+ import { useIntl } from 'react-intl';
3
+ import messages from '../common/messages';
4
+
5
+ // Get selected item text
6
+ export function useSelectedItemText(currentCollection, selectedItemIds) {
7
+ const {
8
+ formatMessage
9
+ } = useIntl();
10
+ return useMemo(() => {
11
+ const selectedCount = selectedItemIds === 'all' ? currentCollection.items.length : selectedItemIds.size;
12
+ if (selectedCount === 0) return '';
13
+
14
+ // Case 1: Single selected item - show item name
15
+ if (selectedCount === 1) {
16
+ const selectedKey = selectedItemIds === 'all' ? currentCollection.items[0].id : selectedItemIds.values().next().value;
17
+ const selectedItem = currentCollection.items.find(item => item.id === selectedKey);
18
+ return selectedItem?.name ?? '';
19
+ }
20
+
21
+ // Case 2: Multiple selected items - show count
22
+ return formatMessage(messages.numFilesSelected, {
23
+ numSelected: selectedCount
24
+ });
25
+ }, [currentCollection.items, formatMessage, selectedItemIds]);
26
+ }
27
+
28
+ // Get template instance based on metadata template and selected items
29
+ export function getTemplateInstance(metadataTemplate, selectedItems) {
30
+ const {
31
+ displayName,
32
+ fields,
33
+ hidden,
34
+ id,
35
+ scope,
36
+ templateKey,
37
+ type
38
+ } = metadataTemplate;
39
+ const selectedItemsFields = fields.map(({
40
+ displayName: fieldDisplayName,
41
+ hidden: fieldHidden,
42
+ id: fieldId,
43
+ key,
44
+ options,
45
+ type: fieldType
46
+ }) => ({
47
+ displayName: fieldDisplayName,
48
+ hidden: fieldHidden,
49
+ id: fieldId,
50
+ key,
51
+ options,
52
+ type: fieldType,
53
+ // TODO: Add support for multiple selected items
54
+ value: selectedItems[0].metadata[scope][templateKey][key]
55
+ }));
56
+ return {
57
+ canEdit: true,
58
+ displayName,
59
+ hidden,
60
+ id,
61
+ fields: selectedItemsFields,
62
+ scope,
63
+ templateKey,
64
+ type
65
+ };
66
+ }
67
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","names":["useMemo","useIntl","messages","useSelectedItemText","currentCollection","selectedItemIds","formatMessage","selectedCount","items","length","size","selectedKey","id","values","next","value","selectedItem","find","item","name","numFilesSelected","numSelected","getTemplateInstance","metadataTemplate","selectedItems","displayName","fields","hidden","scope","templateKey","type","selectedItemsFields","map","fieldDisplayName","fieldHidden","fieldId","key","options","fieldType","metadata","canEdit"],"sources":["../../../src/elements/content-explorer/utils.ts"],"sourcesContent":["import { useMemo } from 'react';\nimport { useIntl } from 'react-intl';\n\nimport type { MetadataTemplate } from '@box/metadata-editor';\nimport type { Selection } from 'react-aria-components';\nimport type { BoxItem, Collection } from '../../common/types/core';\n\nimport messages from '../common/messages';\n\n// Get selected item text\nexport function useSelectedItemText(currentCollection: Collection, selectedItemIds: Selection): string {\n const { formatMessage } = useIntl();\n\n return useMemo(() => {\n const selectedCount = selectedItemIds === 'all' ? currentCollection.items.length : selectedItemIds.size;\n if (selectedCount === 0) return '';\n\n // Case 1: Single selected item - show item name\n if (selectedCount === 1) {\n const selectedKey =\n selectedItemIds === 'all' ? currentCollection.items[0].id : selectedItemIds.values().next().value;\n const selectedItem = currentCollection.items.find(item => item.id === selectedKey);\n return selectedItem?.name ?? '';\n }\n\n // Case 2: Multiple selected items - show count\n return formatMessage(messages.numFilesSelected, { numSelected: selectedCount });\n }, [currentCollection.items, formatMessage, selectedItemIds]);\n}\n\n// Get template instance based on metadata template and selected items\nexport function getTemplateInstance(metadataTemplate: MetadataTemplate, selectedItems: BoxItem[]) {\n const { displayName, fields, hidden, id, scope, templateKey, type } = metadataTemplate;\n\n const selectedItemsFields = fields.map(\n ({ displayName: fieldDisplayName, hidden: fieldHidden, id: fieldId, key, options, type: fieldType }) => ({\n displayName: fieldDisplayName,\n hidden: fieldHidden,\n id: fieldId,\n key,\n options,\n type: fieldType,\n // TODO: Add support for multiple selected items\n value: selectedItems[0].metadata[scope][templateKey][key],\n }),\n );\n\n return {\n canEdit: true,\n displayName,\n hidden,\n id,\n fields: selectedItemsFields,\n scope,\n templateKey,\n type,\n };\n}\n"],"mappings":"AAAA,SAASA,OAAO,QAAQ,OAAO;AAC/B,SAASC,OAAO,QAAQ,YAAY;AAMpC,OAAOC,QAAQ,MAAM,oBAAoB;;AAEzC;AACA,OAAO,SAASC,mBAAmBA,CAACC,iBAA6B,EAAEC,eAA0B,EAAU;EACnG,MAAM;IAAEC;EAAc,CAAC,GAAGL,OAAO,CAAC,CAAC;EAEnC,OAAOD,OAAO,CAAC,MAAM;IACjB,MAAMO,aAAa,GAAGF,eAAe,KAAK,KAAK,GAAGD,iBAAiB,CAACI,KAAK,CAACC,MAAM,GAAGJ,eAAe,CAACK,IAAI;IACvG,IAAIH,aAAa,KAAK,CAAC,EAAE,OAAO,EAAE;;IAElC;IACA,IAAIA,aAAa,KAAK,CAAC,EAAE;MACrB,MAAMI,WAAW,GACbN,eAAe,KAAK,KAAK,GAAGD,iBAAiB,CAACI,KAAK,CAAC,CAAC,CAAC,CAACI,EAAE,GAAGP,eAAe,CAACQ,MAAM,CAAC,CAAC,CAACC,IAAI,CAAC,CAAC,CAACC,KAAK;MACrG,MAAMC,YAAY,GAAGZ,iBAAiB,CAACI,KAAK,CAACS,IAAI,CAACC,IAAI,IAAIA,IAAI,CAACN,EAAE,KAAKD,WAAW,CAAC;MAClF,OAAOK,YAAY,EAAEG,IAAI,IAAI,EAAE;IACnC;;IAEA;IACA,OAAOb,aAAa,CAACJ,QAAQ,CAACkB,gBAAgB,EAAE;MAAEC,WAAW,EAAEd;IAAc,CAAC,CAAC;EACnF,CAAC,EAAE,CAACH,iBAAiB,CAACI,KAAK,EAAEF,aAAa,EAAED,eAAe,CAAC,CAAC;AACjE;;AAEA;AACA,OAAO,SAASiB,mBAAmBA,CAACC,gBAAkC,EAAEC,aAAwB,EAAE;EAC9F,MAAM;IAAEC,WAAW;IAAEC,MAAM;IAAEC,MAAM;IAAEf,EAAE;IAAEgB,KAAK;IAAEC,WAAW;IAAEC;EAAK,CAAC,GAAGP,gBAAgB;EAEtF,MAAMQ,mBAAmB,GAAGL,MAAM,CAACM,GAAG,CAClC,CAAC;IAAEP,WAAW,EAAEQ,gBAAgB;IAAEN,MAAM,EAAEO,WAAW;IAAEtB,EAAE,EAAEuB,OAAO;IAAEC,GAAG;IAAEC,OAAO;IAAEP,IAAI,EAAEQ;EAAU,CAAC,MAAM;IACrGb,WAAW,EAAEQ,gBAAgB;IAC7BN,MAAM,EAAEO,WAAW;IACnBtB,EAAE,EAAEuB,OAAO;IACXC,GAAG;IACHC,OAAO;IACPP,IAAI,EAAEQ,SAAS;IACf;IACAvB,KAAK,EAAES,aAAa,CAAC,CAAC,CAAC,CAACe,QAAQ,CAACX,KAAK,CAAC,CAACC,WAAW,CAAC,CAACO,GAAG;EAC5D,CAAC,CACL,CAAC;EAED,OAAO;IACHI,OAAO,EAAE,IAAI;IACbf,WAAW;IACXE,MAAM;IACNf,EAAE;IACFc,MAAM,EAAEK,mBAAmB;IAC3BH,KAAK;IACLC,WAAW;IACXC;EACJ,CAAC;AACL","ignoreList":[]}
@@ -17,6 +17,7 @@ export interface SubHeaderProps {
17
17
  onGridViewSliderChange?: (newSliderValue: number) => void;
18
18
  onItemClick: (id: string | null, triggerNavigationEvent: boolean | null) => void;
19
19
  onSortChange: (sortBy: string, sortDirection: string) => void;
20
+ onMetadataSidePanelToggle?: () => void;
20
21
  onUpload: () => void;
21
22
  onViewModeChange?: (viewMode: ViewMode) => void;
22
23
  portalElement?: HTMLElement;
@@ -27,5 +28,5 @@ export interface SubHeaderProps {
27
28
  view: View;
28
29
  viewMode?: ViewMode;
29
30
  }
30
- declare const SubHeader: ({ canCreateNewFolder, canUpload, currentCollection, gridColumnCount, gridMaxColumns, gridMinColumns, maxGridColumnCountForWidth, onGridViewSliderChange, isSmall, onClearSelectedItemIds, onCreate, onItemClick, onSortChange, onUpload, onViewModeChange, portalElement, rootId, rootName, selectedItemIds, title, view, viewMode, }: SubHeaderProps) => React.JSX.Element;
31
+ declare const SubHeader: ({ canCreateNewFolder, canUpload, currentCollection, gridColumnCount, gridMaxColumns, gridMinColumns, maxGridColumnCountForWidth, onGridViewSliderChange, isSmall, onClearSelectedItemIds, onCreate, onItemClick, onSortChange, onMetadataSidePanelToggle, onUpload, onViewModeChange, portalElement, rootId, rootName, selectedItemIds, title, view, viewMode, }: SubHeaderProps) => React.JSX.Element;
31
32
  export default SubHeader;
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import * as React from 'react';
2
2
  import type { Selection } from 'react-aria-components';
3
3
  import type { Collection } from '../../../common/types/core';
4
4
  import './SubHeaderLeftV2.scss';
@@ -1,4 +1,5 @@
1
1
  import * as React from 'react';
2
+ import type { Selection } from 'react-aria-components';
2
3
  import type { ViewMode } from '../flowTypes';
3
4
  import type { SortBy, SortDirection, View, Collection } from '../../../common/types/core';
4
5
  import './SubHeaderRight.scss';
@@ -13,11 +14,13 @@ export interface SubHeaderRightProps {
13
14
  onCreate: () => void;
14
15
  onGridViewSliderChange: (newSliderValue: number) => void;
15
16
  onSortChange: (sortBy: SortBy, sortDirection: SortDirection) => void;
17
+ onMetadataSidePanelToggle?: () => void;
16
18
  onUpload: () => void;
17
19
  onViewModeChange?: (viewMode: ViewMode) => void;
18
20
  portalElement?: HTMLElement;
21
+ selectedItemIds?: Selection;
19
22
  view: View;
20
23
  viewMode: ViewMode;
21
24
  }
22
- declare const SubHeaderRight: ({ canCreateNewFolder, canUpload, currentCollection, gridColumnCount, gridMaxColumns, gridMinColumns, maxGridColumnCountForWidth, onCreate, onGridViewSliderChange, onSortChange, onUpload, onViewModeChange, portalElement, view, viewMode, }: SubHeaderRightProps) => React.JSX.Element;
25
+ declare const SubHeaderRight: ({ canCreateNewFolder, canUpload, currentCollection, gridColumnCount, gridMaxColumns, gridMinColumns, maxGridColumnCountForWidth, onCreate, onGridViewSliderChange, onSortChange, onMetadataSidePanelToggle, onUpload, onViewModeChange, portalElement, selectedItemIds, view, viewMode, }: SubHeaderRightProps) => React.JSX.Element;
23
26
  export default SubHeaderRight;
@@ -86,6 +86,7 @@ type State = {
86
86
  isCreateFolderModalOpen: boolean;
87
87
  isDeleteModalOpen: boolean;
88
88
  isLoading: boolean;
89
+ isMetadataSidePanelOpen: boolean;
89
90
  isPreviewModalOpen: boolean;
90
91
  isRenameModalOpen: boolean;
91
92
  isShareModalOpen: boolean;
@@ -546,6 +547,20 @@ declare class ContentExplorer extends Component<ContentExplorerProps, State> {
546
547
  updateMetadata: (item: BoxItem, field: string, oldValue?: MetadataFieldValue | null, newValue?: MetadataFieldValue | null) => void;
547
548
  updateMetadataSuccessCallback: (item: BoxItem, field: string, newValue?: MetadataFieldValue | null) => void;
548
549
  clearSelectedItemIds: () => void;
550
+ /**
551
+ * Toggle metadata side panel visibility
552
+ *
553
+ * @private
554
+ * @return {void}
555
+ */
556
+ onMetadataSidePanelToggle: () => void;
557
+ /**
558
+ * Close metadata side panel
559
+ *
560
+ * @private
561
+ * @return {void}
562
+ */
563
+ closeMetadataSidePanel: () => void;
549
564
  /**
550
565
  * Renders the file picker
551
566
  *
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+ import type { Selection } from 'react-aria-components';
3
+ import type { Collection } from '../../common/types/core';
4
+ import type { MetadataTemplate } from '../../common/types/metadata';
5
+ import './MetadataSidePanel.scss';
6
+ export interface MetadataSidePanelProps {
7
+ currentCollection: Collection;
8
+ onClose: () => void;
9
+ metadataTemplate: MetadataTemplate;
10
+ selectedItemIds: Selection;
11
+ }
12
+ declare const MetadataSidePanel: ({ currentCollection, onClose, selectedItemIds, metadataTemplate, }: MetadataSidePanelProps) => React.JSX.Element;
13
+ export default MetadataSidePanel;
@@ -5,5 +5,6 @@ export declare const metadataView: Story;
5
5
  export declare const metadataViewV2: Story;
6
6
  export declare const metadataViewV2WithCustomActions: Story;
7
7
  export declare const metadataViewV2WithInitialFilterValues: Story;
8
+ export declare const sidePanelOpenWithSingleItemSelected: Story;
8
9
  declare const meta: Meta<typeof ContentExplorer>;
9
10
  export default meta;
@@ -0,0 +1,22 @@
1
+ import type { MetadataTemplate } from '@box/metadata-editor';
2
+ import type { Selection } from 'react-aria-components';
3
+ import type { BoxItem, Collection } from '../../common/types/core';
4
+ export declare function useSelectedItemText(currentCollection: Collection, selectedItemIds: Selection): string;
5
+ export declare function getTemplateInstance(metadataTemplate: MetadataTemplate, selectedItems: BoxItem[]): {
6
+ canEdit: boolean;
7
+ displayName: string;
8
+ hidden: boolean;
9
+ id: string;
10
+ fields: {
11
+ displayName: string;
12
+ hidden: boolean;
13
+ id: string;
14
+ key: string;
15
+ options: import("@box/metadata-editor").MetadataTemplateFieldOption[];
16
+ type: import("@box/metadata-editor").MetadataTemplateFieldType;
17
+ value: any;
18
+ }[];
19
+ scope: string;
20
+ templateKey: string;
21
+ type: string;
22
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "box-ui-elements",
3
- "version": "23.5.0-beta.3",
3
+ "version": "23.5.0-beta.4",
4
4
  "description": "Box UI Elements",
5
5
  "author": "Box (https://www.box.com/)",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -28,6 +28,7 @@ export interface SubHeaderProps {
28
28
  onGridViewSliderChange?: (newSliderValue: number) => void;
29
29
  onItemClick: (id: string | null, triggerNavigationEvent: boolean | null) => void;
30
30
  onSortChange: (sortBy: string, sortDirection: string) => void;
31
+ onMetadataSidePanelToggle?: () => void;
31
32
  onUpload: () => void;
32
33
  onViewModeChange?: (viewMode: ViewMode) => void;
33
34
  portalElement?: HTMLElement;
@@ -53,6 +54,7 @@ const SubHeader = ({
53
54
  onCreate,
54
55
  onItemClick,
55
56
  onSortChange,
57
+ onMetadataSidePanelToggle,
56
58
  onUpload,
57
59
  onViewModeChange,
58
60
  portalElement,
@@ -109,9 +111,11 @@ const SubHeader = ({
109
111
  onCreate={onCreate}
110
112
  onGridViewSliderChange={onGridViewSliderChange}
111
113
  onSortChange={onSortChange}
114
+ onMetadataSidePanelToggle={onMetadataSidePanelToggle}
112
115
  onUpload={onUpload}
113
116
  onViewModeChange={onViewModeChange}
114
117
  portalElement={portalElement}
118
+ selectedItemIds={selectedItemIds}
115
119
  view={view}
116
120
  viewMode={viewMode}
117
121
  />
@@ -1,8 +1,9 @@
1
- import React, { useMemo } from 'react';
1
+ import * as React from 'react';
2
2
  import { useIntl } from 'react-intl';
3
3
  import { XMark } from '@box/blueprint-web-assets/icons/Fill/index';
4
4
  import { IconButton, PageHeader, Text } from '@box/blueprint-web';
5
5
  import type { Selection } from 'react-aria-components';
6
+ import { useSelectedItemText } from '../../content-explorer/utils';
6
7
  import type { Collection } from '../../../common/types/core';
7
8
  import messages from '../messages';
8
9
 
@@ -20,27 +21,7 @@ const SubHeaderLeftV2 = (props: SubHeaderLeftV2Props) => {
20
21
  const { currentCollection, onClearSelectedItemIds, rootName, selectedItemIds, title } = props;
21
22
  const { formatMessage } = useIntl();
22
23
 
23
- // Generate selected item text based on selected keys
24
- const selectedItemText: string = useMemo(() => {
25
- const selectedCount = selectedItemIds === 'all' ? currentCollection.items.length : selectedItemIds.size;
26
-
27
- if (selectedCount === 0) {
28
- return '';
29
- }
30
-
31
- // Case 1: Single selected item - show item name
32
- if (selectedCount === 1) {
33
- const selectedKey =
34
- selectedItemIds === 'all' ? currentCollection.items[0].id : selectedItemIds.values().next().value;
35
- const selectedItem = currentCollection.items.find(item => item.id === selectedKey);
36
- return selectedItem?.name ?? '';
37
- }
38
- // Case 2: Multiple selected items - show count
39
- if (selectedCount > 1) {
40
- return formatMessage(messages.numFilesSelected, { numSelected: selectedCount });
41
- }
42
- return '';
43
- }, [currentCollection.items, formatMessage, selectedItemIds]);
24
+ const selectedItemText = useSelectedItemText(currentCollection, selectedItemIds);
44
25
 
45
26
  // Case 1 and 2: selected item text with X button
46
27
  if (selectedItemText) {
@@ -2,6 +2,7 @@ import * as React from 'react';
2
2
  import { Button } from '@box/blueprint-web';
3
3
  import { Pencil } from '@box/blueprint-web-assets/icons/Fill';
4
4
  import { useIntl } from 'react-intl';
5
+ import type { Selection } from 'react-aria-components';
5
6
  import Sort from './Sort';
6
7
  import Add from './Add';
7
8
  import GridViewSlider from '../../../components/grid-view/GridViewSlider';
@@ -27,9 +28,11 @@ export interface SubHeaderRightProps {
27
28
  onCreate: () => void;
28
29
  onGridViewSliderChange: (newSliderValue: number) => void;
29
30
  onSortChange: (sortBy: SortBy, sortDirection: SortDirection) => void;
31
+ onMetadataSidePanelToggle?: () => void;
30
32
  onUpload: () => void;
31
33
  onViewModeChange?: (viewMode: ViewMode) => void;
32
34
  portalElement?: HTMLElement;
35
+ selectedItemIds?: Selection;
33
36
  view: View;
34
37
  viewMode: ViewMode;
35
38
  }
@@ -45,9 +48,11 @@ const SubHeaderRight = ({
45
48
  onCreate,
46
49
  onGridViewSliderChange,
47
50
  onSortChange,
51
+ onMetadataSidePanelToggle,
48
52
  onUpload,
49
53
  onViewModeChange,
50
54
  portalElement,
55
+ selectedItemIds,
51
56
  view,
52
57
  viewMode,
53
58
  }: SubHeaderRightProps) => {
@@ -60,6 +65,7 @@ const SubHeaderRight = ({
60
65
  const showSort: boolean = isFolder && hasItems;
61
66
  const showAdd: boolean = (!!canUpload || !!canCreateNewFolder) && isFolder;
62
67
  const isMetadataView: boolean = view === VIEW_METADATA;
68
+ const hasSelectedItems: boolean = !!(selectedItemIds && (selectedItemIds === 'all' || selectedItemIds.size > 0));
63
69
  return (
64
70
  <div className="be-sub-header-right">
65
71
  {!isMetadataView && (
@@ -90,8 +96,8 @@ const SubHeaderRight = ({
90
96
  </>
91
97
  )}
92
98
 
93
- {isMetadataView && isMetadataViewV2Feature && (
94
- <Button icon={Pencil} size="large" variant="primary">
99
+ {isMetadataView && isMetadataViewV2Feature && hasSelectedItems && (
100
+ <Button icon={Pencil} size="large" variant="primary" onClick={onMetadataSidePanelToggle}>
95
101
  {formatMessage(messages.metadata)}
96
102
  </Button>
97
103
  )}
@@ -7,5 +7,17 @@
7
7
  .bcpr {
8
8
  z-index: 1; // Prevents overlay issues with list-item when a file is previewed
9
9
  }
10
+
11
+ .be-app-element {
12
+ flex-direction: row;
13
+ gap: var(--space-4);
14
+ }
15
+
16
+ .bce-ContentExplorer-main {
17
+ display: flex;
18
+ flex: 1;
19
+ flex-direction: column;
20
+ min-width: 0;
21
+ }
10
22
  }
11
23
  }