Skip to content

Conversation

@jpan-box
Copy link
Contributor

@jpan-box jpan-box commented Aug 25, 2025

from the element to the shared-feature

Desired User Behavior:

  • When a user clicks a header, the table should provide the selected column ID to Content Explorer's sort function, which calls the MD Query API.
  • The customer-provided onSortChange should also be called at the same time, so that the customer may do things like update query parameters in the URL.
  • The sorting behavior is handled by the metadata query API. Our element only makes the call and displays the data.

Previous State:

  • A customer could provide an onSortChange, but neither the customer-provided callback nor the internally-managed onSortChange were called.
  • When clicking a column header, we would call the GET template schema endpoint for each click. This caused a problem when clicking multiple times very quickly.

New State:

  • Passes both internally-managed onSortChange and customer-provided onSortChange to a handleSortChange, which calls both.
  • Updated column ID used in the MD query to match expected format.
    ** handleSortChange trims the column ID before passing the ID to the API.
  • Cache result from GET template schema API call. We do not expect template schema to update in the timeframe that a user is interacting with MDV.

Verification:

  • Comment out MSW handlers in VRT, replace folder ID and token, replace mockSchema with your schema

Summary by CodeRabbit

  • New Features

    • Metadata view exposes a public sort-change callback and Content Explorer forwards sort events so apps can react to column sorting.
  • Performance Improvements

    • Client-side caching for metadata template schemas reduces redundant requests and speeds up metadata-driven views.
  • Tests

    • Unit and visual tests updated to validate dual-sort behavior, rename sorting flags to allowsSorting, and simulate backend sorting for stability.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 25, 2025

Walkthrough

I pity the fool who misses this: adds client-side caching for metadata template schemas and updates metadata sorting plumbing — forwards onSortChange, introduces dual internal/user sort callbacks, widens sortBy typing to accept Key, and updates tests/stories to use allowsSorting and simulate backend sorting.

Changes

