Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Render summary fields in modal sidebar #4851

Merged
merged 5 commits into from
Sep 27, 2024

Conversation

benjaminpkane
Copy link
Contributor

@benjaminpkane benjaminpkane commented Sep 26, 2024

What changes are proposed in this pull request?

Before

Screenshot 2024-09-26 at 10 59 31 AM

After

Screenshot 2024-09-26 at 10 59 09 AM

How is this patch tested? If it is not, please explain why.

e2e spec

What areas of FiftyOne does this PR affect?

  • App: FiftyOne application changes
  • Build: Build and test infrastructure changes
  • Core: Core fiftyone Python library changes
  • Documentation: FiftyOne documentation changes
  • Other

Summary by CodeRabbit

  • New Features

    • Enhanced formatting logic for displaying values in the sidebar component.
    • New method added to verify object properties in sidebar tests.
    • Introduction of end-to-end tests for summary fields in the dataset.
  • Bug Fixes

    • Improved handling of null and undefined values in formatting functions.
  • Documentation

    • Updated type safety and clarity in various utility functions and components.

@benjaminpkane benjaminpkane added the app Issues related to App features label Sep 26, 2024
@benjaminpkane benjaminpkane self-assigned this Sep 26, 2024
Copy link
Contributor

coderabbitai bot commented Sep 26, 2024

Walkthrough

The changes involve updates to several components and utilities within the application, focusing on enhancing type safety, improving formatting logic, and refining component structure and styling. Key modifications include the introduction of new formatting functions in PathValueEntry.tsx, adjustments to import orders and logic in schema.ts, and enhancements to the formatPrimitive function in index.ts. Additionally, new testing functionalities have been added to support end-to-end tests for summary fields.

Changes

File Path Change Summary
app/packages/core/src/components/Sidebar/Entries/PathValueEntry.tsx Introduced new formatting functions for handling different data types and improved type safety.
app/packages/state/src/recoil/schema.ts Reordered imports and modified logic in isOfDocumentFieldList to enhance clarity and robustness.
app/packages/utilities/src/index.ts Adjusted exports and introduced a new Primitive type, streamlining the formatPrimitive function.
e2e-pw/src/oss/poms/modal/modal-sidebar.ts Added verifyObject method to the SidebarAsserter class for improved assertions in tests.
e2e-pw/src/oss/specs/smoke-tests/summary-fields.spec.ts Introduced a new test file for end-to-end testing of summary fields in a dataset.

Possibly related PRs

Suggested labels

bug

Suggested reviewers

  • brimoor
  • sashankaryal
  • minhtuev

🐰 In the code, I hop and play,
Formatting values in a new way.
With types so safe, and logic clear,
The sidebar shines, oh what a cheer!
Tests now dance, all snug and bright,
In our code garden, everything feels right! 🌼


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ 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.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai 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:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

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

Documentation and Community

  • 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.

@benjaminpkane benjaminpkane changed the base branch from develop to release/v1.0.0 September 26, 2024 15:06
@benjaminpkane benjaminpkane requested review from a team and minhtuev September 26, 2024 18:11
@benjaminpkane benjaminpkane requested review from a team and sashankaryal September 27, 2024 16:02
@benjaminpkane benjaminpkane marked this pull request as ready for review September 27, 2024 16:02
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: 4

🧹 Outside diff range and nitpick comments (4)
app/packages/utilities/src/index.ts (1)

635-651: Approve changes with suggestion: Improve type handling

The updates to formatPrimitive enhance type safety and improve code clarity. However, there are a few areas that could be further improved:

  1. The use of // @ts-ignore comments suggests potential type issues. Consider using type guards or type assertions to handle these cases more safely.

  2. For the DATE_FIELD and DATE_TIME_FIELD cases, you could create a type guard to check if value is of type { datetime: number } instead of using type assertions.

Consider refactoring the function to improve type safety:

function isDateTimeValue(value: Primitive): value is { datetime: number } {
  return typeof value === 'object' && value !== null && 'datetime' in value;
}

export const formatPrimitive = ({
  ftype,
  timeZone,
  value,
}: {
  ftype: string;
  timeZone: string;
  value: Primitive;
}) => {
  if (value === null || value === undefined) return null;

  switch (ftype) {
    case FRAME_SUPPORT_FIELD:
      return Array.isArray(value) ? `[${value[0]}, ${value[1]}]` : null;
    case DATE_FIELD:
      return isDateTimeValue(value) ? formatDate(value.datetime) : null;
    case DATE_TIME_FIELD:
      return isDateTimeValue(value) ? formatDateTime(value.datetime, timeZone) : null;
  }

  return prettify(value);
};

This refactoring eliminates the need for // @ts-ignore comments and provides better type safety.

app/packages/state/src/recoil/schema.ts (1)

789-793: Improved robustness, but consider a minor enhancement

The changes to the isOfDocumentFieldList selector improve its robustness by handling the case when there is no parent path. This prevents potential errors and aligns with TypeScript best practices.

Consider extracting the parent path calculation to a separate variable for improved readability:

-  const parent = path.split(".").slice(0, -1).join(".");
-  if (!parent) {
+  const parentPath = path.split(".").slice(0, -1).join(".");
+  if (!parentPath) {
     return false;
   }
-  const f = get(field(parent));
+  const f = get(field(parentPath));

This minor change would make the code slightly more self-documenting and easier to understand at a glance.

app/packages/core/src/components/Sidebar/Entries/PathValueEntry.tsx (2)

289-291: Key attribute uses toString() for better type consistency.

The key attribute in the mapped div elements now uses i.toString() instead of just i. This ensures type consistency and avoids potential issues with key uniqueness.


323-323: Key attribute uses toString() for better type consistency.

The key attribute in the div element now uses i.toString() instead of just i. This ensures type consistency and avoids potential issues with key uniqueness.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 0b928cf and bad935e.

📒 Files selected for processing (5)
  • app/packages/core/src/components/Sidebar/Entries/PathValueEntry.tsx (10 hunks)
  • app/packages/state/src/recoil/schema.ts (2 hunks)
  • app/packages/utilities/src/index.ts (2 hunks)
  • e2e-pw/src/oss/poms/modal/modal-sidebar.ts (1 hunks)
  • e2e-pw/src/oss/specs/smoke-tests/summary-fields.spec.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
app/packages/core/src/components/Sidebar/Entries/PathValueEntry.tsx (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/state/src/recoil/schema.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

app/packages/utilities/src/index.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

e2e-pw/src/oss/poms/modal/modal-sidebar.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

e2e-pw/src/oss/specs/smoke-tests/summary-fields.spec.ts (1)

Pattern **/*.{ts,tsx}: Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

🔇 Additional comments (14)
app/packages/utilities/src/index.ts (1)

621-626: LGTM: New Primitive type improves type safety

The new Primitive type alias enhances type safety for the formatPrimitive function. It accurately represents the possible types that can be formatted, including date-time values.

e2e-pw/src/oss/specs/smoke-tests/summary-fields.spec.ts (2)

6-13: Well-structured extension of test fixtures

The extension of the base test with the grid and modal fixtures is correctly implemented. This enhances modularity and reusability, adhering to best practices in test design.


54-57: Accurate verification of summary fields

The assertions for verifying the summary and summaries fields are correctly implemented. This ensures that the expected data is rendered in the modal sidebar, aligning with the test objectives.

Also applies to: 64-68

e2e-pw/src/oss/poms/modal/modal-sidebar.ts (1)

129-139: LGTM: New method implements object verification correctly

The verifyObject method correctly verifies the key-value pairs in the sidebar entries.

app/packages/core/src/components/Sidebar/Entries/PathValueEntry.tsx (10)

3-8: Imports are organized and use proper types.

The import statements have been updated to include the necessary types (Primitive and Schema) and constants (EMBEDDED_DOCUMENT_FIELD, formatPrimitive, makePseudoField). This improves type safety and code organization.


59-62: Styling for links is consistent with the theme.

The new styles for links within ScalarDiv use the primary text color from the theme. This ensures consistency in link styling across the application.


128-130: Flexbox properties enhance the layout of the list container.

The addition of flexbox properties (display: flex, flex-direction: column, row-gap: 0.5rem) to ListContainer improves the layout and spacing of list items. This enhances the readability and visual structure of the list.


200-200: Data attribute improves testability.

The data-cy attribute has been added to the arrow icon, enabling more precise targeting of this element in end-to-end tests. This improves the testability and maintainability of the component.


241-257: Type safety and formatting logic improved in ListLoadable.

The ListLoadable component now uses Primitive[] instead of unknown[] for the data type, enhancing type safety. The format function is used to handle the formatting of values based on their field type, ensuring consistent formatting across different data types.


259-265: Empty state handling simplified in ListLoadable.

The handling of empty states in ListLoadable has been simplified by removing the fragment wrapper around the "No results" message. This improves code readability without affecting functionality.


312-312: Formatting logic extracted to format function.

The formatting logic for the slice value has been extracted to the format function, which takes the ftype, value, and timeZone as parameters. This improves code organization and reusability.


382-390: Formatting logic extracted to format function and memoized.

The formatting logic has been extracted to the format function, which takes the fields, ftype, timeZone, and value as parameters. The result is memoized using useMemo to avoid unnecessary re-computations. This improves performance and code organization.


395-395: Event propagation stopped for better control.

The onKeyDown event handler now stops event propagation using e.stopPropagation(). This prevents the event from bubbling up to parent elements, providing better control over event handling.


471-545: New formatting functions improve code organization and reusability.

Several new formatting functions have been introduced:

  • format: Handles the formatting of values based on their field type, including a new check for EMBEDDED_DOCUMENT_FIELD to format objects differently.
  • formatPrimitiveOrURL: Manages the rendering of URLs, allowing them to be displayed as clickable links.
  • formatObject: Formats embedded documents by iterating over their entries and applying the appropriate formatting to each key-value pair.

These functions improve code organization, reusability, and maintainability by encapsulating specific formatting logic and allowing for easier extension and modification.

Comment on lines +19 to +41
await fiftyoneLoader.executePythonCode(`
import fiftyone as fo

dataset = fo.Dataset("${datasetName}")
dataset.persistent = True
dataset.add_sample(
fo.Sample(
filepath=f"image.png",
summary=fo.DynamicEmbeddedDocument(one="two", three="four"),
summaries=[
fo.DynamicEmbeddedDocument(five="six", seven="eight"),
fo.DynamicEmbeddedDocument(nine="ten"),
],
)
)
dataset.app_config.sidebar_groups = [
fo.SidebarGroupDocument(
name="summaries", paths=["summary", "summaries"], expanded=True
)
]
dataset.save()
dataset.add_dynamic_sample_fields()
`);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Ensure safe interpolation of datasetName in Python code

While interpolating datasetName into the Python code string, there's a minimal risk of code injection if datasetName contains unintended characters. Although datasetName is generated internally, it's good practice to sanitize or validate variables before injecting them into executable code to prevent potential security issues.

Consider explicitly validating or sanitizing datasetName before interpolation:

const sanitizedDatasetName = datasetName.replace(/[^a-zA-Z0-9_\-]/g, "");

And then use sanitizedDatasetName in your Python code.

Comment on lines +44 to +69
test("modal sidebar summary fields render", async ({
eventUtils,
fiftyoneLoader,
grid,
modal,
page,
}) => {
await fiftyoneLoader.waitUntilGridVisible(page, datasetName);
await grid.openFirstSample();
await modal.waitForSampleLoadDomAttribute(true);
await modal.sidebar.assert.verifyObject("summary", {
one: "two",
three: "four",
});
const entryExpandPromise = eventUtils.getEventReceivedPromiseForPredicate(
"animation-onRest",
() => true
);
await modal.sidebar.clickFieldDropdown("summaries");
await entryExpandPromise;
await modal.sidebar.assert.verifyObject("summaries", {
five: "six",
seven: "eight",
nine: "ten",
});
});
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add cleanup logic to remove the dataset after tests

The test creates a persistent dataset in the beforeAll hook but does not remove it after execution. This could lead to leftover test data interfering with other tests or cluttering the environment.

Implement an afterAll hook to delete the dataset post-testing:

test.afterAll(async ({ fiftyoneLoader }) => {
  await fiftyoneLoader.executePythonCode(`
    import fiftyone as fo
    if fo.dataset_exists("${datasetName}"):
        dataset = fo.load_dataset("${datasetName}")
        dataset.delete()
  `);
});

Comment on lines +58 to +63
const entryExpandPromise = eventUtils.getEventReceivedPromiseForPredicate(
"animation-onRest",
() => true
);
await modal.sidebar.clickFieldDropdown("summaries");
await entryExpandPromise;
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Refine event predicate to prevent flaky tests

The predicate function () => true used in getEventReceivedPromiseForPredicate will resolve on any animation-onRest event, which may not be specific to the desired interaction. This could cause the test to pass prematurely if multiple animations occur.

Refine the predicate to ensure it matches the specific element or context:

const entryExpandPromise = eventUtils.getEventReceivedPromiseForPredicate(
  "animation-onRest",
  (event) => event.target === expectedTargetElement
);

Replace expectedTargetElement with a reference to the element associated with the animation you intend to wait for.

Comment on lines +132 to +134
for (const k in obj) {
const v = obj[k];
const entry = locator.getByTestId(`key-value-${k}-${v}`);
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Use Object.entries with for...of to iterate over object properties

Using for...in can iterate over inherited enumerable properties, which might not be desired. It's safer to use Object.entries(obj) with a for...of loop to iterate over the object's own properties.

Apply this diff to implement the change:

-    for (const k in obj) {
-      const v = obj[k];
+    for (const [k, v] of Object.entries(obj)) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for (const k in obj) {
const v = obj[k];
const entry = locator.getByTestId(`key-value-${k}-${v}`);
for (const [k, v] of Object.entries(obj)) {
const entry = locator.getByTestId(`key-value-${k}-${v}`);

Copy link
Contributor

@sashankaryal sashankaryal left a comment

Choose a reason for hiding this comment

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

lgtm! love the tests

Comment on lines +621 to +627
export type Primitive =
| number
| null
| string
| undefined
| { datetime: number };

Copy link
Contributor

Choose a reason for hiding this comment

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

👌
(nit: although FoPrimitive would probably make it less confusing elsewhere)

@benjaminpkane benjaminpkane merged commit 81037fa into release/v1.0.0 Sep 27, 2024
11 checks passed
@benjaminpkane benjaminpkane deleted the modal-summary-fields branch September 27, 2024 17:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
app Issues related to App features
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants