Skip to content

Commit f51667a

Browse files
committed
feat(metadata-view): pass-thru onSortChange
from the element to the shared-feature
1 parent 682dbe4 commit f51667a

4 files changed

Lines changed: 72 additions & 12 deletions

File tree

src/elements/content-explorer/Content.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ const Content = ({
8989
isLoading={percentLoaded !== 100}
9090
hasError={view === VIEW_ERROR}
9191
metadataTemplate={metadataTemplate}
92+
onSortChange={onSortChange}
9293
{...metadataViewProps}
9394
/>
9495
)}

src/elements/content-explorer/ContentExplorer.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import throttle from 'lodash/throttle';
1010
import uniqueid from 'lodash/uniqueId';
1111
import { TooltipProvider } from '@box/blueprint-web';
1212
import { AxiosRequestConfig, AxiosResponse } from 'axios';
13-
import type { Selection } from 'react-aria-components';
13+
import type { Key, Selection } from 'react-aria-components';
1414

1515
import CreateFolderDialog from '../common/create-folder-dialog';
1616
import UploadDialog from '../common/upload-dialog';
@@ -152,7 +152,7 @@ export interface ContentExplorerProps {
152152
rootFolderId?: string;
153153
sharedLink?: string;
154154
sharedLinkPassword?: string;
155-
sortBy?: SortBy;
155+
sortBy?: SortBy | Key;
156156
sortDirection?: SortDirection;
157157
staticHost?: string;
158158
staticPath?: string;
@@ -895,7 +895,7 @@ class ContentExplorer extends Component<ContentExplorerProps, State> {
895895
* @param {string} sortDirection - sort direction
896896
* @return {void}
897897
*/
898-
sort = (sortBy: SortBy, sortDirection: SortDirection) => {
898+
sort = (sortBy: SortBy | Key, sortDirection: SortDirection) => {
899899
const {
900900
currentCollection: { id },
901901
view,

src/elements/content-explorer/MetadataViewContainer.tsx

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import * as React from 'react';
22
import type { EnumType, FloatType, MetadataFormFieldValue, RangeType } from '@box/metadata-filter';
33
import { MetadataView, type MetadataViewProps } from '@box/metadata-view';
4+
import { type Key } from '@react-types/shared';
45

6+
import { SortDescriptor } from 'react-aria-components';
57
import type { Collection } from '../../common/types/core';
68
import type { MetadataTemplate } from '../../common/types/metadata';
79

@@ -55,13 +57,16 @@ export interface MetadataViewContainerProps extends Omit<MetadataViewProps, 'ite
5557
actionBarProps?: ActionBarProps;
5658
currentCollection: Collection;
5759
metadataTemplate: MetadataTemplate;
60+
/* Internally controlled onSortChange prop for the MetadataView component. */
61+
onSortChange?: (sortBy: Key, sortDirection: string) => void;
5862
}
5963

6064
const MetadataViewContainer = ({
6165
actionBarProps,
6266
columns,
6367
currentCollection,
6468
metadataTemplate,
69+
onSortChange: onSortChangeBUIE,
6570
...rest
6671
}: MetadataViewContainerProps) => {
6772
const { items = [] } = currentCollection;
@@ -111,7 +116,46 @@ const MetadataViewContainer = ({
111116
};
112117
}, [actionBarProps, initialFilterValues, onFilterSubmit, filterGroups]);
113118

114-
return <MetadataView actionBarProps={transformedActionBarProps} columns={columns} items={items} {...rest} />;
119+
// Extract the original tableProps.onSortChange from rest
120+
const { tableProps, ...otherRest } = rest;
121+
const onSortChangeConsumer = tableProps?.onSortChange;
122+
123+
// Create a wrapper function that calls both. The wrapper function should follow the signature of onSortChange from RAC
124+
const handleSortChange = React.useCallback(
125+
({ column, direction }: SortDescriptor) => {
126+
// Call the internal onSortChange first
127+
// API accepts asc/desc "https://developer.box.com/reference/post-metadata-queries-execute-read/"
128+
if (onSortChangeBUIE) {
129+
onSortChangeBUIE(column, direction === 'ascending' ? 'ASC' : 'DESC');
130+
}
131+
132+
// Then call the original customer-provided onSortChange if it exists
133+
// Accepts "ascending" / "descending" (https://react-spectrum.adobe.com/react-aria/Table.html)
134+
if (onSortChangeConsumer) {
135+
onSortChangeConsumer({
136+
column,
137+
direction,
138+
});
139+
}
140+
},
141+
[onSortChangeBUIE, onSortChangeConsumer],
142+
);
143+
144+
// Create new tableProps with our wrapper function
145+
const newTableProps = {
146+
...tableProps,
147+
onSortChange: handleSortChange,
148+
};
149+
150+
return (
151+
<MetadataView
152+
actionBarProps={transformedActionBarProps}
153+
columns={columns}
154+
items={items}
155+
tableProps={newTableProps}
156+
{...otherRest}
157+
/>
158+
);
115159
};
116160

117161
export default MetadataViewContainer;

src/elements/content-explorer/stories/tests/MetadataView-visual.stories.tsx

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ import { http, HttpResponse } from 'msw';
33
import { Download, SignMeOthers } from '@box/blueprint-web-assets/icons/Fill/index';
44
import { Sign } from '@box/blueprint-web-assets/icons/Line';
55
import { expect, fn, userEvent, waitFor, within, screen } from 'storybook/test';
6+
67
import noop from 'lodash/noop';
8+
import orderBy from 'lodash/orderBy';
79

810
import ContentExplorer from '../../ContentExplorer';
911
import { DEFAULT_HOSTNAME_API } from '../../../../constants';
@@ -138,17 +140,16 @@ export const metadataViewV2: Story = {
138140
args: metadataViewV2ElementProps,
139141
};
140142

141-
// @TODO Assert that rows are actually sorted in a different order, once handleSortChange is implemented
142143
export const metadataViewV2SortsFromHeader: Story = {
143144
args: metadataViewV2ElementProps,
144145
play: async ({ canvas }) => {
145-
await waitFor(() => {
146-
expect(canvas.getByRole('row', { name: /Industry/i })).toBeInTheDocument();
147-
});
146+
const industryHeader = await canvas.findByRole('columnheader', { name: 'Industry' });
147+
expect(industryHeader).toBeInTheDocument();
148148

149-
const firstRow = canvas.getByRole('row', { name: /Industry/i });
150-
const industryHeader = within(firstRow).getByRole('columnheader', { name: 'Industry' });
151-
userEvent.click(industryHeader);
149+
const firstRow = await canvas.findByRole('row', { name: /Child 2/i });
150+
expect(firstRow).toBeInTheDocument();
151+
152+
await userEvent.click(industryHeader);
152153
},
153154
};
154155

@@ -248,7 +249,21 @@ const meta: Meta<typeof ContentExplorer> = {
248249
parameters: {
249250
msw: {
250251
handlers: [
251-
http.post(`${DEFAULT_HOSTNAME_API}/2.0/metadata_queries/execute_read`, () => {
252+
// Note that the Metadata API backend normally handles the sorting. The mocks below simulate the sorting for specific cases, but may not 100% accurately reflect the backend behavior.
253+
http.post(`${DEFAULT_HOSTNAME_API}/2.0/metadata_queries/execute_read`, async ({ request }) => {
254+
const body = await request.clone().json();
255+
const orderByDirection = body.order_by[0].direction;
256+
const orderByFieldKey = body.order_by[0].field_key;
257+
258+
// Hardcoded case for sorting by industry
259+
if (orderByFieldKey === `${metadataFieldNamePrefix}.industry` && orderByDirection === 'ASC') {
260+
const sortedMetadata = orderBy(
261+
mockMetadata.entries,
262+
'metadata.enterprise_0.templateName.industry',
263+
'asc',
264+
);
265+
return HttpResponse.json({ ...mockMetadata, entries: sortedMetadata });
266+
}
252267
return HttpResponse.json(mockMetadata);
253268
}),
254269
http.get(`${DEFAULT_HOSTNAME_API}/2.0/metadata_templates/enterprise/templateName/schema`, () => {

0 commit comments

Comments
 (0)