box-ui-elements 23.5.0-beta.2 → 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 (70) 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.css +1 -1
  5. package/dist/preview.js +1 -1
  6. package/dist/sidebar.css +1 -1
  7. package/dist/sidebar.js +1 -1
  8. package/es/elements/common/sub-header/SubHeader.js +3 -0
  9. package/es/elements/common/sub-header/SubHeader.js.map +1 -1
  10. package/es/elements/common/sub-header/SubHeaderLeftV2.js +3 -23
  11. package/es/elements/common/sub-header/SubHeaderLeftV2.js.map +1 -1
  12. package/es/elements/common/sub-header/SubHeaderRight.js +6 -2
  13. package/es/elements/common/sub-header/SubHeaderRight.js.map +1 -1
  14. package/es/elements/content-explorer/ContentExplorer.js +44 -5
  15. package/es/elements/content-explorer/ContentExplorer.js.map +1 -1
  16. package/es/elements/content-explorer/ContentExplorer.scss +12 -0
  17. package/es/elements/content-explorer/MetadataSidePanel.js +92 -0
  18. package/es/elements/content-explorer/MetadataSidePanel.js.map +1 -0
  19. package/es/elements/content-explorer/MetadataSidePanel.scss +12 -0
  20. package/es/elements/content-explorer/stories/tests/MetadataView-visual.stories.js +30 -0
  21. package/es/elements/content-explorer/stories/tests/MetadataView-visual.stories.js.map +1 -1
  22. package/es/elements/content-explorer/utils.js +67 -0
  23. package/es/elements/content-explorer/utils.js.map +1 -0
  24. package/es/features/metadata-instance-editor/CascadePolicy.js +3 -3
  25. package/es/features/metadata-instance-editor/CascadePolicy.js.flow +2 -3
  26. package/es/features/metadata-instance-editor/CascadePolicy.js.map +1 -1
  27. package/es/features/metadata-instance-editor/Instance.js +0 -2
  28. package/es/features/metadata-instance-editor/Instance.js.flow +0 -5
  29. package/es/features/metadata-instance-editor/Instance.js.map +1 -1
  30. package/es/features/metadata-instance-editor/Instances.js +0 -2
  31. package/es/features/metadata-instance-editor/Instances.js.flow +0 -3
  32. package/es/features/metadata-instance-editor/Instances.js.map +1 -1
  33. package/es/features/metadata-instance-editor/MetadataInstanceEditor.js +0 -2
  34. package/es/features/metadata-instance-editor/MetadataInstanceEditor.js.flow +0 -3
  35. package/es/features/metadata-instance-editor/MetadataInstanceEditor.js.map +1 -1
  36. package/es/features/metadata-instance-editor/stories/tests/CascadePolicy-visual.stories.js +0 -1
  37. package/es/features/metadata-instance-editor/stories/tests/CascadePolicy-visual.stories.js.flow +0 -1
  38. package/es/features/metadata-instance-editor/stories/tests/CascadePolicy-visual.stories.js.map +1 -1
  39. package/es/src/elements/common/sub-header/SubHeader.d.ts +2 -1
  40. package/es/src/elements/common/sub-header/SubHeaderLeftV2.d.ts +1 -1
  41. package/es/src/elements/common/sub-header/SubHeaderRight.d.ts +4 -1
  42. package/es/src/elements/content-explorer/ContentExplorer.d.ts +15 -0
  43. package/es/src/elements/content-explorer/MetadataSidePanel.d.ts +13 -0
  44. package/es/src/elements/content-explorer/__tests__/MetadataSidePanel.test.d.ts +1 -0
  45. package/es/src/elements/content-explorer/stories/tests/MetadataView-visual.stories.d.ts +1 -0
  46. package/es/src/elements/content-explorer/utils.d.ts +22 -0
  47. package/package.json +3 -3
  48. package/src/elements/common/sub-header/SubHeader.tsx +4 -0
  49. package/src/elements/common/sub-header/SubHeaderLeftV2.tsx +3 -22
  50. package/src/elements/common/sub-header/SubHeaderRight.tsx +8 -2
  51. package/src/elements/content-explorer/ContentExplorer.scss +12 -0
  52. package/src/elements/content-explorer/ContentExplorer.tsx +120 -71
  53. package/src/elements/content-explorer/MetadataSidePanel.scss +12 -0
  54. package/src/elements/content-explorer/MetadataSidePanel.tsx +126 -0
  55. package/src/elements/content-explorer/__tests__/ContentExplorer.test.tsx +80 -16
  56. package/src/elements/content-explorer/__tests__/MetadataSidePanel.test.tsx +127 -0
  57. package/src/elements/content-explorer/stories/tests/MetadataView-visual.stories.tsx +26 -0
  58. package/src/elements/content-explorer/utils.ts +58 -0
  59. package/src/features/metadata-instance-editor/CascadePolicy.js +2 -3
  60. package/src/features/metadata-instance-editor/Instance.js +0 -5
  61. package/src/features/metadata-instance-editor/Instances.js +0 -3
  62. package/src/features/metadata-instance-editor/MetadataInstanceEditor.js +0 -3
  63. package/src/features/metadata-instance-editor/__tests__/CascadePolicy.test.js +0 -9
  64. package/src/features/metadata-instance-editor/__tests__/Instance.test.js +0 -28
  65. package/src/features/metadata-instance-editor/__tests__/Instances.test.js +7 -9
  66. package/src/features/metadata-instance-editor/__tests__/MetadataInstanceEditor.test.js +1 -48
  67. package/src/features/metadata-instance-editor/__tests__/__snapshots__/Instance.test.js.snap +0 -1
  68. package/src/features/metadata-instance-editor/__tests__/__snapshots__/Instances.test.js.snap +0 -2
  69. package/src/features/metadata-instance-editor/__tests__/__snapshots__/MetadataInstanceEditor.test.js.snap +0 -1
  70. package/src/features/metadata-instance-editor/stories/tests/CascadePolicy-visual.stories.js +0 -1
