box-ui-elements 24.0.0-beta.4 → 24.0.0-beta.5

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 (98) hide show
  1. package/dist/explorer.js +1 -1
  2. package/dist/openwith.js +1 -1
  3. package/dist/picker.js +1 -1
  4. package/dist/preview.js +1 -1
  5. package/dist/sharing.js +1 -1
  6. package/dist/sidebar.js +1 -1
  7. package/dist/uploader.js +1 -1
  8. package/es/api/Metadata.js +98 -13
  9. package/es/api/Metadata.js.flow +110 -12
  10. package/es/api/Metadata.js.map +1 -1
  11. package/es/elements/common/messages.js +16 -0
  12. package/es/elements/common/messages.js.flow +25 -0
  13. package/es/elements/common/messages.js.map +1 -1
  14. package/es/elements/content-explorer/Content.js +2 -1
  15. package/es/elements/content-explorer/Content.js.map +1 -1
  16. package/es/elements/content-explorer/ContentExplorer.js +19 -5
  17. package/es/elements/content-explorer/ContentExplorer.js.map +1 -1
  18. package/es/elements/content-explorer/MetadataQueryAPIHelper.js +61 -4
  19. package/es/elements/content-explorer/MetadataQueryAPIHelper.js.map +1 -1
  20. package/es/elements/content-explorer/MetadataSidePanel.js +40 -14
  21. package/es/elements/content-explorer/MetadataSidePanel.js.map +1 -1
  22. package/es/elements/content-explorer/MetadataViewContainer.js +55 -4
  23. package/es/elements/content-explorer/MetadataViewContainer.js.map +1 -1
  24. package/es/elements/content-explorer/stories/tests/MetadataView-visual.stories.js +61 -13
  25. package/es/elements/content-explorer/stories/tests/MetadataView-visual.stories.js.map +1 -1
  26. package/es/elements/content-explorer/utils.js +140 -12
  27. package/es/elements/content-explorer/utils.js.map +1 -1
  28. package/es/src/elements/content-explorer/ContentExplorer.d.ts +11 -3
  29. package/es/src/elements/content-explorer/MetadataQueryAPIHelper.d.ts +11 -1
  30. package/es/src/elements/content-explorer/MetadataSidePanel.d.ts +6 -3
  31. package/es/src/elements/content-explorer/MetadataViewContainer.d.ts +3 -1
  32. package/es/src/elements/content-explorer/stories/tests/MetadataView-visual.stories.d.ts +1 -0
  33. package/es/src/elements/content-explorer/utils.d.ts +9 -3
  34. package/i18n/bn-IN.js +4 -0
  35. package/i18n/bn-IN.properties +4 -0
  36. package/i18n/da-DK.js +4 -0
  37. package/i18n/da-DK.properties +4 -0
  38. package/i18n/de-DE.js +5 -1
  39. package/i18n/de-DE.properties +4 -0
  40. package/i18n/en-AU.js +4 -0
  41. package/i18n/en-AU.properties +4 -0
  42. package/i18n/en-CA.js +4 -0
  43. package/i18n/en-CA.properties +4 -0
  44. package/i18n/en-GB.js +4 -0
  45. package/i18n/en-GB.properties +4 -0
  46. package/i18n/en-US.js +4 -0
  47. package/i18n/en-US.properties +8 -0
  48. package/i18n/en-x-pseudo.js +4 -0
  49. package/i18n/es-419.js +5 -1
  50. package/i18n/es-419.properties +4 -0
  51. package/i18n/es-ES.js +5 -1
  52. package/i18n/es-ES.properties +4 -0
  53. package/i18n/fi-FI.js +4 -0
  54. package/i18n/fi-FI.properties +4 -0
  55. package/i18n/fr-CA.js +4 -0
  56. package/i18n/fr-CA.properties +4 -0
  57. package/i18n/fr-FR.js +4 -0
  58. package/i18n/fr-FR.properties +4 -0
  59. package/i18n/hi-IN.js +4 -0
  60. package/i18n/hi-IN.properties +4 -0
  61. package/i18n/it-IT.js +4 -0
  62. package/i18n/it-IT.properties +4 -0
  63. package/i18n/ja-JP.js +6 -2
  64. package/i18n/ja-JP.properties +6 -2
  65. package/i18n/ko-KR.js +4 -0
  66. package/i18n/ko-KR.properties +4 -0
  67. package/i18n/nb-NO.js +4 -0
  68. package/i18n/nb-NO.properties +4 -0
  69. package/i18n/nl-NL.js +4 -0
  70. package/i18n/nl-NL.properties +4 -0
  71. package/i18n/pl-PL.js +4 -0
  72. package/i18n/pl-PL.properties +4 -0
  73. package/i18n/pt-BR.js +4 -0
  74. package/i18n/pt-BR.properties +4 -0
  75. package/i18n/ru-RU.js +5 -1
  76. package/i18n/ru-RU.properties +4 -0
  77. package/i18n/sv-SE.js +4 -0
  78. package/i18n/sv-SE.properties +4 -0
  79. package/i18n/tr-TR.js +5 -1
  80. package/i18n/tr-TR.properties +4 -0
  81. package/i18n/zh-CN.js +4 -0
  82. package/i18n/zh-CN.properties +4 -0
  83. package/i18n/zh-TW.js +4 -0
  84. package/i18n/zh-TW.properties +4 -0
  85. package/package.json +1 -1
  86. package/src/api/Metadata.js +110 -12
  87. package/src/api/__tests__/Metadata.test.js +120 -0
  88. package/src/elements/common/messages.js +25 -0
  89. package/src/elements/content-explorer/Content.tsx +1 -0
  90. package/src/elements/content-explorer/ContentExplorer.tsx +220 -181
  91. package/src/elements/content-explorer/MetadataQueryAPIHelper.ts +89 -4
  92. package/src/elements/content-explorer/MetadataSidePanel.tsx +55 -14
  93. package/src/elements/content-explorer/MetadataViewContainer.tsx +61 -1
  94. package/src/elements/content-explorer/__tests__/ContentExplorer.test.tsx +36 -2
  95. package/src/elements/content-explorer/__tests__/MetadataQueryAPIHelper.test.ts +8 -5
  96. package/src/elements/content-explorer/__tests__/MetadataSidePanel.test.tsx +145 -3
  97. package/src/elements/content-explorer/stories/tests/MetadataView-visual.stories.tsx +54 -8
  98. package/src/elements/content-explorer/utils.ts +150 -13
