box-ui-elements 23.4.0 → 23.5.0-beta.2

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 (43) hide show
  1. package/dist/explorer.css +1 -1
  2. package/dist/explorer.js +1 -1
  3. package/dist/preview.js +1 -1
  4. package/dist/sidebar.js +1 -1
  5. package/es/elements/common/routing/index.js +1 -1
  6. package/es/elements/common/routing/index.js.flow +2 -1
  7. package/es/elements/common/routing/index.js.map +1 -1
  8. package/es/elements/common/routing/withRouterIfEnabled.js +15 -0
  9. package/es/elements/common/routing/withRouterIfEnabled.js.flow +24 -0
  10. package/es/elements/common/routing/withRouterIfEnabled.js.map +1 -0
  11. package/es/elements/content-explorer/ContentExplorer.js +23 -25
  12. package/es/elements/content-explorer/ContentExplorer.js.map +1 -1
  13. package/es/elements/content-explorer/MetadataViewContainer.js +53 -3
  14. package/es/elements/content-explorer/MetadataViewContainer.js.map +1 -1
  15. package/es/elements/content-explorer/stories/MetadataView.stories.js +3 -0
  16. package/es/elements/content-explorer/stories/MetadataView.stories.js.map +1 -1
  17. package/es/elements/content-explorer/stories/tests/MetadataView-visual.stories.js +44 -1
  18. package/es/elements/content-explorer/stories/tests/MetadataView-visual.stories.js.map +1 -1
  19. package/es/elements/content-sidebar/AddTaskButton.js +3 -3
  20. package/es/elements/content-sidebar/AddTaskButton.js.flow +5 -4
  21. package/es/elements/content-sidebar/AddTaskButton.js.map +1 -1
  22. package/es/elements/content-sidebar/SidebarToggle.js +2 -2
  23. package/es/elements/content-sidebar/SidebarToggle.js.flow +3 -2
  24. package/es/elements/content-sidebar/SidebarToggle.js.map +1 -1
  25. package/es/elements/content-sidebar/versions/VersionsSidebarContainer.js +4 -3
  26. package/es/elements/content-sidebar/versions/VersionsSidebarContainer.js.flow +5 -4
  27. package/es/elements/content-sidebar/versions/VersionsSidebarContainer.js.map +1 -1
  28. package/es/src/elements/content-explorer/ContentExplorer.d.ts +0 -7
  29. package/es/src/elements/content-explorer/MetadataViewContainer.d.ts +13 -2
  30. package/es/src/elements/content-explorer/stories/tests/MetadataView-visual.stories.d.ts +1 -0
  31. package/package.json +9 -9
  32. package/src/elements/common/routing/__tests__/withRouterIfEnabled.test.js +55 -0
  33. package/src/elements/common/routing/index.js +2 -1
  34. package/src/elements/common/routing/withRouterIfEnabled.js +24 -0
  35. package/src/elements/content-explorer/ContentExplorer.tsx +23 -25
  36. package/src/elements/content-explorer/MetadataViewContainer.tsx +77 -12
  37. package/src/elements/content-explorer/__tests__/MetadataViewContainer.test.tsx +38 -3
  38. package/src/elements/content-explorer/stories/MetadataView.stories.tsx +4 -0
  39. package/src/elements/content-explorer/stories/tests/MetadataView-visual.stories.tsx +37 -3
  40. package/src/elements/content-sidebar/AddTaskButton.js +5 -4
  41. package/src/elements/content-sidebar/SidebarToggle.js +3 -2
  42. package/src/elements/content-sidebar/__tests__/__snapshots__/ActivitySidebar.test.js.snap +1 -1
  43. package/src/elements/content-sidebar/versions/VersionsSidebarContainer.js +5 -4
@@ -4,5 +4,6 @@ type Story = StoryObj<typeof ContentExplorer>;
4
4
  export declare const metadataView: Story;
5
5
  export declare const metadataViewV2: Story;
6
6
  export declare const metadataViewV2WithCustomActions: Story;
7
+ export declare const metadataViewV2WithInitialFilterValues: Story;
7
8
  declare const meta: Meta<typeof ContentExplorer>;