@@ -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
  }
@@ -24,6 +24,7 @@ import ThemingStyles from '../common/theming';
24
24
  import API from '../../api';
25
25
  import MetadataQueryAPIHelperV2 from './MetadataQueryAPIHelper';
26
26
  import MetadataQueryAPIHelper from '../../features/metadata-based-view/MetadataQueryAPIHelper';
27
+ import MetadataSidePanel from './MetadataSidePanel';
27
28
  import Footer from './Footer';
28
29
  import PreviewDialog from '../common/preview-dialog/PreviewDialog';
29
30
  import ShareDialog from './ShareDialog';
@@ -169,6 +170,7 @@ type State = {
169
170
  isCreateFolderModalOpen: boolean;
170
171
  isDeleteModalOpen: boolean;
171
172
  isLoading: boolean;
173
+ isMetadataSidePanelOpen: boolean;
172
174
  isPreviewModalOpen: boolean;
173
175
  isRenameModalOpen: boolean;
174
176
  isShareModalOpen: boolean;
@@ -294,6 +296,7 @@ class ContentExplorer extends Component<ContentExplorerProps, State> {
294
296
  isCreateFolderModalOpen: false,
295
297
  isDeleteModalOpen: false,
296
298
  isLoading: false,
299
+ isMetadataSidePanelOpen: false,
297
300
  isPreviewModalOpen: false,
298
301
  isRenameModalOpen: false,
299
302
  isShareModalOpen: false,
@@ -1562,7 +1565,11 @@ class ContentExplorer extends Component<ContentExplorerProps, State> {
1562
1565
  selectedKeys: selectedItemIds,
1563
1566
  onSelectionChange: (ids: Selection) => {
1564
1567
  onSelectionChange?.(ids);
1565
- this.setState({ selectedItemIds: ids });
1568
+ const isSelectionEmpty = ids !== 'all' && ids.size === 0;
1569
+ this.setState({
1570
+ selectedItemIds: ids,
1571
+ ...(isSelectionEmpty && { isMetadataSidePanelOpen: false }),
1572
+ });
1566
1573
  },
1567
1574
  },
1568
1575
  };
@@ -1644,7 +1651,32 @@ class ContentExplorer extends Component<ContentExplorerProps, State> {
1644
1651
  };
1645
1652
 
1646
1653
  clearSelectedItemIds = () => {
1647
- this.setState({ selectedItemIds: new Set() });
1654
+ this.setState({
1655
+ selectedItemIds: new Set(),
1656
+ isMetadataSidePanelOpen: false,
1657
+ });
1658
+ };
1659
+
1660
+ /**
1661
+ * Toggle metadata side panel visibility
1662
+ *
1663
+ * @private
1664
+ * @return {void}
1665
+ */
1666
+ onMetadataSidePanelToggle = () => {
1667
+ this.setState(prevState => ({
1668
+ isMetadataSidePanelOpen: !prevState.isMetadataSidePanelOpen,
1669
+ }));
1670
+ };
1671
+
1672
+ /**
1673
+ * Close metadata side panel
1674
+ *
1675
+ * @private
1676
+ * @return {void}
1677
+ */
1678
+ closeMetadataSidePanel = () => {
1679
+ this.setState({ isMetadataSidePanelOpen: false });
1648
1680
  };
1649
1681
 
1650
1682
  /**
@@ -1706,6 +1738,7 @@ class ContentExplorer extends Component<ContentExplorerProps, State> {
1706
1738
  isCreateFolderModalOpen,
1707
1739
  isDeleteModalOpen,
1708
1740
  isLoading,
1741
+ isMetadataSidePanelOpen,
1709
1742
  isPreviewModalOpen,
1710
1743
  isRenameModalOpen,
1711
1744
  isShareModalOpen,
@@ -1714,6 +1747,7 @@ class ContentExplorer extends Component<ContentExplorerProps, State> {
1714
1747
  metadataTemplate,
1715
1748
  rootName,
1716
1749
  selected,
1750
+ selectedItemIds,
1717
1751
  view,
1718
1752
  }: State = this.state;
1719
1753
 
@@ -1723,6 +1757,7 @@ class ContentExplorer extends Component<ContentExplorerProps, State> {
1723
1757
  const allowUpload: boolean = canUpload && !!can_upload;
1724
1758
  const allowCreate: boolean = canCreateNewFolder && !!can_upload;
1725
1759
  const isDefaultViewMetadata: boolean = defaultView === DEFAULT_VIEW_METADATA;
1760
+ const isMetadataViewV2Feature = isFeatureEnabled(features, 'contentExplorer.metadataViewV2');
1726
1761
  const isErrorView: boolean = view === VIEW_ERROR;
1727
1762
 
1728
1763
  const viewMode = this.getViewMode();
@@ -1741,75 +1776,89 @@ class ContentExplorer extends Component<ContentExplorerProps, State> {
1741
1776
  <div id={this.id} className={styleClassName} ref={measureRef} data-testid="content-explorer">
1742
1777
  <ThemingStyles selector={`#${this.id}`} theme={theme} />
1743
1778
  <div className="be-app-element" onKeyDown={this.onKeyDown} tabIndex={0}>
1744
- {!isDefaultViewMetadata && <Header view={view} logoUrl={logoUrl} onSearch={this.search} />}
1745
-
1746
- <SubHeader
1747
- view={view}
1748
- viewMode={viewMode}
1749
- rootId={rootFolderId}
1750
- isSmall={isSmall}
1751
- rootName={rootName}
1752
- currentCollection={currentCollection}
1753
- canUpload={allowUpload}
1754
- canCreateNewFolder={allowCreate}
1755
- gridColumnCount={gridColumnCount}
1756
- gridMaxColumns={GRID_VIEW_MAX_COLUMNS}
1757
- gridMinColumns={GRID_VIEW_MIN_COLUMNS}
1758
- maxGridColumnCountForWidth={maxGridColumnCount}
1759
- onUpload={this.upload}
1760
- onClearSelectedItemIds={this.clearSelectedItemIds}
1761
- onCreate={this.createFolder}
1762
- onGridViewSliderChange={this.onGridViewSliderChange}
1763
- onItemClick={this.fetchFolder}
1764
- onSortChange={this.sort}
1765
- onViewModeChange={this.changeViewMode}
1766
- portalElement={this.rootElement}
1767
- selectedItemIds={this.state.selectedItemIds}
1768
- title={title}
1769
- />
1770
-
1771
- <Content
1772
- canDelete={canDelete}
1773
- canDownload={canDownload}
1774
- canPreview={canPreview}
1775
- canRename={canRename}
1776
- canShare={canShare}
1777
- currentCollection={currentCollection}
1778
- features={features}
1779
- gridColumnCount={Math.min(gridColumnCount, maxGridColumnCount)}
1780
- isMedium={isMedium}
1781
- isSmall={isSmall}
1782
- isTouch={isTouch}
1783
- itemActions={itemActions}
1784
- fieldsToShow={fieldsToShow}
1785
- metadataTemplate={metadataTemplate}
1786
- metadataViewProps={metadataViewProps}
1787
- onItemClick={this.onItemClick}
1788
- onItemDelete={this.delete}
1789
- onItemDownload={this.download}
1790
- onItemPreview={this.preview}
1791
- onItemRename={this.rename}
1792
- onItemSelect={this.select}
1793
- onItemShare={this.share}
1794
- onMetadataUpdate={this.updateMetadata}
1795
- onSortChange={this.sort}
1796
- portalElement={this.rootElement}
1797
- view={view}
1798
- viewMode={viewMode}
1799
- />
1800
- {!isErrorView && (
1801
- <Footer>
1802
- <Pagination
1803
- hasNextMarker={hasNextMarker}
1804
- hasPrevMarker={hasPreviousMarker}
1805
- isSmall={isSmall}
1806
- offset={offset}
1807
- onOffsetChange={this.paginate}
1808
- pageSize={currentPageSize}
1809
- totalCount={totalCount}
1810
- onMarkerBasedPageChange={this.markerBasedPaginate}
1811
- />
1812
- </Footer>
1779
+ <div className="bce-ContentExplorer-main">
1780
+ {!isDefaultViewMetadata && (
1781
+ <Header view={view} logoUrl={logoUrl} onSearch={this.search} />
1782
+ )}
1783
+
1784
+ <SubHeader
1785
+ view={view}
1786
+ viewMode={viewMode}
1787
+ rootId={rootFolderId}
1788
+ isSmall={isSmall}
1789
+ rootName={rootName}
1790
+ currentCollection={currentCollection}
1791
+ canUpload={allowUpload}
1792
+ canCreateNewFolder={allowCreate}
1793
+ gridColumnCount={gridColumnCount}
1794
+ gridMaxColumns={GRID_VIEW_MAX_COLUMNS}
1795
+ gridMinColumns={GRID_VIEW_MIN_COLUMNS}
1796
+ maxGridColumnCountForWidth={maxGridColumnCount}
1797
+ onUpload={this.upload}
1798
+ onClearSelectedItemIds={this.clearSelectedItemIds}
1799
+ onCreate={this.createFolder}
1800
+ onGridViewSliderChange={this.onGridViewSliderChange}
1801
+ onItemClick={this.fetchFolder}
1802
+ onSortChange={this.sort}
1803
+ onMetadataSidePanelToggle={this.onMetadataSidePanelToggle}
1804
+ onViewModeChange={this.changeViewMode}
1805
+ portalElement={this.rootElement}
1806
+ selectedItemIds={selectedItemIds}
1807
+ title={title}
1808
+ />
1809
+
1810
+ <Content
1811
+ canDelete={canDelete}
1812
+ canDownload={canDownload}
1813
+ canPreview={canPreview}
1814
+ canRename={canRename}
1815
+ canShare={canShare}
1816
+ currentCollection={currentCollection}
1817
+ features={features}
1818
+ gridColumnCount={Math.min(gridColumnCount, maxGridColumnCount)}
1819
+ isMedium={isMedium}
1820
+ isSmall={isSmall}
1821
+ isTouch={isTouch}
1822
+ itemActions={itemActions}
1823
+ fieldsToShow={fieldsToShow}
1824
+ metadataTemplate={metadataTemplate}
1825
+ metadataViewProps={metadataViewProps}
1826
+ onItemClick={this.onItemClick}
1827
+ onItemDelete={this.delete}
1828
+ onItemDownload={this.download}
1829
+ onItemPreview={this.preview}
1830
+ onItemRename={this.rename}
1831
+ onItemSelect={this.select}
1832
+ onItemShare={this.share}
1833
+ onMetadataUpdate={this.updateMetadata}
1834
+ onSortChange={this.sort}
1835
+ portalElement={this.rootElement}
1836
+ view={view}
1837
+ viewMode={viewMode}
1838
+ />
1839
+
1840
+ {!isErrorView && (
1841
+ <Footer>
1842
+ <Pagination
1843
+ hasNextMarker={hasNextMarker}
1844
+ hasPrevMarker={hasPreviousMarker}
1845
+ isSmall={isSmall}
1846
+ offset={offset}
1847
+ onOffsetChange={this.paginate}
1848
+ pageSize={currentPageSize}
1849
+ totalCount={totalCount}
1850
+ onMarkerBasedPageChange={this.markerBasedPaginate}
1851
+ />
1852
+ </Footer>
1853
+ )}
1854
+ </div>
1855
+ {isDefaultViewMetadata && isMetadataViewV2Feature && isMetadataSidePanelOpen && (
1856
+ <MetadataSidePanel
1857
+ currentCollection={currentCollection}
1858
+ onClose={this.closeMetadataSidePanel}
1859
+ metadataTemplate={metadataTemplate}
1860
+ selectedItemIds={selectedItemIds}
1861
+ />
1813
1862
  )}
1814
1863
  </div>
1815
1864
  {allowUpload && !!this.appElement ? (
@@ -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
+ }
@@ -0,0 +1,126 @@
1
+ import React, { useState } from 'react';
2
+ import { useIntl } from 'react-intl';
3
+
4
+ import { IconButton, SidePanel, Text } from '@box/blueprint-web';
5
+ import { XMark } from '@box/blueprint-web-assets/icons/Fill/index';
6
+ import { FileDefault } from '@box/blueprint-web-assets/icons/Line/index';
7
+ import {
8
+ AutofillContextProvider,
9
+ FormValues,
10
+ JSONPatchOperations,
11
+ MetadataInstance,
12
+ MetadataInstanceForm,
13
+ } from '@box/metadata-editor';
14
+
15
+ import type { Selection } from 'react-aria-components';
16
+ import type { Collection } from '../../common/types/core';
17
+ import type { MetadataTemplate } from '../../common/types/metadata';
18
+ import { getTemplateInstance, useSelectedItemText } from './utils';
19
+
20
+ import messages from '../common/messages';
21
+
22
+ import './MetadataSidePanel.scss';
23
+
24
+ export interface MetadataSidePanelProps {
25
+ currentCollection: Collection;
26
+ onClose: () => void;
27
+ metadataTemplate: MetadataTemplate;
28
+ selectedItemIds: Selection;
29
+ }
30
+
31
+ const MetadataSidePanel = ({
32
+ currentCollection,
33
+ onClose,
34
+ selectedItemIds,
35
+ metadataTemplate,
36
+ }: MetadataSidePanelProps) => {
37
+ const { formatMessage } = useIntl();
38
+ const [isEditing, setIsEditing] = useState<boolean>(false);
39
+ const [isUnsavedChangesModalOpen, setIsUnsavedChangesModalOpen] = useState<boolean>(false);
40
+
41
+ const selectedItemText = useSelectedItemText(currentCollection, selectedItemIds);
42
+ const selectedItems =
43
+ selectedItemIds === 'all'
44
+ ? currentCollection.items
45
+ : currentCollection.items.filter(item => selectedItemIds.has(item.id));
46
+ const templateInstance = getTemplateInstance(metadataTemplate, selectedItems);
47
+
48
+ const handleMetadataInstanceEdit = () => {
49
+ setIsEditing(true);
50
+ };
51
+
52
+ const handleMetadataInstanceFormCancel = () => {
53
+ setIsEditing(false);
54
+ };
55
+
56
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
57
+ const handleMetadataInstanceFormChange = (values: FormValues) => {
58
+ // TODO: Implement on form change
59
+ };
60
+
61
+ const handleMetadataInstanceFormDiscardUnsavedChanges = () => {
62
+ setIsUnsavedChangesModalOpen(false);
63
+ setIsEditing(false);
64
+ };
65
+
66
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
67
+ const handleMetadataInstanceFormSubmit = async (values: FormValues, operations: JSONPatchOperations) => {
68
+ // TODO: Implement onSave callback
69
+ };
70
+
71
+ return (
72
+ <SidePanel variant="persistent">
73
+ <SidePanel.Header>
74
+ <div>
75
+ <Text as="span" variant="titleLarge">
76
+ {formatMessage(messages.sidebarMetadataTitle)}
77
+ </Text>
78
+ <div className="bce-MetadataSidePanel-subtitle">
79
+ <FileDefault />
80
+ <Text as="span" color="textOnLightSecondary" variant="subtitle">
81
+ {selectedItemText}
82
+ </Text>
83
+ </div>
84
+ </div>
85
+ <IconButton aria-label={formatMessage(messages.close)} icon={XMark} onClick={onClose} size="large" />
86
+ </SidePanel.Header>
87
+ <SidePanel.ScrollableContainer>
88
+ <div className="bce-MetadataSidePanel-content">
89
+ <AutofillContextProvider fetchSuggestions={null} isAiSuggestionsFeatureEnabled={false}>
90
+ {isEditing ? (
91
+ <MetadataInstanceForm
92
+ areAiSuggestionsAvailable={false}
93
+ isAiSuggestionsFeatureEnabled={false}
94
+ isBetaLanguageEnabled={false}
95
+ isDeleteButtonDisabled={true}
96
+ isDeleteConfirmationModalCheckboxEnabled={false}
97
+ isLargeFile={false}
98
+ isMultilevelTaxonomyFieldEnabled={false}
99
+ isUnsavedChangesModalOpen={isUnsavedChangesModalOpen}
100
+ selectedTemplateInstance={templateInstance}
101
+ onCancel={handleMetadataInstanceFormCancel}
102
+ onChange={handleMetadataInstanceFormChange}
103
+ onDelete={null}
104
+ onDiscardUnsavedChanges={handleMetadataInstanceFormDiscardUnsavedChanges}
105
+ onSubmit={handleMetadataInstanceFormSubmit}
106
+ setIsUnsavedChangesModalOpen={setIsUnsavedChangesModalOpen}
107
+ taxonomyOptionsFetcher={null}
108
+ />
109
+ ) : (
110
+ <MetadataInstance
111
+ areAiSuggestionsAvailable={false}
112
+ isAiSuggestionsFeatureEnabled={false}
113
+ isBetaLanguageEnabled={false}
114
+ onEdit={handleMetadataInstanceEdit}
115
+ templateInstance={templateInstance}
116
+ taxonomyNodeFetcher={null}
117
+ />
118
+ )}
119
+ </AutofillContextProvider>
120
+ </div>
121
+ </SidePanel.ScrollableContainer>
122
+ </SidePanel>
123
+ );
124
+ };
125
+
126
+ export default MetadataSidePanel;
@@ -1,5 +1,7 @@
1
1
  import React from 'react';
2
2
  import userEvent from '@testing-library/user-event';
3
+ import type { MetadataFieldType } from '@box/metadata-view';
4
+
3
5
  import { render, screen, waitFor, within } from '../../../test-utils/testing-library';
4
6
  import { ContentExplorerComponent as ContentExplorer, ContentExplorerProps } from '../ContentExplorer';
5
7
  import { mockRecentItems, mockRootFolder, mockRootFolderSharedLink } from '../../common/__mocks__/mockRootFolder';
@@ -77,7 +79,13 @@ describe('elements/content-explorer/ContentExplorer', () => {
77
79
  const renderComponent = ({ features, ...props }: Partial<ContentExplorerProps> = {}) => {
78
80
  return render(
79
81
  <FeatureProvider features={features}>
80
- <ContentExplorer defaultView="list" rootFolderId="69083462919" token="token" {...props} />
82
+ <ContentExplorer
83
+ defaultView="list"
84
+ features={features}
85
+ rootFolderId="69083462919"
86
+ token="token"
87
+ {...props}
88
+ />
81
89
  </FeatureProvider>,
82
90
  );
83
91
  };
@@ -414,31 +422,87 @@ describe('elements/content-explorer/ContentExplorer', () => {
414
422
  expect(screen.getByText('Technology')).toBeInTheDocument();
415
423
  expect(screen.getByText('November 16, 2023')).toBeInTheDocument();
416
424
  });
425
+
417
426
  describe('Metadata View V2', () => {
418
- test('should render metadata view button', async () => {
419
- renderComponent({
420
- defaultView: 'metadata',
421
- features: {
422
- contentExplorer: {
423
- metadataViewV2: true,
424
- },
427
+ const { scope: templateScope, templateKey } = mockSchema;
428
+ const metadataScopeAndKey = `${templateScope}.${templateKey}`;
429
+ const metadataFieldNamePrefix = `metadata.${metadataScopeAndKey}`;
430
+ const metadataQuery = {
431
+ from: metadataScopeAndKey,
432
+ ancestor_folder_id: '69083462919',
433
+ sort_by: [
434
+ {
435
+ field_key: `${metadataFieldNamePrefix}.${mockSchema.fields[0].key}`, // Default to sorting by the first field in the schema
436
+ direction: 'asc',
437
+ },
438
+ ],
439
+ fields: [
440
+ // Default to returning all fields in the metadata template schema, and name as a standalone (non-metadata) field
441
+ ...mockSchema.fields.map(field => `${metadataFieldNamePrefix}.${field.key}`),
442
+ 'name',
443
+ ],
444
+ };
445
+ const fieldsToShow = [
446
+ { key: `${metadataFieldNamePrefix}.name`, canEdit: false, displayName: 'Alias' },
447
+ { key: `${metadataFieldNamePrefix}.industry`, canEdit: true },
448
+ { key: `${metadataFieldNamePrefix}.last_contacted_at`, canEdit: true },
449
+ { key: `${metadataFieldNamePrefix}.role`, canEdit: true },
450
+ ];
451
+ const columns = [
452
+ {
453
+ // Always include the name column
454
+ textValue: 'Name',
455
+ id: 'name',
456
+ type: 'string' as const,
457
+ allowSorting: true,
458
+ minWidth: 150,
459
+ maxWidth: 150,
460
+ },
461
+ ...mockSchema.fields.map(field => ({
462
+ textValue: field.displayName,
463
+ id: `${metadataFieldNamePrefix}.${field.key}`,
464
+ type: field.type as MetadataFieldType,
465
+ allowSorting: true,
466
+ minWidth: 150,
467
+ maxWidth: 150,
468
+ })),
469
+ ];
470
+ const defaultView = 'metadata';
471
+ const metadataViewV2ElementProps = {
472
+ metadataViewProps: {
473
+ columns,
474
+ metadataTemplate: mockSchema,
475
+ tableProps: {
476
+ isSelectAllEnabled: true,
425
477
  },
478
+ },
479
+ metadataQuery,
480
+ fieldsToShow,
481
+ defaultView,
482
+ features: {
483
+ contentExplorer: {
484
+ metadataViewV2: true,
485
+ },
486
+ },
487
+ };
488
+
489
+ test('should render metadata view button', async () => {
490
+ renderComponent(metadataViewV2ElementProps);
491
+ await waitFor(() => {
492
+ expect(screen.getByTestId('content-explorer')).toBeInTheDocument();
426
493
  });
427
494
 
428
- // two separate promises need to be resolved before the component is ready
429
495
  await waitFor(() => {
430
- expect(screen.getByText('Please wait while the items load...')).toBeInTheDocument();
496
+ expect(screen.queryByRole('button', { name: 'Switch to Grid View' })).toBeInTheDocument();
431
497
  });
432
498
 
433
499
  await waitFor(() => {
434
- expect(screen.getByTestId('content-explorer')).toBeInTheDocument();
500
+ expect(screen.getByRole('row', { name: /Child 2/i })).toBeInTheDocument();
435
501
  });
436
502
 
437
- expect(screen.queryByRole('searchbox', { name: 'Search files and folders' })).not.toBeInTheDocument();
438
- expect(screen.queryByRole('button', { name: 'Preview Test Folder' })).not.toBeInTheDocument();
439
- expect(screen.queryByRole('button', { name: 'Switch to Grid View' })).not.toBeInTheDocument();
440
- expect(screen.queryByRole('button', { name: 'Sort' })).not.toBeInTheDocument();
441
- expect(screen.queryByRole('button', { name: 'Add' })).not.toBeInTheDocument();
503
+ const selectAllCheckbox = screen.getByLabelText('Select all');
504
+ await userEvent.click(selectAllCheckbox);
505
+
442
506
  expect(screen.getByRole('button', { name: 'Metadata' })).toBeInTheDocument();
443
507
  });
444
508
  });