@@ -8,9 +8,10 @@ import getProp from 'lodash/get';
8
8
  import noop from 'lodash/noop';
9
9
  import throttle from 'lodash/throttle';
10
10
  import uniqueid from 'lodash/uniqueId';
11
- import { TooltipProvider } from '@box/blueprint-web';
11
+ import { Notification, TooltipProvider } from '@box/blueprint-web';
12
12
  import { AxiosRequestConfig, AxiosResponse } from 'axios';
13
- import type { Selection } from 'react-aria-components';
13
+ import type { Key, Selection } from 'react-aria-components';
14
+ import type { MetadataTemplateField } from '@box/metadata-editor';
14
15
 
15
16
  import CreateFolderDialog from '../common/create-folder-dialog';
16
17
  import UploadDialog from '../common/upload-dialog';
@@ -77,6 +78,7 @@ import {
77
78
  import type { ViewMode } from '../common/flowTypes';
78
79
  import type { ItemAction } from '../common/item';
79
80
  import type { Theme } from '../common/theming';
81
+ import type { JSONPatchOperations } from '../../common/types/api';
80
82
  import type { MetadataQuery, FieldsToShow } from '../../common/types/metadataQueries';
81
83
  import type { MetadataFieldValue, MetadataTemplate } from '../../common/types/metadata';
82
84
  import type {
@@ -153,7 +155,7 @@ export interface ContentExplorerProps {
153
155
  rootFolderId?: string;
154
156
  sharedLink?: string;
155
157
  sharedLinkPassword?: string;
156
- sortBy?: SortBy;
158
+ sortBy?: SortBy | Key;
157
159
  sortDirection?: SortDirection;
158
160
  staticHost?: string;
159
161
  staticPath?: string;
@@ -492,6 +494,38 @@ class ContentExplorer extends Component<ContentExplorerProps, State> {
492
494
  );
493
495
  }
494
496
 
497
+ /**
498
+ * Update selected items' metadata instances based on original and new field values in the metadata instance form
499
+ *
500
+ * @private
501
+ * @return {void}
502
+ */
503
+ updateMetadataV2 = async (
504
+ items: BoxItem[],
505
+ operations: JSONPatchOperations,
506
+ templateOldFields: MetadataTemplateField[],
507
+ templateNewFields: MetadataTemplateField[],
508
+ successCallback: () => void,
509
+ errorCallback: ErrorCallback,
510
+ ) => {
511
+ if (items.length === 1) {
512
+ await this.metadataQueryAPIHelper.updateMetadataWithOperations(
513
+ items[0],
514
+ operations,
515
+ successCallback,
516
+ errorCallback,
517
+ );
518
+ } else {
519
+ await this.metadataQueryAPIHelper.bulkUpdateMetadata(
520
+ items,
521
+ templateOldFields,
522
+ templateNewFields,
523
+ successCallback,
524
+ errorCallback,
525
+ );
526
+ }
527
+ };
528
+
495
529
  /**
496
530
  * Resets the collection so that the loading bar starts showing
497
531
  *
@@ -896,7 +930,7 @@ class ContentExplorer extends Component<ContentExplorerProps, State> {
896
930
  * @param {string} sortDirection - sort direction
897
931
  * @return {void}
898
932
  */
899
- sort = (sortBy: SortBy, sortDirection: SortDirection) => {
933
+ sort = (sortBy: SortBy | Key, sortDirection: SortDirection) => {
900
934
  const {
901
935
  currentCollection: { id },
902
936
  view,
@@ -1785,188 +1819,193 @@ class ContentExplorer extends Component<ContentExplorerProps, State> {
1785
1819
  /* eslint-disable jsx-a11y/no-noninteractive-tabindex */
1786
1820
  return (
1787
1821
  <Internationalize language={language} messages={messages}>
1788
- <TooltipProvider container={this.rootElement}>
1789
- <div id={this.id} className={styleClassName} ref={measureRef} data-testid="content-explorer">
1790
- <ThemingStyles selector={`#${this.id}`} theme={theme} />
1791
- <div className="be-app-element" onKeyDown={this.onKeyDown} tabIndex={0}>
1792
- <div className="bce-ContentExplorer-main">
1793
- {!isDefaultViewMetadata && (
1794
- <Header view={view} logoUrl={logoUrl} onSearch={this.search} />
1822
+ <Notification.Provider>
1823
+ <Notification.Viewport />
1824
+ <TooltipProvider container={this.rootElement}>
1825
+ <div id={this.id} className={styleClassName} ref={measureRef} data-testid="content-explorer">
1826
+ <ThemingStyles selector={`#${this.id}`} theme={theme} />
1827
+ <div className="be-app-element" onKeyDown={this.onKeyDown} tabIndex={0}>
1828
+ <div className="bce-ContentExplorer-main">
1829
+ {!isDefaultViewMetadata && (
1830
+ <Header view={view} logoUrl={logoUrl} onSearch={this.search} />
1831
+ )}
1832
+
1833
+ <SubHeader
1834
+ bulkItemActions={bulkItemActions}
1835
+ view={view}
1836
+ viewMode={viewMode}
1837
+ rootId={rootFolderId}
1838
+ isSmall={isSmall}
1839
+ rootName={rootName}
1840
+ currentCollection={currentCollection}
1841
+ canUpload={allowUpload}
1842
+ canCreateNewFolder={allowCreate}
1843
+ gridColumnCount={gridColumnCount}
1844
+ gridMaxColumns={GRID_VIEW_MAX_COLUMNS}
1845
+ gridMinColumns={GRID_VIEW_MIN_COLUMNS}
1846
+ maxGridColumnCountForWidth={maxGridColumnCount}
1847
+ onUpload={this.upload}
1848
+ onClearSelectedItemIds={this.clearSelectedItemIds}
1849
+ onCreate={this.createFolder}
1850
+ onGridViewSliderChange={this.onGridViewSliderChange}
1851
+ onItemClick={this.fetchFolder}
1852
+ onSortChange={this.sort}
1853
+ onMetadataSidePanelToggle={this.onMetadataSidePanelToggle}
1854
+ onViewModeChange={this.changeViewMode}
1855
+ portalElement={this.rootElement}
1856
+ selectedItemIds={selectedItemIds}
1857
+ title={title}
1858
+ />
1859
+
1860
+ <Content
1861
+ canDelete={canDelete}
1862
+ canDownload={canDownload}
1863
+ canPreview={canPreview}
1864
+ canRename={canRename}
1865
+ canShare={canShare}
1866
+ currentCollection={currentCollection}
1867
+ features={features}
1868
+ gridColumnCount={Math.min(gridColumnCount, maxGridColumnCount)}
1869
+ isMedium={isMedium}
1870
+ isSmall={isSmall}
1871
+ isTouch={isTouch}
1872
+ itemActions={itemActions}
1873
+ fieldsToShow={fieldsToShow}
1874
+ metadataTemplate={metadataTemplate}
1875
+ metadataViewProps={metadataViewProps}
1876
+ onItemClick={this.onItemClick}
1877
+ onItemDelete={this.delete}
1878
+ onItemDownload={this.download}
1879
+ onItemPreview={this.preview}
1880
+ onItemRename={this.rename}
1881
+ onItemSelect={this.select}
1882
+ onItemShare={this.share}
1883
+ onMetadataUpdate={this.updateMetadata}
1884
+ onSortChange={this.sort}
1885
+ portalElement={this.rootElement}
1886
+ view={view}
1887
+ viewMode={viewMode}
1888
+ />
1889
+
1890
+ {!isErrorView && (
1891
+ <Footer>
1892
+ <Pagination
1893
+ hasNextMarker={hasNextMarker}
1894
+ hasPrevMarker={hasPreviousMarker}
1895
+ isSmall={isSmall}
1896
+ offset={offset}
1897
+ onOffsetChange={this.paginate}
1898
+ pageSize={currentPageSize}
1899
+ totalCount={totalCount}
1900
+ onMarkerBasedPageChange={this.markerBasedPaginate}
1901
+ />
1902
+ </Footer>
1903
+ )}
1904
+ </div>
1905
+ {isDefaultViewMetadata && isMetadataViewV2Feature && isMetadataSidePanelOpen && (
1906
+ <MetadataSidePanel
1907
+ currentCollection={currentCollection}
1908
+ metadataTemplate={metadataTemplate}
1909
+ onClose={this.closeMetadataSidePanel}
1910
+ onUpdate={this.updateMetadataV2}
1911
+ refreshCollection={this.refreshCollection}
1912
+ selectedItemIds={selectedItemIds}
1913
+ />
1795
1914
  )}
1796
-
1797
- <SubHeader
1798
- bulkItemActions={bulkItemActions}
1799
- view={view}
1800
- viewMode={viewMode}
1801
- rootId={rootFolderId}
1802
- isSmall={isSmall}
1803
- rootName={rootName}
1804
- currentCollection={currentCollection}
1805
- canUpload={allowUpload}
1806
- canCreateNewFolder={allowCreate}
1807
- gridColumnCount={gridColumnCount}
1808
- gridMaxColumns={GRID_VIEW_MAX_COLUMNS}
1809
- gridMinColumns={GRID_VIEW_MIN_COLUMNS}
1810
- maxGridColumnCountForWidth={maxGridColumnCount}
1811
- onUpload={this.upload}
1812
- onClearSelectedItemIds={this.clearSelectedItemIds}
1813
- onCreate={this.createFolder}
1814
- onGridViewSliderChange={this.onGridViewSliderChange}
1815
- onItemClick={this.fetchFolder}
1816
- onSortChange={this.sort}
1817
- onMetadataSidePanelToggle={this.onMetadataSidePanelToggle}
1818
- onViewModeChange={this.changeViewMode}
1819
- portalElement={this.rootElement}
1820
- selectedItemIds={selectedItemIds}
1821
- title={title}
1915
+ </div>
1916
+ {allowUpload && !!this.appElement ? (
1917
+ <UploadDialog
1918
+ isOpen={isUploadModalOpen}
1919
+ currentFolderId={id}
1920
+ token={token}
1921
+ sharedLink={sharedLink}
1922
+ sharedLinkPassword={sharedLinkPassword}
1923
+ apiHost={apiHost}
1924
+ uploadHost={uploadHost}
1925
+ onClose={this.uploadSuccessHandler}
1926
+ parentElement={this.rootElement}
1927
+ appElement={this.appElement}
1928
+ onUpload={onUpload}
1929
+ contentUploaderProps={contentUploaderProps}
1930
+ requestInterceptor={requestInterceptor}
1931
+ responseInterceptor={responseInterceptor}
1822
1932
  />
1823
-
1824
- <Content
1825
- canDelete={canDelete}
1826
- canDownload={canDownload}
1827
- canPreview={canPreview}
1828
- canRename={canRename}
1829
- canShare={canShare}
1830
- currentCollection={currentCollection}
1831
- features={features}
1832
- gridColumnCount={Math.min(gridColumnCount, maxGridColumnCount)}
1833
- isMedium={isMedium}
1834
- isSmall={isSmall}
1835
- isTouch={isTouch}
1836
- itemActions={itemActions}
1837
- fieldsToShow={fieldsToShow}
1838
- metadataTemplate={metadataTemplate}
1839
- metadataViewProps={metadataViewProps}
1840
- onItemClick={this.onItemClick}
1841
- onItemDelete={this.delete}
1842
- onItemDownload={this.download}
1843
- onItemPreview={this.preview}
1844
- onItemRename={this.rename}
1845
- onItemSelect={this.select}
1846
- onItemShare={this.share}
1847
- onMetadataUpdate={this.updateMetadata}
1848
- onSortChange={this.sort}
1849
- portalElement={this.rootElement}
1850
- view={view}
1851
- viewMode={viewMode}
1933
+ ) : null}
1934
+ {allowCreate && !!this.appElement ? (
1935
+ <CreateFolderDialog
1936
+ isOpen={isCreateFolderModalOpen}
1937
+ onCreate={this.throttledCreateFolderCallback}
1938
+ onCancel={this.closeModals}
1939
+ isLoading={isLoading}
1940
+ errorCode={errorCode}
1941
+ parentElement={this.rootElement}
1942
+ appElement={this.appElement}
1852
1943
  />
1853
-
1854
- {!isErrorView && (
1855
- <Footer>
1856
- <Pagination
1857
- hasNextMarker={hasNextMarker}
1858
- hasPrevMarker={hasPreviousMarker}
1859
- isSmall={isSmall}
1860
- offset={offset}
1861
- onOffsetChange={this.paginate}
1862
- pageSize={currentPageSize}
1863
- totalCount={totalCount}
1864
- onMarkerBasedPageChange={this.markerBasedPaginate}
1865
- />
1866
- </Footer>
1867
- )}
1868
- </div>
1869
- {isDefaultViewMetadata && isMetadataViewV2Feature && isMetadataSidePanelOpen && (
1870
- <MetadataSidePanel
1871
- currentCollection={currentCollection}
1872
- onClose={this.closeMetadataSidePanel}
1873
- metadataTemplate={metadataTemplate}
1874
- selectedItemIds={selectedItemIds}
1944
+ ) : null}
1945
+ {canDelete && selected && !!this.appElement ? (
1946
+ <DeleteConfirmationDialog
1947
+ isOpen={isDeleteModalOpen}
1948
+ onDelete={this.deleteCallback}
1949
+ onCancel={this.closeModals}
1950
+ item={selected}
1951
+ isLoading={isLoading}
1952
+ parentElement={this.rootElement}
1953
+ appElement={this.appElement}
1954
+ />
1955
+ ) : null}
1956
+ {canRename && selected && !!this.appElement ? (
1957
+ <RenameDialog
1958
+ isOpen={isRenameModalOpen}
1959
+ onRename={this.renameCallback}
1960
+ onCancel={this.closeModals}
1961
+ item={selected}
1962
+ isLoading={isLoading}
1963
+ errorCode={errorCode}
1964
+ parentElement={this.rootElement}
1965
+ appElement={this.appElement}
1966
+ />
1967
+ ) : null}
1968
+ {canShare && selected && !!this.appElement ? (
1969
+ <ShareDialog
1970
+ isOpen={isShareModalOpen}
1971
+ canSetShareAccess={canSetShareAccess}
1972
+ onShareAccessChange={this.changeShareAccess}
1973
+ onCancel={this.refreshCollection}
1974
+ item={selected}
1975
+ isLoading={isLoading}
1976
+ parentElement={this.rootElement}
1977
+ appElement={this.appElement}
1978
+ />
1979
+ ) : null}
1980
+ {canPreview && selected && !!this.appElement ? (
1981
+ <PreviewDialog
1982
+ isOpen={isPreviewModalOpen}
1983
+ isTouch={isTouch}
1984
+ onCancel={this.closeModals}
1985
+ item={selected}
1986
+ currentCollection={cloneDeep(currentCollection)}
1987
+ token={token}
1988
+ parentElement={this.rootElement}
1989
+ appElement={this.appElement}
1990
+ onPreview={onPreview}
1991
+ onDownload={onDownload}
1992
+ canDownload={canDownload}
1993
+ cache={this.api.getCache()}
1994
+ apiHost={apiHost}
1995
+ appHost={appHost}
1996
+ staticHost={staticHost}
1997
+ staticPath={staticPath}
1998
+ previewLibraryVersion={previewLibraryVersion}
1999
+ sharedLink={sharedLink}
2000
+ sharedLinkPassword={sharedLinkPassword}
2001
+ contentPreviewProps={contentPreviewProps}
2002
+ requestInterceptor={requestInterceptor}
2003
+ responseInterceptor={responseInterceptor}
1875
2004
  />
1876
- )}
2005
+ ) : null}
1877
2006
  </div>
1878
- {allowUpload && !!this.appElement ? (
1879
- <UploadDialog
1880
- isOpen={isUploadModalOpen}
1881
- currentFolderId={id}
1882
- token={token}
1883
- sharedLink={sharedLink}
1884
- sharedLinkPassword={sharedLinkPassword}
1885
- apiHost={apiHost}
1886
- uploadHost={uploadHost}
1887
- onClose={this.uploadSuccessHandler}
1888
- parentElement={this.rootElement}
1889
- appElement={this.appElement}
1890
- onUpload={onUpload}
1891
- contentUploaderProps={contentUploaderProps}
1892
- requestInterceptor={requestInterceptor}
1893
- responseInterceptor={responseInterceptor}
1894
- />
1895
- ) : null}
1896
- {allowCreate && !!this.appElement ? (
1897
- <CreateFolderDialog
1898
- isOpen={isCreateFolderModalOpen}
1899
- onCreate={this.throttledCreateFolderCallback}
1900
- onCancel={this.closeModals}
1901
- isLoading={isLoading}
1902
- errorCode={errorCode}
1903
- parentElement={this.rootElement}
1904
- appElement={this.appElement}
1905
- />
1906
- ) : null}
1907
- {canDelete && selected && !!this.appElement ? (
1908
- <DeleteConfirmationDialog
1909
- isOpen={isDeleteModalOpen}
1910
- onDelete={this.deleteCallback}
1911
- onCancel={this.closeModals}
1912
- item={selected}
1913
- isLoading={isLoading}
1914
- parentElement={this.rootElement}
1915
- appElement={this.appElement}
1916
- />
1917
- ) : null}
1918
- {canRename && selected && !!this.appElement ? (
1919
- <RenameDialog
1920
- isOpen={isRenameModalOpen}
1921
- onRename={this.renameCallback}
1922
- onCancel={this.closeModals}
1923
- item={selected}
1924
- isLoading={isLoading}
1925
- errorCode={errorCode}
1926
- parentElement={this.rootElement}
1927
- appElement={this.appElement}
1928
- />
1929
- ) : null}
1930
- {canShare && selected && !!this.appElement ? (
1931
- <ShareDialog
1932
- isOpen={isShareModalOpen}
1933
- canSetShareAccess={canSetShareAccess}
1934
- onShareAccessChange={this.changeShareAccess}
1935
- onCancel={this.refreshCollection}
1936
- item={selected}
1937
- isLoading={isLoading}
1938
- parentElement={this.rootElement}
1939
- appElement={this.appElement}
1940
- />
1941
- ) : null}
1942
- {canPreview && selected && !!this.appElement ? (
1943
- <PreviewDialog
1944
- isOpen={isPreviewModalOpen}
1945
- isTouch={isTouch}
1946
- onCancel={this.closeModals}
1947
- item={selected}
1948
- currentCollection={cloneDeep(currentCollection)}
1949
- token={token}
1950
- parentElement={this.rootElement}
1951
- appElement={this.appElement}
1952
- onPreview={onPreview}
1953
- onDownload={onDownload}
1954
- canDownload={canDownload}
1955
- cache={this.api.getCache()}
1956
- apiHost={apiHost}
1957
- appHost={appHost}
1958
- staticHost={staticHost}
1959
- staticPath={staticPath}
1960
- previewLibraryVersion={previewLibraryVersion}
1961
- sharedLink={sharedLink}
1962
- sharedLinkPassword={sharedLinkPassword}
1963
- contentPreviewProps={contentPreviewProps}
1964
- requestInterceptor={requestInterceptor}
1965
- responseInterceptor={responseInterceptor}
1966
- />
1967
- ) : null}
1968
- </div>
1969
- </TooltipProvider>
2007
+ </TooltipProvider>
2008
+ </Notification.Provider>
1970
2009
  </Internationalize>
1971
2010
  );
1972
2011
  /* eslint-enable jsx-a11y/no-static-element-interactions */
@@ -3,8 +3,10 @@ import find from 'lodash/find';
3
3
  import getProp from 'lodash/get';
4
4
  import includes from 'lodash/includes';
5
5
  import isArray from 'lodash/isArray';
6
- import isNil from 'lodash/isNil';
6
+ import type { MetadataTemplateField } from '@box/metadata-editor';
7
+ import type { MetadataFieldType } from '@box/metadata-view';
7
8
  import API from '../../api';
9
+ import { areFieldValuesEqual, isEmptyValue, isMultiValuesField } from './utils';
8
10
 
9
11
  import {
10
12
  JSON_PATCH_OP_ADD,
@@ -14,7 +16,7 @@ import {
14
16
  METADATA_FIELD_TYPE_ENUM,
15
17
  METADATA_FIELD_TYPE_MULTISELECT,
16
18
  } from '../../common/constants';
17
- import { FIELD_NAME, FIELD_METADATA, FIELD_EXTENSION } from '../../constants';
19
+ import { FIELD_NAME, FIELD_METADATA, FIELD_EXTENSION, FIELD_PERMISSIONS } from '../../constants';
18
20
 
19
21
  import type { MetadataQuery as MetadataQueryType, MetadataQueryResponseData } from '../../common/types/metadataQueries';
20
22
  import type {
@@ -57,13 +59,18 @@ export default class MetadataQueryAPIHelper {
57
59
  oldValue: MetadataFieldValue | null,
58
60
  newValue: MetadataFieldValue | null,
59
61
  ): JSONPatchOperations => {
62
+ // check if two values are the same, return empty operations if so
63
+ if (areFieldValuesEqual(oldValue, newValue)) {
64
+ return [];
65
+ }
66
+
60
67
  let operation = JSON_PATCH_OP_REPLACE;
61
68
 
62
- if (isNil(oldValue) && newValue) {
69
+ if (isEmptyValue(oldValue) && !isEmptyValue(newValue)) {
63
70
  operation = JSON_PATCH_OP_ADD;
64
71
  }
65
72
 
66
- if (oldValue && isNil(newValue)) {
73
+ if (!isEmptyValue(oldValue) && isEmptyValue(newValue)) {
67
74
  operation = JSON_PATCH_OP_REMOVE;
68
75
  }
69
76
 
@@ -170,6 +177,51 @@ export default class MetadataQueryAPIHelper {
170
177
  return this.api.getMetadataAPI(true).getSchemaByTemplateKey(this.templateKey);
171
178
  };
172
179
 
180
+ /**
181
+ * Generate operations for all fields update in the metadata sidepanel
182
+ *
183
+ * @private
184
+ * @return {JSONPatchOperations}
185
+ */
186
+ generateOperations = (
187
+ item: BoxItem,
188
+ templateOldFields: MetadataTemplateField[],
189
+ templateNewFields: MetadataTemplateField[],
190
+ ): JSONPatchOperations => {
191
+ const { scope, templateKey } = this.metadataTemplate;
192
+ const itemFields = item.metadata[scope][templateKey];
193
+ const operations = templateNewFields.flatMap(newField => {
194
+ let newFieldValue = newField.value;
195
+ const { key, type } = newField;
196
+ // when retrieve value from float type field, it gives a string instead
197
+ if (type === 'float' && newFieldValue !== '') {
198
+ newFieldValue = Number(newFieldValue);
199
+ }
200
+ const oldField = templateOldFields.find(f => f.key === key);
201
+ const oldFieldValue = oldField.value;
202
+
203
+ /*
204
+ Generate operations array based on all the fields' orignal value and the incoming updated value.
205
+
206
+ Edge Case:
207
+ If there are multiple items shared different value for enum or multi-select field, the form will
208
+ return 'Multiple values' as the value. In this case, it needs to generate operation based on the
209
+ actual item's field value.
210
+ */
211
+ const shouldUseItemFieldValue =
212
+ isMultiValuesField(type as MetadataFieldType, oldFieldValue) &&
213
+ !isMultiValuesField(type as MetadataFieldType, newFieldValue);
214
+
215
+ return this.createJSONPatchOperations(
216
+ key,
217
+ shouldUseItemFieldValue ? itemFields[key] : oldFieldValue,
218
+ newFieldValue,
219
+ );
220
+ });
221
+
222
+ return operations;
223
+ };
224
+
173
225
  queryMetadata = (): Promise<MetadataQueryResponseData> => {
174
226
  return new Promise((resolve, reject) => {
175
227
  this.api.getMetadataQueryAPI().queryMetadata(this.metadataQuery, resolve, reject, { forceFetch: true });
@@ -205,6 +257,34 @@ export default class MetadataQueryAPIHelper {
205
257
  .updateMetadata(file, this.metadataTemplate, operations, successCallback, errorCallback);
206
258
  };
207
259
 
260
+ updateMetadataWithOperations = (
261
+ item: BoxItem,
262
+ operations: JSONPatchOperations,
263
+ successCallback: () => void,
264
+ errorCallback: ErrorCallback,
265
+ ): Promise<void> => {
266
+ return this.api
267
+ .getMetadataAPI(true)
268
+ .updateMetadata(item, this.metadataTemplate, operations, successCallback, errorCallback);
269
+ };
270
+
271
+ bulkUpdateMetadata = (
272
+ items: BoxItem[],
273
+ templateOldFields: MetadataTemplateField[],
274
+ templateNewFields: MetadataTemplateField[],
275
+ successCallback: () => void,
276
+ errorCallback: ErrorCallback,
277
+ ): Promise<void> => {
278
+ const operations: JSONPatchOperations = [];
279
+ items.forEach(item => {
280
+ const operation = this.generateOperations(item, templateOldFields, templateNewFields);
281
+ operations.push(operation);
282
+ });
283
+ return this.api
284
+ .getMetadataAPI(true)
285
+ .bulkUpdateMetadata(items, this.metadataTemplate, operations, successCallback, errorCallback);
286
+ };
287
+
208
288
  /**
209
289
  * Verify that the metadata query has required fields and update it if necessary
210
290
  * For a file item, default fields included in the response are "type", "id", "etag"
@@ -225,6 +305,11 @@ export default class MetadataQueryAPIHelper {
225
305
  clonedFields.push(FIELD_EXTENSION);
226
306
  }
227
307
 
308
+ // This field is necessary to check if the user has permission to update metadata
309
+ if (!clonedFields.includes(FIELD_PERMISSIONS)) {
310
+ clonedFields.push(FIELD_PERMISSIONS);
311
+ }
312
+
228
313
  clonedQuery.fields = clonedFields;
229
314
 
230
315
  return clonedQuery;