8
9
  export default meta;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "box-ui-elements",
3
- "version": "23.4.0",
3
+ "version": "23.5.0-beta.2",
4
4
  "description": "Box UI Elements",
5
5
  "author": "Box (https://www.box.com/)",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -132,15 +132,15 @@
132
132
  "@box/blueprint-web-assets": "4.61.5",
133
133
  "@box/box-ai-agent-selector": "^0.52.0",
134
134
  "@box/box-ai-content-answers": "^0.124.1",
135
- "@box/box-item-type-selector": "^0.61.12",
135
+ "@box/box-item-type-selector": "^0.63.12",
136
136
  "@box/cldr-data": "^34.2.0",
137
137
  "@box/combobox-with-api": "^0.34.9",
138
138
  "@box/frontend": "^11.0.1",
139
- "@box/item-icon": "^0.17.0",
139
+ "@box/item-icon": "^0.17.15",
140
140
  "@box/languages": "^1.0.0",
141
141
  "@box/metadata-editor": "^0.122.12",
142
- "@box/metadata-filter": "^1.16.12",
143
- "@box/metadata-view": "^0.29.4",
142
+ "@box/metadata-filter": "^1.19.2",
143
+ "@box/metadata-view": "^0.41.2",
144
144
  "@box/react-virtualized": "^9.22.3-rc-box.10",
145
145
  "@box/types": "^0.2.1",
146
146
  "@cfaester/enzyme-adapter-react-18": "^0.8.0",
@@ -301,13 +301,13 @@
301
301
  "@box/blueprint-web-assets": "4.61.5",
302
302
  "@box/box-ai-agent-selector": "^0.52.0",
303
303
  "@box/box-ai-content-answers": "^0.124.1",
304
- "@box/box-item-type-selector": "^0.61.12",
304
+ "@box/box-item-type-selector": "^0.63.12",
305
305
  "@box/cldr-data": ">=34.2.0",
306
306
  "@box/combobox-with-api": "^0.34.9",
307
- "@box/item-icon": "^0.17.0",
307
+ "@box/item-icon": "^0.17.15",
308
308
  "@box/metadata-editor": "^0.122.12",
309
- "@box/metadata-filter": "^1.16.12",
310
- "@box/metadata-view": "^0.29.4",
309
+ "@box/metadata-filter": "^1.19.2",
310
+ "@box/metadata-view": "^0.41.2",
311
311
  "@box/react-virtualized": "^9.22.3-rc-box.10",
312
312
  "@box/types": "^0.2.1",
313
313
  "@hapi/address": "^2.1.4",
@@ -0,0 +1,55 @@
1
+ // @flow
2
+ import * as React from 'react';
3
+ import { MemoryRouter } from 'react-router-dom';
4
+ import { render } from '../../../../test-utils/testing-library';
5
+ import withRouterIfEnabled from '../withRouterIfEnabled';
6
+
7
+ const TestComponent = (props: any) => {
8
+ const { history, location, match, routerDisabled } = props;
9
+ return (
10
+ <div
11
+ data-testid="test-component"
12
+ data-router-disabled={routerDisabled ? 'true' : undefined}
13
+ data-has-history={history ? 'true' : undefined}
14
+ data-has-location={location ? 'true' : undefined}
15
+ data-has-match={match ? 'true' : undefined}
16
+ />
17
+ );
18
+ };
19
+ TestComponent.displayName = 'TestComponent';
20
+
21
+ const WithRouterIfEnabled = withRouterIfEnabled(TestComponent);
22
+
23
+ test('injects router props when wrapped in a Router', () => {
24
+ const { getByTestId } = render(
25
+ <MemoryRouter initialEntries={[{ pathname: '/foo' }]}>
26
+ <WithRouterIfEnabled />
27
+ </MemoryRouter>,
28
+ );
29
+
30
+ const component = getByTestId('test-component');
31
+ expect(component).toHaveAttribute('data-has-history', 'true');
32
+ expect(component).toHaveAttribute('data-has-location', 'true');
33
+ expect(component).toHaveAttribute('data-has-match', 'true');
34
+ expect(component).not.toHaveAttribute('data-router-disabled');
35
+ });
36
+
37
+ test('renders without Router and without router props (routerDisabled prop)', () => {
38
+ const { getByTestId } = render(<WithRouterIfEnabled routerDisabled />);
39
+ const component = getByTestId('test-component');
40
+ expect(component).not.toHaveAttribute('data-has-history');
41
+ expect(component).not.toHaveAttribute('data-has-location');
42
+ expect(component).not.toHaveAttribute('data-has-match');
43
+ expect(component).toHaveAttribute('data-router-disabled', 'true');
44
+ });
45
+
46
+ test('renders without Router and without router props (feature flag)', () => {
47
+ const features = { routerDisabled: { value: true } };
48
+ const { getByTestId } = render(<WithRouterIfEnabled features={features} />);
49
+
50
+ const component = getByTestId('test-component');
51
+ expect(component).not.toHaveAttribute('data-has-history');
52
+ expect(component).not.toHaveAttribute('data-has-location');
53
+ expect(component).not.toHaveAttribute('data-has-match');
54
+ });
55
+
@@ -1,2 +1,3 @@
1
- // eslint-disable-next-line import/prefer-default-export
1
+ // @flow
2
2
  export { default as withRouterAndRef } from './withRouterAndRef';
3
+ export { default as withRouterIfEnabled } from './withRouterIfEnabled';
@@ -0,0 +1,24 @@
1
+ // @flow
2
+ import * as React from 'react';
3
+ import { withRouter } from 'react-router-dom';
4
+ import { isFeatureEnabled } from '../feature-checking';
5
+
6
+ export default function withRouterIfEnabled(Wrapped: React.ComponentType<any>) {
7
+
8
+ const WrappedWithRouter = withRouter(Wrapped);
9
+
10
+ const WithRouterIfEnabled = (props: any) => {
11
+ const routerDisabled =
12
+ props?.routerDisabled === true || isFeatureEnabled(props?.features, 'routerDisabled.value');
13
+
14
+ const Component = routerDisabled ? Wrapped : WrappedWithRouter;
15
+
16
+ return <Component {...props} />;
17
+ };
18
+
19
+ const name = Wrapped.displayName || Wrapped.name || 'Component';
20
+ WithRouterIfEnabled.displayName = `withRouterIfEnabled(${name})`;
21
+
22
+ return WithRouterIfEnabled;
23
+ }
24
+
@@ -338,7 +338,7 @@ class ContentExplorer extends Component<ContentExplorerProps, State> {
338
338
  * @return {void}
339
339
  */
340
340
  componentDidMount() {
341
- const { currentFolderId, defaultView, metadataQuery }: ContentExplorerProps = this.props;
341
+ const { currentFolderId, defaultView }: ContentExplorerProps = this.props;
342
342
  this.rootElement = document.getElementById(this.id) as HTMLElement;
343
343
  this.appElement = this.rootElement.firstElementChild as HTMLElement;
344
344
 
@@ -348,7 +348,6 @@ class ContentExplorer extends Component<ContentExplorerProps, State> {
348
348
  break;
349
349
  case DEFAULT_VIEW_METADATA:
350
350
  this.showMetadataQueryResults();
351
- this.fetchFolderName(metadataQuery?.ancestor_folder_id);
352
351
  break;
353
352
  default:
354
353
  this.fetchFolder(currentFolderId);
@@ -390,12 +389,14 @@ class ContentExplorer extends Component<ContentExplorerProps, State> {
390
389
  metadataTemplate: MetadataTemplate,
391
390
  ): void => {
392
391
  const { nextMarker } = metadataQueryCollection;
392
+ const { metadataQuery, features } = this.props;
393
393
  const { currentCollection, currentPageNumber, markers }: State = this.state;
394
394
  const cloneMarkers = [...markers];
395
395
  if (nextMarker) {
396
396
  cloneMarkers[currentPageNumber + 1] = nextMarker;
397
397
  }
398
- this.setState({
398
+
399
+ const nextState = {
399
400
  currentCollection: {
400
401
  ...currentCollection,
401
402
  ...metadataQueryCollection,
@@ -403,7 +404,25 @@ class ContentExplorer extends Component<ContentExplorerProps, State> {
403
404
  },
404
405
  markers: cloneMarkers,
405
406
  metadataTemplate,
406
- });
407
+ };
408
+
409
+ // if v2, fetch folder name and add to state
410
+ if (metadataQuery?.ancestor_folder_id && isFeatureEnabled(features, 'contentExplorer.metadataViewV2')) {
411
+ this.api.getFolderAPI().getFolderFields(
412
+ metadataQuery.ancestor_folder_id,
413
+ ({ name }) => {
414
+ this.setState({
415
+ ...nextState,
416
+ rootName: name || '',
417
+ });
418
+ },
419
+ this.errorCallback,
420
+ { fields: [FIELD_NAME] },
421
+ );
422
+ } else {
423
+ // No folder name to fetch, update state immediately with just metadata
424
+ this.setState(nextState);
425
+ }
407
426
  };
408
427
 
409
428
  /**
@@ -1628,27 +1647,6 @@ class ContentExplorer extends Component<ContentExplorerProps, State> {
1628
1647
  this.setState({ selectedItemIds: new Set() });
1629
1648
  };
1630
1649
 
1631
- /**
1632
- * Fetches the folder name and stores it in state rootName if successful
1633
- *
1634
- * @private
1635
- * @return {void}
1636
- */
1637
- fetchFolderName = (folderId?: string) => {
1638
- if (!folderId) {
1639
- return;
1640
- }
1641
-
1642
- this.api.getFolderAPI(false).getFolderFields(
1643
- folderId,
1644
- ({ name }) => {
1645
- this.setState({ rootName: name });
1646
- },
1647
- this.errorCallback,
1648
- { fields: [FIELD_NAME] },
1649
- );
1650
- };
1651
-
1652
1650
  /**
1653
1651
  * Renders the file picker
1654
1652
  *
@@ -1,9 +1,58 @@
1
1
  import * as React from 'react';
2
+ import type { EnumType, FloatType, MetadataFormFieldValue, RangeType } from '@box/metadata-filter';
2
3
  import { MetadataView, type MetadataViewProps } from '@box/metadata-view';
3
- import type { MetadataTemplate } from '../../common/types/metadata';
4
+
4
5
  import type { Collection } from '../../common/types/core';
6
+ import type { MetadataTemplate } from '../../common/types/metadata';
7
+
8
+ // Public-friendly version of MetadataFormFieldValue from @box/metadata-filter
9
+ // (string[] for enum type, range/float objects stay the same)
10
+ type EnumToStringArray<T> = T extends EnumType ? string[] : T;
11
+ type ExternalMetadataFormFieldValue = EnumToStringArray<MetadataFormFieldValue>;
12
+
13
+ type ExternalFilterValues = Record<
14
+ string,
15
+ {
16
+ value: ExternalMetadataFormFieldValue;
17
+ }
18
+ >;
5
19
 
6
- export interface MetadataViewContainerProps extends Omit<MetadataViewProps, 'items'> {
20
+ type ActionBarProps = Omit<
21
+ MetadataViewProps['actionBarProps'],
22
+ 'initialFilterValues' | 'onFilterSubmit' | 'filterGroups'
23
+ > & {
24
+ initialFilterValues?: ExternalFilterValues;
25
+ onFilterSubmit?: (filterValues: ExternalFilterValues) => void;
26
+ };
27
+
28
+ function transformInitialFilterValuesToInternal(
29
+ publicValues?: ExternalFilterValues,
30
+ ): Record<string, { value: MetadataFormFieldValue }> | undefined {
31
+ if (!publicValues) return undefined;
32
+
33
+ return Object.entries(publicValues).reduce<Record<string, { value: MetadataFormFieldValue }>>(
34
+ (acc, [key, { value }]) => {
35
+ acc[key] = Array.isArray(value) ? { value: { enum: value } } : { value };
36
+ return acc;
37
+ },
38
+ {},
39
+ );
40
+ }
41
+
42
+ function transformInternalFieldsToPublic(
43
+ fields: Record<string, { value: MetadataFormFieldValue }>,
44
+ ): ExternalFilterValues {
45
+ return Object.entries(fields).reduce<ExternalFilterValues>((acc, [key, { value }]) => {
46
+ acc[key] =
47
+ 'enum' in value && Array.isArray(value.enum)
48
+ ? { value: value.enum }
49
+ : { value: value as RangeType | FloatType };
50
+ return acc;
51
+ }, {});
52
+ }
53
+
54
+ export interface MetadataViewContainerProps extends Omit<MetadataViewProps, 'items' | 'actionBarProps'> {
55
+ actionBarProps?: ActionBarProps;
7
56
  currentCollection: Collection;
8
57
  metadataTemplate: MetadataTemplate;
9
58
  }
@@ -16,6 +65,7 @@ const MetadataViewContainer = ({
16
65
  ...rest
17
66
  }: MetadataViewContainerProps) => {
18
67
  const { items = [] } = currentCollection;
68
+ const { initialFilterValues: initialFilterValuesProp, onFilterSubmit: onFilterSubmitProp } = actionBarProps ?? {};
19
69
 
20
70
  const filterGroups = React.useMemo(
21
71
  () => [
@@ -36,17 +86,32 @@ const MetadataViewContainer = ({
36
86
  [metadataTemplate],
37
87
  );
38
88
 
39
- return (
40
- <MetadataView
41
- actionBarProps={{
42
- ...actionBarProps,
43
- filterGroups,
44
- }}
45
- columns={columns}
46
- items={items}
47
- {...rest}
48
- />
89
+ // Transform initial filter values to internal field format
90
+ const initialFilterValues = React.useMemo(
91
+ () => transformInitialFilterValuesToInternal(initialFilterValuesProp),
92
+ [initialFilterValuesProp],
49
93
  );
94
+
95
+ // Transform field values to public-friendly format
96
+ const onFilterSubmit = React.useCallback(
97
+ (fields: Record<string, { value: MetadataFormFieldValue }>) => {
98
+ if (!onFilterSubmitProp) return;
99
+ const transformed = transformInternalFieldsToPublic(fields);
100
+ onFilterSubmitProp(transformed);
101
+ },
102
+ [onFilterSubmitProp],
103
+ );
104
+
105
+ const transformedActionBarProps = React.useMemo(() => {
106
+ return {
107
+ ...actionBarProps,
108
+ initialFilterValues,
109
+ onFilterSubmit,
110
+ filterGroups,
111
+ };
112
+ }, [actionBarProps, initialFilterValues, onFilterSubmit, filterGroups]);
113
+
114
+ return <MetadataView actionBarProps={transformedActionBarProps} columns={columns} items={items} {...rest} />;
50
115
  };
51
116
 
52
117
  export default MetadataViewContainer;
@@ -1,8 +1,9 @@
1
1
  import * as React from 'react';
2
- import { render, screen } from '../../../test-utils/testing-library';
3
- import MetadataViewContainer, { MetadataViewContainerProps } from '../MetadataViewContainer';
2
+
4
3
  import type { Collection } from '../../../common/types/core';
5
4
  import type { MetadataTemplate, MetadataTemplateField } from '../../../common/types/metadata';
5
+ import { render, screen, userEvent, waitFor, within } from '../../../test-utils/testing-library';
6
+ import MetadataViewContainer, { type MetadataViewContainerProps } from '../MetadataViewContainer';
6
7
 
7
8
  describe('elements/content-explorer/MetadataViewContainer', () => {
8
9
  const mockItems = [
@@ -18,7 +19,7 @@ describe('elements/content-explorer/MetadataViewContainer', () => {
18
19
  type: 'string',
19
20
  },
20
21
  {
21
- id: 'field1',
22
+ id: 'field2',
22
23
  key: 'industry',
23
24
  displayName: 'Industry',
24
25
  type: 'enum',
@@ -80,4 +81,38 @@ describe('elements/content-explorer/MetadataViewContainer', () => {
80
81
  expect(screen.getByText('File 1.txt')).toBeInTheDocument();
81
82
  expect(screen.getByText('File 2.pdf')).toBeInTheDocument();
82
83
  });
84
+
85
+ test('should pass values as string[] on submit', async () => {
86
+ const onFilterSubmit = jest.fn();
87
+ const template: MetadataTemplate = {
88
+ ...mockMetadataTemplate,
89
+ fields: [
90
+ {
91
+ id: 'ms1',
92
+ key: 'role',
93
+ displayName: 'Contact Role',
94
+ type: 'multiSelect',
95
+ options: [
96
+ { id: 'r1', key: 'Developer' },
97
+ { id: 'r2', key: 'Marketing' },
98
+ { id: 'r3', key: 'Sales' },
99
+ ],
100
+ },
101
+ ],
102
+ };
103
+
104
+ renderComponent({ metadataTemplate: template, actionBarProps: { onFilterSubmit } });
105
+
106
+ await userEvent().click(screen.getByRole('button', { name: /Contact Role/ }));
107
+ await userEvent().click(within(screen.getByRole('menu')).getByRole('menuitemcheckbox', { name: 'Developer' }));
108
+ // Re-open the chip to select a second value (menu closes after submit)
109
+ await userEvent().click(screen.getByRole('button', { name: /Contact Role/ }));
110
+ await userEvent().click(within(screen.getByRole('menu')).getByRole('menuitemcheckbox', { name: 'Marketing' }));
111
+
112
+ await waitFor(() => expect(onFilterSubmit).toHaveBeenCalledTimes(2));
113
+ const firstCall = onFilterSubmit.mock.calls[0][0];
114
+ const secondCall = onFilterSubmit.mock.calls[1][0];
115
+ expect(firstCall['role-filter'].value).toEqual(['Developer']);
116
+ expect(secondCall['role-filter'].value).toEqual(['Developer', 'Marketing']);
117
+ });
83
118
  });
@@ -5,6 +5,7 @@ import type { Meta, StoryObj } from '@storybook/react';
5
5
  import ContentExplorer from '../ContentExplorer';
6
6
  import { DEFAULT_HOSTNAME_API } from '../../../constants';
7
7
  import { mockMetadata, mockSchema } from '../../common/__mocks__/mockMetadata';
8
+ import { mockRootFolder } from '../../common/__mocks__/mockRootFolder';
8
9
 
9
10
  const EID = '0';
10
11
  const templateName = 'templateName';
@@ -120,6 +121,9 @@ const meta: Meta<typeof ContentExplorer> = {
120
121
  http.get(`${DEFAULT_HOSTNAME_API}/2.0/metadata_templates/enterprise/templateName/schema`, () => {
121
122
  return HttpResponse.json(mockSchema);
122
123
  }),
124
+ http.get(`${DEFAULT_HOSTNAME_API}/2.0/folders/:id`, () => {
125
+ return HttpResponse.json(mockRootFolder);
126
+ }),
123
127
  ],
124
128
  },
125
129
  },
@@ -1,12 +1,14 @@
1
- import { http, HttpResponse } from 'msw';
2
1
  import type { Meta, StoryObj } from '@storybook/react';
2
+ import { http, HttpResponse } from 'msw';
3
+ import { expect, userEvent, waitFor, within } from 'storybook/test';
3
4
  import { Download, SignMeOthers } from '@box/blueprint-web-assets/icons/Fill/index';
4
5
  import { Sign } from '@box/blueprint-web-assets/icons/Line';
5
- import { expect, userEvent, waitFor, within } from 'storybook/test';
6
6
  import noop from 'lodash/noop';
7
+
7
8
  import ContentExplorer from '../../ContentExplorer';
8
9
  import { DEFAULT_HOSTNAME_API } from '../../../../constants';
9
10
  import { mockMetadata, mockSchema } from '../../../common/__mocks__/mockMetadata';
11
+ import { mockRootFolder } from '../../../common/__mocks__/mockRootFolder';
10
12
 
11
13
  // 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.
12
14
  const { scope: templateScope, templateKey } = mockSchema;
@@ -126,13 +128,42 @@ export const metadataViewV2WithCustomActions: Story = {
126
128
  await waitFor(() => {
127
129
  expect(canvas.getByRole('row', { name: /Child 2/i })).toBeInTheDocument();
128
130
  });
129
-
130
131
  const firstRow = canvas.getByRole('row', { name: /Child 2/i });
131
132
  const ellipsesButton = within(firstRow).getByRole('button', { name: 'Action menu' });
132
133
  userEvent.click(ellipsesButton);
133
134
  },
134
135
  };
135
136
 
137
+ const initialFilterActionBarProps = {
138
+ initialFilterValues: {
139
+ 'industry-filter': { value: ['Legal'] },
140
+ 'mimetype-filter': { value: ['boxnoteType', 'documentType', 'threedType'] },
141
+ 'role-filter': { value: ['Developer', 'Business Owner', 'Marketing'] },
142
+ },
143
+ };
144
+
145
+ export const metadataViewV2WithInitialFilterValues: Story = {
146
+ args: {
147
+ ...metadataViewV2ElementProps,
148
+ metadataViewProps: {
149
+ columns,
150
+ actionBarProps: initialFilterActionBarProps,
151
+ },
152
+ },
153
+ play: async ({ canvas }) => {
154
+ // Wait for chips to update with initial values
155
+ await waitFor(() => {
156
+ expect(canvas.getByRole('button', { name: /Industry/i })).toHaveTextContent(/\(1\)/);
157
+ });
158
+ // Other chips should reflect initialized values
159
+ const contactRoleChip = canvas.getByRole('button', { name: /Contact Role/i });
160
+ expect(contactRoleChip).toHaveTextContent(/\(3\)/);
161
+
162
+ const fileTypeChip = canvas.getByRole('button', { name: /Box Note/i });
163
+ expect(fileTypeChip).toHaveTextContent(/\+2/);
164
+ },
165
+ };
166
+
136
167
  const meta: Meta<typeof ContentExplorer> = {
137
168
  title: 'Elements/ContentExplorer/tests/MetadataView/visual',
138
169
  component: ContentExplorer,
@@ -150,6 +181,9 @@ const meta: Meta<typeof ContentExplorer> = {
150
181
  http.get(`${DEFAULT_HOSTNAME_API}/2.0/metadata_templates/enterprise/templateName/schema`, () => {
151
182
  return HttpResponse.json(mockSchema);
152
183
  }),
184
+ http.get(`${DEFAULT_HOSTNAME_API}/2.0/folders/:id`, () => {
185
+ return HttpResponse.json(mockRootFolder);
186
+ }),
153
187
  ],
154
188
  },
155
189
  },
@@ -1,6 +1,7 @@
1
1
  // @flow
2
2
  import * as React from 'react';
3
- import { withRouter, type RouterHistory } from 'react-router-dom';
3
+ import { type RouterHistory } from 'react-router-dom';
4
+ import { withRouterIfEnabled } from '../common/routing';
4
5
 
5
6
  import AddTaskMenu from './AddTaskMenu';
6
7
  import TaskModal from './TaskModal';
@@ -11,7 +12,7 @@ import type { ElementsXhrError } from '../../common/types/api';
11
12
  import type { InternalSidebarNavigation, InternalSidebarNavigationHandler } from '../common/types/SidebarNavigation';
12
13
 
13
14
  type Props = {|
14
- history: RouterHistory,
15
+ history?: RouterHistory,
15
16
  internalSidebarNavigation?: InternalSidebarNavigation,
16
17
  internalSidebarNavigationHandler?: InternalSidebarNavigationHandler,
17
18
  isDisabled: boolean,
@@ -54,7 +55,7 @@ class AddTaskButton extends React.Component<Props, State> {
54
55
  },
55
56
  true,
56
57
  );
57
- } else {
58
+ } else if (history) {
58
59
  history.replace({ state: { open: true } });
59
60
  }
60
61
 
@@ -103,4 +104,4 @@ class AddTaskButton extends React.Component<Props, State> {
103
104
  }
104
105
 
105
106
  export { AddTaskButton as AddTaskButtonComponent };
106
- export default withRouter(AddTaskButton);
107
+ export default withRouterIfEnabled(AddTaskButton);
@@ -5,7 +5,8 @@
5
5
  */
6
6
 
7
7
  import * as React from 'react';
8
- import { withRouter, type RouterHistory } from 'react-router-dom';
8
+ import { type RouterHistory } from 'react-router-dom';
9
+ import { withRouterIfEnabled } from '../common/routing';
9
10
  import SidebarToggleButton from '../../components/sidebar-toggle-button/SidebarToggleButton';
10
11
  import { SIDEBAR_NAV_TARGETS } from '../common/interactionTargets';
11
12
  import type { InternalSidebarNavigation, InternalSidebarNavigationHandler } from '../common/types/SidebarNavigation';
@@ -53,4 +54,4 @@ const SidebarToggle = ({
53
54
  };
54
55
 
55
56
  export { SidebarToggle as SidebarToggleComponent };
56
- export default withRouter(SidebarToggle);
57
+ export default withRouterIfEnabled(SidebarToggle);
@@ -4,7 +4,7 @@ exports[`elements/content-sidebar/ActivitySidebar render() should render the act
4
4
  <SidebarContent
5
5
  actions={
6
6
  <React.Fragment>
7
- <withRouter(AddTaskButton)
7
+ <withRouterIfEnabled(AddTaskButton)
8
8
  isDisabled={false}
9
9
  onTaskModalClose={[Function]}
10
10
  taskFormProps={
@@ -9,7 +9,7 @@ import flow from 'lodash/flow';
9
9
  import getProp from 'lodash/get';
10
10
  import merge from 'lodash/merge';
11
11
  import noop from 'lodash/noop';
12
- import { generatePath, withRouter } from 'react-router-dom';
12
+ import { generatePath } from 'react-router-dom';
13
13
  import type { Match, RouterHistory } from 'react-router-dom';
14
14
  import type { MessageDescriptor } from 'react-intl';
15
15
  import { withFeatureConsumer, isFeatureEnabled } from '../../common/feature-checking';
@@ -21,6 +21,7 @@ import StaticVersionsSidebar from './StaticVersionSidebar';
21
21
  import VersionsSidebar from './VersionsSidebar';
22
22
  import VersionsSidebarAPI from './VersionsSidebarAPI';
23
23
  import { withAPIContext } from '../../common/api-context';
24
+ import { withRouterIfEnabled } from '../../common/routing';
24
25
  import type { FeatureConfig } from '../../common/feature-checking';
25
26
  import type { VersionActionCallback, VersionChangeCallback, SidebarLoadCallback } from './flowTypes';
26
27
  import type { BoxItemVersion, BoxItem, FileVersions } from '../../../common/types/core';
@@ -36,7 +37,7 @@ type Props = {
36
37
  features: FeatureConfig,
37
38
  fileId: string,
38
39
  hasSidebarInitialized?: boolean,
39
- history: RouterHistory,
40
+ history?: RouterHistory,
40
41
  internalSidebarNavigation?: InternalSidebarNavigation,
41
42
  internalSidebarNavigationHandler?: InternalSidebarNavigationHandler,
42
43
  match: Match,
@@ -283,7 +284,7 @@ class VersionsSidebarContainer extends React.Component<Props, State> {
283
284
  delete navigationUpdate.versionId;
284
285
  }
285
286
  internalSidebarNavigationHandler(navigationUpdate);
286
- } else {
287
+ } else if (history) {
287
288
  history.push(generatePath(match.path, { ...match.params, versionId }));
288
289
  }
289
290
  };
@@ -349,4 +350,4 @@ class VersionsSidebarContainer extends React.Component<Props, State> {
349
350
 
350
351
  export type VersionsSidebarProps = Props;
351
352
  export { VersionsSidebarContainer as VersionsSidebarContainerComponent };
352
- export default flow([withRouter, withAPIContext, withFeatureConsumer])(VersionsSidebarContainer);
353
+ export default flow([withRouterIfEnabled, withAPIContext, withFeatureConsumer])(VersionsSidebarContainer);