Cohort / File(s) Summary of changes
Metadata API caching
src/api/Metadata.js
Added getMetadataTemplateSchemaCacheKey(templateKey: string). Made getSchemaByTemplateKey async: check APICache for key, return cached value if present; otherwise await XHR, cache result, return schema.
Metadata view sorting plumbing
src/elements/content-explorer/Content.tsx, src/elements/content-explorer/ContentExplorer.tsx, src/elements/content-explorer/MetadataViewContainer.tsx
Content forwards onSortChange to MetadataViewContainer. ContentExplorer widens sortBy type to `SortBy
Tests and stories updates
src/elements/content-explorer/__tests__/ContentExplorer.test.tsx, src/elements/content-explorer/stories/tests/MetadataView-visual.stories.tsx
Tests: allowSortingallowsSorting, added dual-callback sort test asserting internal trimmed key and user RAC args. Stories: use async queries, MSW handler simulates ASC sorting for industry via lodash/orderBy, added noop/orderBy imports and note about simulated sorting.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant CE as ContentExplorer
  participant C as Content
  participant MVC as MetadataViewContainer
  participant MV as MetadataView (Table)
  Note over CE,MVC: Sorting flow (dual callbacks)
  CE->>C: Render with onSortChange prop
  C->>MVC: onSortChange forwarded
  MVC->>MV: tableProps with wrapped onSortChange
  MV-->>MVC: onSortChange({column, direction})
  rect rgba(233,246,255,0.6)
    note right of MVC: Map RAC descriptor -> internal (trimmed key, ASC/DESC)
    MVC->>MVC: Call internalOnSortChange(trimmedKey, ASC|DESC)
    MVC->>CE: Call consumer onSortChange(column, "ascending"/"descending") if provided
  end
Loading
sequenceDiagram
  autonumber
  participant UI as Caller
  participant M as Metadata API
  participant Cache as APICache
  participant XHR as XHR
  Note over M: getSchemaByTemplateKey(templateKey)
  UI->>M: getSchemaByTemplateKey(key)
  M->>Cache: get(cacheKey)
  alt Cache hit
    Cache-->>M: schema
    M-->>UI: schema
  else Cache miss
    M->>XHR: GET /metadata_templates/{key}/schema
    XHR-->>M: schema
    M->>Cache: set(cacheKey, schema)
    M-->>UI: schema
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • tjuanitas
  • tjiang-box

Poem

I pity the fool who fetches twice,
I cache that schema — ain't that nice.
Two sorts now march, trimmed then plain,
Internal first, then user gain.
Ascend, descend — we handle the slice.


📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP (Model Context Protocol) integration is disabled
  • Jira integration is disabled
  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 573134f and 7f009a7.

📒 Files selected for processing (1)
  • src/elements/content-explorer/ContentExplorer.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/elements/content-explorer/ContentExplorer.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Rule: Automatic merge queue (queue)
  • GitHub Check: lint_test_build
  • GitHub Check: Summary
  • GitHub Check: Queue: Embarked in merge queue
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbit in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbit in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbit gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbit read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbit help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbit ignore or @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbit summary or @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbit or @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@jpan-box jpan-box force-pushed the mdv-osc branch 2 times, most recently from f51667a to 5791e15 Compare August 26, 2025 20:49
@jpan-box jpan-box marked this pull request as ready for review August 27, 2025 12:55
@jpan-box jpan-box requested a review from a team as a code owner August 27, 2025 12:55
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/elements/content-explorer/Content.tsx (1)

87-95: Avoid consumer override of internal onSortChange.

Because {...metadataViewProps} is spread after onSortChange, a consumer-provided onSortChange inside metadataViewProps will override the internal handler, breaking pass-thru. Spread first, then set onSortChange (or omit onSortChange from the type).

Apply:

 <MetadataViewContainer
     currentCollection={currentCollection}
     isLoading={percentLoaded !== 100}
     hasError={view === VIEW_ERROR}
     metadataTemplate={metadataTemplate}
-    onSortChange={onSortChange}
     {...metadataViewProps}
+    onSortChange={onSortChange}
 />

Optionally tighten the prop type:

- metadataViewProps?: Omit<MetadataViewContainerProps, 'currentCollection'>;
+ metadataViewProps?: Omit<MetadataViewContainerProps, 'currentCollection' | 'onSortChange'>;
🧹 Nitpick comments (9)
src/api/Metadata.js (1)

93-102: Cache key helper looks good, but consider scope collisions.

If multiple scopes are ever introduced for template schemas, templateKey-only keys could collide. Consider including scope in the key for future-proofing.

Example:

- getMetadataTemplateSchemaCacheKey(templateKey: string): string {
-     return `${CACHE_PREFIX_METADATA}template_schema_${templateKey}`;
- }
+ getMetadataTemplateSchemaCacheKey(templateKey: string, scope: string = 'enterprise'): string {
+     return `${CACHE_PREFIX_METADATA}template_schema_${scope}_${templateKey}`;
+ }
src/elements/content-explorer/ContentExplorer.tsx (2)

13-14: Type import alignment with RAC.

Good move importing Key from react-aria-components here. Keep this consistent across files (see MetadataViewContainer.tsx which imports Key from @react-types/shared).


155-156: Prop typing widened correctly; update State for consistency.

sortBy?: SortBy | Key is fine. Mirror this in State.sortBy to avoid narrowing back to string.

Apply:

- sortBy: SortBy | string;
+ sortBy: SortBy | Key;
src/elements/content-explorer/stories/tests/MetadataView-visual.stories.tsx (2)

146-153: Strengthen assertion to verify sort actually applied.

After clicking the header, assert row order changes to catch regressions where order_by.field_key is wrong or mocks don’t sort.

Example:

- await userEvent.click(industryHeader);
+ await userEvent.click(industryHeader);
+ const rows = await canvas.findAllByRole('row');
+ expect(rows[1]).toHaveAccessibleName(/Child 1/i); // or whatever the sorted first row should be

252-267: Mock handler: be lenient on direction casing and support both asc/desc.

Backend expects ASC/DESC, but callers may pass lowercase in some paths. Handle both to make the story resilient.

Apply:

- const orderByDirection = body.order_by[0].direction;
+ const orderByDirection = String(body.order_by?.[0]?.direction || '').toUpperCase();
  const orderByFieldKey = body.order_by[0].field_key;

  // Hardcoded case for sorting by industry
- if (orderByFieldKey === `${metadataFieldNamePrefix}.industry` && orderByDirection === 'ASC') {
+ if (orderByFieldKey === `${metadataFieldNamePrefix}.industry` && orderByDirection === 'ASC') {
     const sortedMetadata = orderBy(
         mockMetadata.entries,
         'metadata.enterprise_0.templateName.industry',
         'asc',
     );
     return HttpResponse.json({ ...mockMetadata, entries: sortedMetadata });
  }

Optionally add the DESC case as well for completeness.

src/elements/content-explorer/MetadataViewContainer.tsx (2)

4-4: Unify Key type source.

Import Key from react-aria-components (same as ContentExplorer) to keep types consistent and avoid dual sources.

Apply:

-import { type Key } from '@react-types/shared';
+import type { Key } from 'react-aria-components';

6-6: Type-only import for SortDescriptor.

Import as a type to avoid bundling a non-existent runtime symbol.

Apply:

-import { SortDescriptor } from 'react-aria-components';
+import type { SortDescriptor } from 'react-aria-components';
src/elements/content-explorer/__tests__/ContentExplorer.test.tsx (2)

457-460: Verify prop name and coverage: allowsSorting

  • Please confirm the prop is indeed allowsSorting (not allowSorting) for the table column API used by Metadata View V2.
  • Consider adding a quick test that clicks the Name column header to verify both callbacks fire for a non-metadata column as well.

Also applies to: 465-468


509-541: Strengthen the dual-callback test to reduce flakiness and broaden assertions

Good coverage of the dual-callback contract. Two small upgrades will make this test more robust and aligned with the PR goal of “trimmed column in API”:

  • Wrap callback assertions in waitFor to avoid timing flakiness after the click.
  • Click the header a second time to verify toggle to descending for both callback shapes.
  • Avoid hard-coding the full column ID; build it from metadataFieldNamePrefix to keep the test resilient.
  • Optionally, assert the POST payload for the metadata query uses the trimmed field key for sorting (either sort_by or order_by, depending on the implementation).

Suggested inline diff for this test block:

                 await userEvent.click(industryHeader);
 
-                // Internal callback gets trimmed version for API calls
-                expect(mockOnSortChangeBUIE).toHaveBeenCalledWith('industry', 'ASC');
-
-                // User callback gets full column ID with direction
-                expect(mockOnSortChangeConsumer).toHaveBeenCalledWith({
-                    column: 'metadata.enterprise_0.templateName.industry',
-                    direction: 'ascending',
-                });
+                // Internal callback gets trimmed version for API calls
+                await waitFor(() => {
+                    expect(mockOnSortChangeBUIE).toHaveBeenCalledWith('industry', 'ASC');
+                });
+
+                // User callback gets full column ID with direction
+                await waitFor(() => {
+                    expect(mockOnSortChangeConsumer).toHaveBeenCalledWith({
+                        column: `${metadataFieldNamePrefix}.industry`,
+                        direction: 'ascending',
+                    });
+                });
+
+                // Toggle to descending on second click
+                await userEvent.click(industryHeader);
+                await waitFor(() => {
+                    expect(mockOnSortChangeBUIE).toHaveBeenCalledWith('industry', 'DESC');
+                    expect(mockOnSortChangeConsumer).toHaveBeenCalledWith({
+                        column: `${metadataFieldNamePrefix}.industry`,
+                        direction: 'descending',
+                    });
+                });

Optional: import the Xhr mock and assert the trimmed field key is sent to the API (update to order_by if that’s what the API expects):

// Add near the imports (top of file)
import Xhr from '../../../utils/Xhr';

// Inside the test, after the second click:
const XhrMock = Xhr as unknown as jest.Mock;
const lastInstance = XhrMock.mock.instances.at(-1);
const postCalls = lastInstance.post.mock.calls as Array<[ { url: string; data?: any } ]>;
const lastExecuteRead = postCalls
  .map(args => args[0])
  .filter(arg => arg.url === 'https://api.box.com/2.0/metadata_queries/execute_read')
  .at(-1);

expect(lastExecuteRead?.data).toEqual(
  expect.objectContaining({
    // If implementation uses `order_by`, update below accordingly
    sort_by: [
      expect.objectContaining({
        field_key: 'industry',
      }),
    ],
  }),
);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 0c63595 and 5791e15.

📒 Files selected for processing (6)
  • src/api/Metadata.js (2 hunks)
  • src/elements/content-explorer/Content.tsx (1 hunks)
  • src/elements/content-explorer/ContentExplorer.tsx (3 hunks)
  • src/elements/content-explorer/MetadataViewContainer.tsx (4 hunks)
  • src/elements/content-explorer/__tests__/ContentExplorer.test.tsx (2 hunks)
  • src/elements/content-explorer/stories/tests/MetadataView-visual.stories.tsx (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
src/elements/content-explorer/MetadataViewContainer.tsx (1)
src/features/content-explorer/item-list/ItemList.js (1)
  • tableProps (201-201)
src/elements/content-explorer/stories/tests/MetadataView-visual.stories.tsx (1)
src/constants.js (2)
  • DEFAULT_HOSTNAME_API (210-210)
  • DEFAULT_HOSTNAME_API (210-210)
🪛 Biome (2.1.2)
src/api/Metadata.js

[error] 99-99: Type annotations are a TypeScript only feature. Convert your file to a TypeScript file or remove the syntax.

TypeScript only syntax

(parse)


[error] 99-99: return type annotation are a TypeScript only feature. Convert your file to a TypeScript file or remove the syntax.

TypeScript only syntax

(parse)


[error] 350-350: Type annotations are a TypeScript only feature. Convert your file to a TypeScript file or remove the syntax.

TypeScript only syntax

(parse)


[error] 350-350: return type annotation are a TypeScript only feature. Convert your file to a TypeScript file or remove the syntax.

TypeScript only syntax

(parse)


[error] 351-351: type annotation are a TypeScript only feature. Convert your file to a TypeScript file or remove the syntax.

TypeScript only syntax

(parse)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Summary
🔇 Additional comments (4)
src/api/Metadata.js (1)

99-101: Biome “TS-only syntax” warnings are false positives for Flow types in this file.

This file consistently uses Flow annotations. Ensure the formatter/linter is configured for Flow or the path is excluded from Biome’s TS parser to avoid noise.

Also applies to: 350-351

src/elements/content-explorer/ContentExplorer.tsx (1)

898-907: LGTM: sort handler accepts RAC Key.

Signature and refresh logic look correct for both folder and metadata views.

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

8-9: LGTM: orderBy inclusion for mock sorting.

src/elements/content-explorer/MetadataViewContainer.tsx (1)

71-77: Public onSortChange passthrough is well-scoped.

Prop shape and docstring make intent clear.

Copy link
Contributor

@jfox-box jfox-box left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall LGTM, some comments

jfox-box
jfox-box previously approved these changes Aug 27, 2025
Copy link
Contributor

@jfox-box jfox-box left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, pending other comments

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
src/elements/content-explorer/MetadataViewContainer.tsx (1)

160-164: Guard against undefined tableProps to prevent runtime error.
Spreading an undefined value throws. Use a nullish coalesce before spreading.

-    const newTableProps = {
-        ...tableProps,
-        onSortChange: handleSortChange,
-    };
+    const newTableProps = {
+        ...(tableProps ?? {}),
+        onSortChange: handleSortChange,
+    };
src/api/Metadata.js (1)

350-367: Bug: caching/returning raw XHR response instead of payload.
This will leak transport metadata to callers and break code expecting the schema object.

-    async getSchemaByTemplateKey(templateKey: string): Promise<MetadataTemplateSchemaResponse> {
+    async getSchemaByTemplateKey(templateKey: string): Promise<MetadataTemplateSchemaResponse> {
         const cache: APICache = this.getCache();
         const key = this.getMetadataTemplateSchemaCacheKey(templateKey);

         // Return cached value if it exists
         if (cache.has(key)) {
             return cache.get(key);
         }

         // Fetch from API if not cached
         const url = this.getMetadataTemplateSchemaUrl(templateKey);
-        const response = await this.xhr.get({ url });
+        const { data } = await this.xhr.get({ url });

         // Cache the response
-        cache.set(key, response);
+        cache.set(key, data);

-        return response;
+        return data;
     }
🧹 Nitpick comments (3)
src/elements/content-explorer/MetadataViewContainer.tsx (1)

134-159: Dual-callback wrapper is correct; minor comment drift.

  • Call order (internal → external) and direction mapping are right.
  • Nit: the comment says “API accepts asc/desc” but the code intentionally passes “ASC”/“DESC”. Align the comment to avoid confusion.
-            // API accepts asc/desc "https://developer.box.com/reference/post-metadata-queries-execute-read/"
+            // Upstream expects "ASC"/"DESC" here (API ultimately normalizes).
src/api/Metadata.js (1)

350-367: Optional: add a forceFetch to bypass cache when needed.
Helps invalidate stale schemas without touching cache internals.

-    async getSchemaByTemplateKey(templateKey: string): Promise<MetadataTemplateSchemaResponse> {
+    async getSchemaByTemplateKey(templateKey: string, forceFetch?: boolean = false): Promise<MetadataTemplateSchemaResponse> {
         const cache: APICache = this.getCache();
         const key = this.getMetadataTemplateSchemaCacheKey(templateKey);
-        if (cache.has(key)) {
+        if (!forceFetch && cache.has(key)) {
             return cache.get(key);
         }
         const url = this.getMetadataTemplateSchemaUrl(templateKey);
-        const { data } = await this.xhr.get({ url });
+        const { data } = await this.xhr.get({ url });
         cache.set(key, data);
         return data;
     }
src/elements/content-explorer/__tests__/ContentExplorer.test.tsx (1)

509-541: Great coverage of dual onSortChange paths; consider asserting call order.
Ensures internal fires before external.

                 await userEvent.click(industryHeader);

                 // Internal callback gets trimmed version for API calls
                 expect(mockOnSortChangeBUIE).toHaveBeenCalledWith('industry', 'ASC');

                 // User callback gets full column ID with direction
                 expect(mockOnSortChangeConsumer).toHaveBeenCalledWith({
                     column: 'metadata.enterprise_0.templateName.industry',
                     direction: 'ascending',
                 });
+                // Verify call order: internal before external
+                expect(mockOnSortChangeBUIE.mock.invocationCallOrder[0])
+                    .toBeLessThan(mockOnSortChangeConsumer.mock.invocationCallOrder[0]);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 5791e15 and 66128b4.

📒 Files selected for processing (6)
  • src/api/Metadata.js (2 hunks)
  • src/elements/content-explorer/Content.tsx (1 hunks)
  • src/elements/content-explorer/ContentExplorer.tsx (3 hunks)
  • src/elements/content-explorer/MetadataViewContainer.tsx (4 hunks)
  • src/elements/content-explorer/__tests__/ContentExplorer.test.tsx (2 hunks)
  • src/elements/content-explorer/stories/tests/MetadataView-visual.stories.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/elements/content-explorer/Content.tsx
  • src/elements/content-explorer/ContentExplorer.tsx
  • src/elements/content-explorer/stories/tests/MetadataView-visual.stories.tsx
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-27T15:25:53.190Z
Learnt from: jpan-box
PR: box/box-ui-elements#4248
File: src/elements/content-explorer/MetadataViewContainer.tsx:30-44
Timestamp: 2025-08-27T15:25:53.190Z
Learning: In the Box API, there is an inconsistency where some endpoints require trimmed metadata field names (e.g., "industry") while others require the full field path (e.g., "metadata.enterprise_1515946.templateName.industry"). The trimMetadataFieldPrefix helper function in MetadataViewContainer.tsx correctly trims the field names for API endpoints that expect the shorter format.

Applied to files:

  • src/elements/content-explorer/MetadataViewContainer.tsx
🧬 Code graph analysis (2)
src/elements/content-explorer/MetadataViewContainer.tsx (1)
src/features/content-explorer/item-list/ItemList.js (1)
  • tableProps (201-201)
src/api/Metadata.js (1)
src/constants.js (2)
  • CACHE_PREFIX_METADATA (63-63)
  • CACHE_PREFIX_METADATA (63-63)
🪛 Biome (2.1.2)
src/api/Metadata.js

[error] 99-99: Type annotations are a TypeScript only feature. Convert your file to a TypeScript file or remove the syntax.

TypeScript only syntax

(parse)


[error] 99-99: return type annotation are a TypeScript only feature. Convert your file to a TypeScript file or remove the syntax.

TypeScript only syntax

(parse)


[error] 350-350: Type annotations are a TypeScript only feature. Convert your file to a TypeScript file or remove the syntax.

TypeScript only syntax

(parse)


[error] 350-350: return type annotation are a TypeScript only feature. Convert your file to a TypeScript file or remove the syntax.

TypeScript only syntax

(parse)


[error] 351-351: type annotation are a TypeScript only feature. Convert your file to a TypeScript file or remove the syntax.

TypeScript only syntax

(parse)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: lint_test_build
  • GitHub Check: Summary
🔇 Additional comments (7)
src/elements/content-explorer/MetadataViewContainer.tsx (5)

4-6: Imports look correct.
Type additions for Key and SortDescriptor align with the new handler signature.


30-44: Prefix-trim helper is appropriate here.
Using the retrieved learning about API inconsistencies, trimming to the field key for the internal path is valid. Keep this localized to places that explicitly require trimmed keys.


75-77: Public prop surface is clear.
Prop doc clarifies internal usage; signature matches downstream expectations.


84-84: Good aliasing to onSortChangeInternal.
Improves readability alongside onSortChangeExternal.


167-174: Pass-through props look good.
items/columns/actionBar/tableProps wired as expected.

src/api/Metadata.js (1)

93-102: Cache key helper LGTM.
Prefix and shape are consistent with existing cache keys.

src/elements/content-explorer/__tests__/ContentExplorer.test.tsx (1)

457-457: Use of allowsSorting is correct.
Matches RAC prop name across columns.

Also applies to: 465-465

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/elements/content-explorer/__tests__/ContentExplorer.test.tsx (1)

509-541: Dual onSortChange test is solid—tighten it up with call counts and payload verification

Lock this down so regressions don’t sneak in. I pity the fool who lets flaky sorts slide!

  • Assert single invocations for both callbacks.
  • Verify the MQ payload used the trimmed field key ('industry').
  • Optional: add a caching test to ensure rapid clicks don’t refetch the schema.

Apply within this test:

@@
                 // Internal callback gets trimmed version for API calls
                 expect(mockOnSortChangeInternal).toHaveBeenCalledWith('industry', 'ASC');
+                expect(mockOnSortChangeInternal).toHaveBeenCalledTimes(1);
@@
                 // User callback gets full column ID with direction
                 expect(mockOnSortChangeExternal).toHaveBeenCalledWith({
                     column: 'metadata.enterprise_0.templateName.industry',
                     direction: 'ascending',
                 });
+                expect(mockOnSortChangeExternal).toHaveBeenCalledTimes(1);
+
+                // Verify the Metadata Query payload used the trimmed field key
+                const xhrCtor = (Xhr as unknown as jest.Mock);
+                const xhrInstance = xhrCtor.mock.instances[0];
+                const postMock = xhrInstance.post as jest.Mock;
+                const mqArg = postMock.mock.calls.find(([arg]) =>
+                    arg?.url === 'https://api.box.com/2.0/metadata_queries/execute_read',
+                )?.[0];
+                expect(mqArg?.data?.sort_by?.[0]?.field_key).toBe('industry');

Add this import at the top of the file:

import Xhr from '../../../utils/Xhr';

Optional caching test addition:

test('should not refetch template schema on rapid header clicks (cached)', async () => {
    renderComponent(metadataViewV2ElementProps);

    const industryHeader = await screen.findByRole('columnheader', { name: 'Industry' });
    await userEvent.click(industryHeader);
    await userEvent.click(industryHeader);
    await userEvent.click(industryHeader);

    const xhrCtor = (Xhr as unknown as jest.Mock);
    const xhrInstance = xhrCtor.mock.instances[0];
    const getMock = xhrInstance.get as jest.Mock;
    const schemaCalls = getMock.mock.calls.filter(([arg]) =>
        arg?.url === 'https://api.box.com/2.0/metadata_templates/enterprise/templateName/schema',
    );
    expect(schemaCalls.length).toBe(1);
});
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP (Model Context Protocol) integration is disabled
  • Jira integration is disabled
  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 66128b4 and 573134f.

📒 Files selected for processing (1)
  • src/elements/content-explorer/__tests__/ContentExplorer.test.tsx (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: lint_test_build
  • GitHub Check: Summary
🔇 Additional comments (1)
src/elements/content-explorer/__tests__/ContentExplorer.test.tsx (1)

457-457: allowsSorting rename wired correctly

Nice alignment with the V2 props on both name and metadata columns. Clean and clear, sucka.

Also applies to: 465-465

@mergify mergify bot merged commit 1a8a59f into box:master Aug 28, 2025
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants