Skip to content

Conversation

@BrunaCubos
Copy link
Collaborator

@BrunaCubos BrunaCubos commented Nov 10, 2025

Tasks https://vtex-dev.atlassian.net/jira/software/projects/BE/boards/2868?selectedIssue=BE-69 BE-70

Here's the completed PR description in English:

  • Introduced FileUploadCard for file selection and upload functionality, including drag-and-drop support.
  • Added FileUploadStatus to display upload progress and error handling.
  • Updated index files to export new components and types.
  • Enhanced SearchInputField to include an attachment button that triggers the file upload card.
  • Added styles for both new components to ensure proper UI integration.

What's the purpose of this pull request?

This PR introduces a new file upload feature that allows users to perform bulk searches by uploading CSV files. The feature provides a modern, user-friendly interface with drag-and-drop support, real-time upload status tracking, and comprehensive error handling to guide users through the file upload process.

How it works?

FileUploadCard Component

  • File Selection: Users can either click "Select file" button or drag-and-drop files into the dropzone
  • Drag & Drop: Visual feedback with border color and background changes when dragging files
  • File Input: Hidden file input that's always present in the DOM to ensure file picker works in all states
  • Download Template: Provides a CSV template download option for users who need guidance on file format
  • Validation: Only accepts CSV files (.csv extension)
  • States: Manages upload states (uploading, completed, error) with appropriate UI updates

FileUploadStatus Component

Displays the current status of the uploaded file with three distinct states:

  1. Uploading State

    • Shows spinning CircleNotch icon
    • Displays "Uploading your file..." message
    • Shows filename
  2. Completed State

    • Shows Table icon in green background
    • Displays filename and file size
    • Shows "Search" button to proceed with the uploaded file
  3. Error State

    • Shows WarningOctagon icon in red background
    • Special styling: light pink background (#FDF6F5) with red border (#FFDFD9)
    • Displays error title and description in two lines
    • Shows two action buttons: "Download template" and "Select file"

Error Types

The component handles six different error scenarios:

  • Unsupported: Wrong file type (non-CSV files)
  • Unreadable: File can't be parsed or is corrupted
  • Invalid Structure: Missing headers or incorrect columns
  • Empty: File has no data
  • Too Large: File exceeds size limit
  • Unexpected: Generic error for unknown issues

Integration

  • Added attachment icon button to SearchInputField component
  • Clicking the attachment button toggles the FileUploadCard visibility
  • Card appears as a dropdown below the search input
  • Smooth animations for show/hide transitions

Key Features

  • File validation: Automatically checks file type on selection/drop
  • Error recovery: Users can easily select a new file or download template when errors occur
  • Responsive design: Components adapt to container width
  • Accessibility: Proper ARIA labels and keyboard navigation support
  • Visual feedback: Icons, colors, and animations provide clear status indication

How to test it?

Basic File Upload Flow

  1. Navigate to a page with the search input
  2. Click the attachment icon button (Paperclip icon)
  3. The FileUploadCard should appear below the search input
  4. Click "Select file" or drag-and-drop a CSV file
  5. Observe the uploading state with spinning icon
  6. After 2 seconds, see the completed state with "Search" button
  7. Click the X button to remove the file and return to initial state

Drag and Drop

  1. Open the FileUploadCard
  2. Drag a CSV file over the dropzone
  3. Observe the visual feedback (border and background color change)
  4. Drop the file to upload

Error Handling - Unsupported File Type

  1. Open the FileUploadCard
  2. Select or drop a non-CSV file (e.g., .txt, .pdf, .xlsx)
  3. Observe error state with red icon and pink background
  4. Read error message: "Unsupported file type." / "Upload a CSV or use the template provided."
  5. Click "Download template" to get a sample CSV
  6. Click "Select file" to try uploading again

File Removal

  1. Upload a file successfully
  2. Click the X button in the top-right of the file status
  3. Component should return to initial dropzone state

Starters Deploy Preview

References

Checklist

You may erase this after checking them all 😉

PR Title and Commit Messages

  • PR title and commit messages follow the Conventional Commits specification
    • Available prefixes: feat, fix, chore, docs, style, refactor, ci and test

PR Description

  • Added a label according to the PR goal - breaking change, bug, contributing, performance, documentation..

Dependencies

  • Committed the pnpm-lock.yaml file when there were changes to the packages

Documentation

  • PR description

Summary by CodeRabbit

Release Notes

  • New Features

    • File upload card component enabling file selection via drag-and-drop or file picker with status indicators
    • File upload status component displaying upload progress, success, and error states with customizable error handling
    • Attachment button in search input for quick file upload access
  • Chores

    • Added component styling and Storybook demonstrations

✏️ Tip: You can customize this high-level summary in your review settings.

@BrunaCubos BrunaCubos requested a review from a team as a code owner November 10, 2025 19:15
@BrunaCubos BrunaCubos requested review from lariciamota and lemagnetic and removed request for a team November 10, 2025 19:15
@codesandbox-ci
Copy link

codesandbox-ci bot commented Nov 10, 2025

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

Comment on lines 43 to 46
// const UISearchInputField = dynamic<UISearchInputFieldProps & any>(() =>
// /* webpackChunkName: "UISearchInputField" */
// import('@faststore/ui').then((module) => module.SearchInputField)
// )
Copy link
Contributor

Choose a reason for hiding this comment

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

Mudar a forma de importação

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ajustado!


&:focus-visible {
border-radius: var(--fs-border-radius-small);
outline: 2px solid var(--fs-color-focus-ring);
Copy link
Contributor

Choose a reason for hiding this comment

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

Devemos evitar usar px, usamos os tokens e quando não conseguimos, rem

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ajustado!

Copy link
Contributor

@ArthurTriis1 ArthurTriis1 left a comment

Choose a reason for hiding this comment

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

O projeto da FS conta com storybook, pode adicionar uma versão simples para ajudar na revisão de novos componentes?

@BrunaCubos BrunaCubos changed the base branch from main to feat/quick-order-by-file-feature-branch November 21, 2025 13:53
Comment on lines +66 to +51
height: 285px;
max-height: 285px;
Copy link
Contributor

Choose a reason for hiding this comment

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

Converter PX para rem, aqui e ao longo do código, exceto os stories

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ajustado!

Copy link
Contributor

@ArthurTriis1 ArthurTriis1 left a comment

Choose a reason for hiding this comment

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

Aprovado, muito bom o resultado dos componentes e o storybook ajudou na correção. Deixei alguns comentários, favor corrigir antes do merge

Copy link
Contributor

@lucasfp13 lucasfp13 left a comment

Choose a reason for hiding this comment

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

Deixei algumas sugestões de ajustes, mas em relação à funcionalidade me parece ok.


const isValidFileType = (file: File): boolean => {
const fileName = file.name.toLowerCase()
const validExtensions = ['.csv']
Copy link
Contributor

Choose a reason for hiding this comment

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

As extensões válidas que o componente aceita, por padrão, são .csv, .xlsx e .xls, mas nesse array apenas a .csv está presente. lsso é intencional ou deveria ter as outras também?

Copy link
Contributor

Choose a reason for hiding this comment

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

Devemos manter apenas .csv

Copy link
Contributor

Choose a reason for hiding this comment

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

🎗️ Mudar o accept que é passado pro input para aceitar apenas csv

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ajustado!

aria-hidden={!isOpen}
{...otherProps}
>
<input
Copy link
Contributor

Choose a reason for hiding this comment

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

sugestão: Importar e usar aqui o átomo Input que temos no @faststore/ui

Suggested change
<input
<Input

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ajustado!

Select file
</Button>

<button
Copy link
Contributor

Choose a reason for hiding this comment

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

Tem algum motivo específico de não usar o Button do @faststore/ui aqui também?

Suggested change
<button
<Button

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ajustado!

...otherProps
}: FileUploadStatusProps) => {
const formatFileSize = (bytes: number): string => {
return `${(bytes / 1024).toFixed(0)} KB` //ADJUST ONCE INTEGRATED
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
return `${(bytes / 1024).toFixed(0)} KB` //ADJUST ONCE INTEGRATED
return `${(bytes / 1024).toFixed(0)} KB` // TODO: ADJUST ONCE INTEGRATED

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ajustado!

</div>

{onRemove && (
<button
Copy link
Contributor

Choose a reason for hiding this comment

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

Tem algum motivo específico de não usar o Button do @faststore/ui aqui também?

Suggested change
<button
<Button

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ajustado!

ref={ref}
buttonProps={buttonProps}
placeholder={placeholder}
showAttachmentButton={true}
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
showAttachmentButton={true}
showAttachmentButton

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ajustado!

Comment on lines 6 to 25
--fs-file-upload-card-padding: var(--fs-spacing-3);
--fs-file-upload-card-bkg-color: var(--fs-color-neutral-0);
--fs-file-upload-card-border-width: var(--fs-border-width);
--fs-file-upload-card-border-color: var(--fs-border-color-light);
--fs-file-upload-card-border-radius: var(--fs-border-radius-medium);

--fs-file-upload-card-dropzone-padding:
var(--fs-spacing-3)
var(--fs-spacing-4);
--fs-file-upload-card-dropzone-border-width: var(--fs-border-width-thick);
--fs-file-upload-card-dropzone-border-color: var(--fs-border-color-light);
--fs-file-upload-card-dropzone-border-radius: var(--fs-border-radius);

--fs-file-upload-card-icon-size: 3rem;
--fs-file-upload-card-badge-size: 1.5rem;
--fs-file-upload-card-badge-bg-color: #0366dd;
--fs-file-upload-card-shadow-color: #bfdbfe;

--fs-file-upload-card-title-color: var(--fs-color-text);
--fs-file-upload-card-link-color: var(--fs-color-link);
Copy link
Contributor

Choose a reason for hiding this comment

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

Apenas ajustando a indentação.

Suggested change
--fs-file-upload-card-padding: var(--fs-spacing-3);
--fs-file-upload-card-bkg-color: var(--fs-color-neutral-0);
--fs-file-upload-card-border-width: var(--fs-border-width);
--fs-file-upload-card-border-color: var(--fs-border-color-light);
--fs-file-upload-card-border-radius: var(--fs-border-radius-medium);
--fs-file-upload-card-dropzone-padding:
var(--fs-spacing-3)
var(--fs-spacing-4);
--fs-file-upload-card-dropzone-border-width: var(--fs-border-width-thick);
--fs-file-upload-card-dropzone-border-color: var(--fs-border-color-light);
--fs-file-upload-card-dropzone-border-radius: var(--fs-border-radius);
--fs-file-upload-card-icon-size: 3rem;
--fs-file-upload-card-badge-size: 1.5rem;
--fs-file-upload-card-badge-bg-color: #0366dd;
--fs-file-upload-card-shadow-color: #bfdbfe;
--fs-file-upload-card-title-color: var(--fs-color-text);
--fs-file-upload-card-link-color: var(--fs-color-link);
--fs-file-upload-card-padding: var(--fs-spacing-3);
--fs-file-upload-card-bkg-color: var(--fs-color-neutral-0);
--fs-file-upload-card-border-width: var(--fs-border-width);
--fs-file-upload-card-border-color: var(--fs-border-color-light);
--fs-file-upload-card-border-radius: var(--fs-border-radius-medium);
--fs-file-upload-card-dropzone-padding: var(--fs-spacing-3) var(--fs-spacing-4);
--fs-file-upload-card-dropzone-border-width: var(--fs-border-width-thick);
--fs-file-upload-card-dropzone-border-color: var(--fs-border-color-light);
--fs-file-upload-card-dropzone-border-radius: var(--fs-border-radius);
--fs-file-upload-card-icon-size: 3rem;
--fs-file-upload-card-badge-size: 1.5rem;
--fs-file-upload-card-badge-bg-color: #0366dd;
--fs-file-upload-card-shadow-color: #bfdbfe;
--fs-file-upload-card-title-color: var(--fs-color-text);
--fs-file-upload-card-link-color: var(--fs-color-link);

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ajustado!

Comment on lines 6 to 19
--fs-file-upload-status-padding: var(--fs-spacing-0);
--fs-file-upload-status-gap: var(--fs-spacing-3);
--fs-file-upload-status-bkg-color: var(--fs-color-neutral-0);

--fs-file-upload-status-file-info-padding: var(--fs-spacing-3);
--fs-file-upload-status-file-info-border-color: var(--fs-border-color-light);
--fs-file-upload-status-file-info-border-radius: 1.25rem;

--fs-file-upload-status-icon-size: var(--fs-spacing-6);
--fs-file-upload-status-icon-bg-completed: #08a822;
--fs-file-upload-status-icon-bg-uploading: #08a822;
--fs-file-upload-status-icon-bg-error: #d31a15;

--fs-file-upload-status-filename-color: var(--fs-color-text);
--fs-file-upload-status-text-color: var(--fs-color-text-light);

--fs-file-upload-status-button-width: 100%;
--fs-file-upload-status-button-height: var(--fs-spacing-7);
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
--fs-file-upload-status-padding: var(--fs-spacing-0);
--fs-file-upload-status-gap: var(--fs-spacing-3);
--fs-file-upload-status-bkg-color: var(--fs-color-neutral-0);
--fs-file-upload-status-file-info-padding: var(--fs-spacing-3);
--fs-file-upload-status-file-info-border-color: var(--fs-border-color-light);
--fs-file-upload-status-file-info-border-radius: 1.25rem;
--fs-file-upload-status-icon-size: var(--fs-spacing-6);
--fs-file-upload-status-icon-bg-completed: #08a822;
--fs-file-upload-status-icon-bg-uploading: #08a822;
--fs-file-upload-status-icon-bg-error: #d31a15;
--fs-file-upload-status-filename-color: var(--fs-color-text);
--fs-file-upload-status-text-color: var(--fs-color-text-light);
--fs-file-upload-status-button-width: 100%;
--fs-file-upload-status-button-height: var(--fs-spacing-7);
--fs-file-upload-status-padding: var(--fs-spacing-0);
--fs-file-upload-status-gap: var(--fs-spacing-3);
--fs-file-upload-status-bkg-color: var(--fs-color-neutral-0);
--fs-file-upload-status-file-info-padding: var(--fs-spacing-3);
--fs-file-upload-status-file-info-border-color: var(--fs-border-color-light);
--fs-file-upload-status-file-info-border-radius: 1.25rem;
--fs-file-upload-status-icon-size: var(--fs-spacing-6);
--fs-file-upload-status-icon-bg-completed: #08a822;
--fs-file-upload-status-icon-bg-uploading: #08a822;
--fs-file-upload-status-icon-bg-error: #d31a15;
--fs-file-upload-status-filename-color: var(--fs-color-text);
--fs-file-upload-status-text-color: var(--fs-color-text-light);
--fs-file-upload-status-button-width: 100%;
--fs-file-upload-status-button-height: var(--fs-spacing-7);

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ajustado!
Estranho que o proprio lint quando commita remove os espaços! Mas nao commitei ele ajustado.

items?: Value
postalCode?: Value
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Oie!
Acredito que esteja faltando rebase nas branchs dessa feature, porque está misturado com mudanças que já estão na main da faststore. Essa do jwt, por exemplo.

BrunaCubos and others added 5 commits January 14, 2026 11:50
- Introduced `FileUploadCard` for file selection and upload functionality, including drag-and-drop support.
- Added `FileUploadStatus` to display upload progress and error handling.
- Updated index files to export new components and types.
- Enhanced `SearchInputField` to include an attachment button that triggers the file upload card.
- Added styles for both new components to ensure proper UI integration.
- Replaced native input elements with custom Input and Button components for consistency.
- Simplified accepted file types in FileUploadCard to only '.csv'.
- Added TODO comment for file size formatting in FileUploadStatus.
- Adjusted SCSS variable formatting for improved readability.
@coderabbitai
Copy link

coderabbitai bot commented Jan 14, 2026

📝 Walkthrough

Walkthrough

This PR introduces FileUploadCard and FileUploadStatus React components with drag-and-drop file selection, state management for upload workflows, and integration into SearchInput. Includes comprehensive styling, public API exports, and Storybook demonstrations.

Changes

Cohort / File(s) Summary
FileUploadCard Component
packages/components/src/molecules/FileUploadCard/FileUploadCard.tsx, packages/components/src/molecules/FileUploadCard/index.tsx
New React component supporting file selection via input or drag-and-drop. Manages upload state (uploading/completed/error), provides callbacks (onFileSelect, onDownloadTemplate, onSearch, onDismiss), and renders conditional UI with FileUploadStatus when file is selected. Default template CSV download included.
FileUploadStatus Component
packages/components/src/molecules/FileUploadStatus/FileUploadStatus.tsx, packages/components/src/molecules/FileUploadStatus/index.tsx
New React component displaying file upload status with icon/state indicators. Exports FileUploadState type, FileUploadErrorType enum (unexpected/unsupported/unreadable/invalid-structure/empty/too-large), and FileUploadStatusProps interface. Renders error/success/uploading states with conditional action buttons (remove, search, download template, select file).
SearchInput Integration
packages/core/src/components/search/SearchInput/SearchInput.tsx, packages/components/src/molecules/SearchInputField/SearchInputField.tsx
SearchInput adds file upload flow with new state (fileUploadVisible, isUploadOpen, hasFile) and integrates FileUploadCard. SearchInputField adds new props: showAttachmentButton, attachmentButtonProps, attachmentButtonIcon to render attachment button with separator.
Public API Exports
packages/components/src/index.ts
Exports FileUploadCard, FileUploadCardProps, FileUploadStatus, FileUploadState, FileUploadErrorType, and FileUploadStatusProps from molecule modules.
Styling
packages/ui/src/components/molecules/FileUploadCard/styles.scss, packages/ui/src/components/molecules/FileUploadStatus/styles.scss, packages/ui/src/components/molecules/SearchInputField/styles.scss, packages/ui/src/styles/components.scss, packages/core/src/components/sections/Navbar/section.module.scss
New complete SCSS modules for FileUploadCard (card container, dropzone, drag states, files panel) and FileUploadStatus (state icons, animations, error styling). Updated SearchInputField styles to accommodate new attachment button/separator layout. Global imports added.
Storybook Stories
packages/storybook/stories/fileupload.stories.tsx
Six story exports demonstrating FileUploadCard (default, with toggle), FileUploadStatus states (uploading, completed, error, all error types), and SearchInputField with file upload integration. Includes template download and interaction logging.

Sequence Diagram

sequenceDiagram
    actor User
    participant Card as FileUploadCard
    participant Status as FileUploadStatus
    participant Handler as Parent Component
    
    User->>Card: Click "Select file" / Drag-drop
    Card->>Card: Validate file (extension/type)
    alt File valid
        Card->>Card: Set selectedFile & uploadState='uploading'
        Card->>Status: Render with uploading state
        User->>Status: Monitor upload progress
        Card->>Card: Simulate 2s upload delay
        Card->>Card: uploadState='completed'
        Status->>Status: Display success icon
        User->>Status: Click "Search" or "Remove"
        Status->>Handler: onSearch() / onRemove() callback
    else File invalid
        Card->>Card: Set uploadState='error'
        Card->>Status: Render with error state
        User->>Status: Click "Download template" or "Select file"
        Status->>Handler: onDownloadTemplate() / onSelectFile() callback
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Poem

🐰 With cards that catch files mid-flight,
And dropzones draped in soft delight,
Upload, revert, or start anew—
A rabbit hops through states of two!
Files dance where I command. 📁✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add FileUploadCard and FileUploadStatus components' clearly and specifically summarizes the main changes: two new components are being added to the codebase.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

@lariciamota
Copy link
Contributor

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Jan 20, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@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: 5

🤖 Fix all issues with AI agents
In `@packages/components/src/molecules/FileUploadCard/FileUploadCard.tsx`:
- Around line 108-111: The simulated upload setTimeout in FileUploadCard
currently calls setUploadState('completed') without cleanup, risking state
updates after unmount; modify the component to store the timer id (e.g., in a
ref) and set the timeout inside a useEffect that returns a cleanup which clears
the timeout, or cancel the timer in an existing unmount effect so setUploadState
is never called after the component unmounts.

In `@packages/components/src/molecules/FileUploadStatus/FileUploadStatus.tsx`:
- Around line 202-233: The action buttons in FileUploadStatus (the Search button
with data-fs-file-upload-status-search-button, and the Download template and
Select file buttons with data-fs-file-upload-status-download-button and
data-fs-file-upload-status-select-button) are missing an explicit type and will
default to submit when rendered inside a form; update each Button element
rendered under state === 'completed' (onSearch) and state === 'error'
(onDownloadTemplate, onSelectFile) to include type="button" so they do not
trigger form submission.

In `@packages/storybook/stories/fileupload.stories.tsx`:
- Around line 133-143: The handleDownloadTemplate function currently triggers a
download by clicking a detached anchor which can be ignored by some browsers
(e.g., Firefox); modify handleDownloadTemplate to append the created <a> element
to document.body before calling a.click(), then remove the anchor after the
click and revoke the object URL (keep creating the Blob, setting
a.href/a.download as-is) to improve cross-browser reliability.

In `@packages/ui/src/components/molecules/SearchInputField/styles.scss`:
- Around line 109-122: There is a duplicate CSS rule for the selector
[data-fs-search-input-field-input]; remove the second block (the one that sets
padding-right) and consolidate any unique properties (background-color,
transition, height, and the media query) into the first definition so the rule
uses padding-inline-end for RTL support; ensure the merged rule retains the
media query (`@include` media("<notebook") { border: 0; }) and the
transition/background-color/height settings from the duplicate block.
- Around line 69-78: Remove the orphaned CSS properties block (the standalone
rules: right, display, gap, align-items and the media query) that are present
without a selector; these are duplicate/leftover and will break SCSS. Locate the
stray block near the end of the file and delete it, keeping the existing valid
styling defined under the [data-fs-search-input-field-actions] selector (which
already defines the same rules), and ensure only the selector-scoped styles
remain.
🧹 Nitpick comments (8)
packages/core/src/components/search/SearchInput/SearchInput.tsx (2)

115-120: Incomplete file upload handling.

The handleFileSelect function sets state but has a TODO indicating the actual file upload logic is not implemented. The commented-out setFileUploadVisible(false) suggests uncertainty about the intended behavior.

Consider completing the implementation or tracking this as a follow-up task.

Would you like me to open an issue to track the implementation of the actual file upload logic?


212-219: Redundant isOpen prop condition.

The isOpen prop evaluates to isUploadOpen || hasFile || fileUploadVisible, but since this block only renders when fileUploadVisible is true, isOpen will always be true. The isUploadOpen and hasFile conditions have no effect here.

Simplify the condition
            {fileUploadVisible && (
              <FileUploadCard
-               isOpen={isUploadOpen || hasFile || fileUploadVisible}
+               isOpen={fileUploadVisible}
                onDismiss={() => setFileUploadVisible(false)}
                onFileSelect={handleFileSelect}
                onDownloadTemplate={handleDownloadTemplate}
              />
            )}

Or consider removing isUploadOpen and hasFile states if they're not needed elsewhere.

packages/ui/src/components/molecules/FileUploadStatus/styles.scss (2)

13-15: Hardcoded colors reduce theming flexibility.

The icon background colors use hardcoded hex values (#08a822, #d31a15) instead of design tokens. This makes it harder to maintain consistent theming across the application.

Consider using design tokens
-  --fs-file-upload-status-icon-bg-completed: `#08a822`;
-  --fs-file-upload-status-icon-bg-uploading: `#08a822`;
-  --fs-file-upload-status-icon-bg-error: `#d31a15`;
+  --fs-file-upload-status-icon-bg-completed: var(--fs-color-success, `#08a822`);
+  --fs-file-upload-status-icon-bg-uploading: var(--fs-color-success, `#08a822`);
+  --fs-file-upload-status-icon-bg-error: var(--fs-color-danger, `#d31a15`);

This applies to other hardcoded colors in this file as well (lines 48-49, 198-200, 220-226).


29-29: Remove commented-out code.

The commented height: 188px; should either be removed or documented if it serves as a reference.

packages/ui/src/components/molecules/FileUploadCard/styles.scss (2)

49-51: Use rem units for fixed height.

The fixed height of 285px should be converted to rem units for consistency with the design system. Based on past review comments, pixel units should be avoided except in stories.

Convert to rem
  &[data-fs-file-upload-card-open="true"] {
-   height: 285px;
-   max-height: 285px;
+   height: 17.8125rem;
+   max-height: 17.8125rem;
    pointer-events: auto;

17-18: Hardcoded colors reduce theming flexibility.

The badge and shadow colors use hardcoded hex values. Consider using design tokens for better maintainability and theming support.

Consider using design tokens
-  --fs-file-upload-card-badge-bg-color:         `#0366dd`;
-  --fs-file-upload-card-shadow-color:           `#bfdbfe`;
+  --fs-file-upload-card-badge-bg-color:         var(--fs-color-primary, `#0366dd`);
+  --fs-file-upload-card-shadow-color:           var(--fs-color-primary-bkg-light, `#bfdbfe`);

This pattern applies to other hardcoded colors in this file (lines 161, 189-196).

packages/components/src/molecules/FileUploadCard/FileUploadCard.tsx (1)

92-117: Duplicate file processing logic.

handleFileChange and handleDrop contain nearly identical file processing logic (validation, state updates, simulated upload). Extract this into a shared helper function.

Extract shared file processing logic
+  const processFile = (file: File, files: File[]) => {
+    setSelectedFile(file)
+
+    if (!isValidFileType(file)) {
+      setUploadState('error')
+      setErrorType('unsupported')
+      return
+    }
+
+    setUploadState('uploading')
+    setErrorType(undefined)
+
+    // Simulate upload process
+    setTimeout(() => {
+      setUploadState('completed')
+    }, 2000)
+
+    onFileSelect?.(files)
+  }
+
   const handleFileChange = (e: ChangeEvent<HTMLInputElement>) => {
     const files = Array.from(e.target.files || [])
     if (files.length > 0) {
-      const file = files[0]
-      setSelectedFile(file)
-
-      // Validate file type
-      if (!isValidFileType(file)) {
-        setUploadState('error')
-        setErrorType('unsupported')
-        return
-      }
-
-      setUploadState('uploading')
-      setErrorType(undefined)
-
-      // Simulate upload process
-      setTimeout(() => {
-        setUploadState('completed')
-      }, 2000)
-
-      if (onFileSelect) {
-        onFileSelect(files)
-      }
+      processFile(files[0], files)
     }
   }

Apply similar refactoring to handleDrop.

Also applies to: 130-160

packages/storybook/stories/fileupload.stories.tsx (1)

1-7: Use the public type export instead of a deep dist import.

Deep imports can break with package refactors or alternate build outputs. Prefer the root export if available; otherwise consider exporting FileUploadErrorType from the package index.

🔧 Proposed change
-import type { FileUploadErrorType } from '@faststore/components/dist/esm/molecules/FileUploadStatus'
+import type { FileUploadErrorType } from '@faststore/components'

Comment on lines +108 to +111
// Simulate upload process
setTimeout(() => {
setUploadState('completed')
}, 2000)
Copy link

@coderabbitai coderabbitai bot Jan 20, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Potential state update on unmounted component.

The setTimeout for simulating upload completion doesn't have cleanup logic. If the component unmounts before the 2-second timeout completes, React will warn about updating state on an unmounted component.

Add cleanup with useRef or useEffect
+  const uploadTimeoutRef = useRef<NodeJS.Timeout | null>(null)
+
+  useEffect(() => {
+    return () => {
+      if (uploadTimeoutRef.current) {
+        clearTimeout(uploadTimeoutRef.current)
+      }
+    }
+  }, [])
+
   // In processFile or handleFileChange:
-  setTimeout(() => {
+  uploadTimeoutRef.current = setTimeout(() => {
     setUploadState('completed')
   }, 2000)
🤖 Prompt for AI Agents
In `@packages/components/src/molecules/FileUploadCard/FileUploadCard.tsx` around
lines 108 - 111, The simulated upload setTimeout in FileUploadCard currently
calls setUploadState('completed') without cleanup, risking state updates after
unmount; modify the component to store the timer id (e.g., in a ref) and set the
timeout inside a useEffect that returns a cleanup which clears the timeout, or
cancel the timer in an existing unmount effect so setUploadState is never called
after the component unmounts.

Copy link
Contributor

Choose a reason for hiding this comment

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

We can ignore this comment since the setTimeout is only for testing purposes

Copy link

Choose a reason for hiding this comment

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

@lariciamota, understood! Since this is a simulation for testing purposes, the timeout cleanup isn't critical here.

If you'd like, you could add a brief comment noting this is intentional test/demo behavior to prevent future reviewers from flagging it again, but that's entirely optional.


✏️ Learnings added
Learnt from: lariciamota
Repo: vtex/faststore PR: 3109
File: packages/components/src/molecules/FileUploadCard/FileUploadCard.tsx:108-111
Timestamp: 2026-01-20T12:59:11.210Z
Learning: In `packages/components/src/molecules/FileUploadCard/FileUploadCard.tsx`, the setTimeout that simulates upload completion (setting uploadState to 'completed' after 2 seconds) is intentional for testing/demo purposes and does not require cleanup.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Comment on lines +202 to +233
{state === 'completed' && onSearch && (
<Button
variant="primary"
size="regular"
onClick={onSearch}
data-fs-file-upload-status-search-button
>
Search
</Button>
)}

{state === 'error' && (
<div data-fs-file-upload-status-error-actions>
{onDownloadTemplate && (
<Button
variant="secondary"
size="regular"
onClick={onDownloadTemplate}
data-fs-file-upload-status-download-button
>
Download template
</Button>
)}
{onSelectFile && (
<Button
variant="primary"
size="regular"
onClick={onSelectFile}
data-fs-file-upload-status-select-button
>
Select file
</Button>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Set non-submit action buttons to type="button".
Without an explicit type, these buttons default to submit inside a form, which can trigger unintended form submissions when this component is rendered within a form context.

Proposed fix
       {state === 'completed' && onSearch && (
         <Button
+          type="button"
           variant="primary"
           size="regular"
           onClick={onSearch}
           data-fs-file-upload-status-search-button
         >
           Search
         </Button>
       )}

       {state === 'error' && (
         <div data-fs-file-upload-status-error-actions>
           {onDownloadTemplate && (
             <Button
+              type="button"
               variant="secondary"
               size="regular"
               onClick={onDownloadTemplate}
               data-fs-file-upload-status-download-button
             >
               Download template
             </Button>
           )}
           {onSelectFile && (
             <Button
+              type="button"
               variant="primary"
               size="regular"
               onClick={onSelectFile}
               data-fs-file-upload-status-select-button
             >
               Select file
             </Button>
           )}
         </div>
       )}
📝 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
{state === 'completed' && onSearch && (
<Button
variant="primary"
size="regular"
onClick={onSearch}
data-fs-file-upload-status-search-button
>
Search
</Button>
)}
{state === 'error' && (
<div data-fs-file-upload-status-error-actions>
{onDownloadTemplate && (
<Button
variant="secondary"
size="regular"
onClick={onDownloadTemplate}
data-fs-file-upload-status-download-button
>
Download template
</Button>
)}
{onSelectFile && (
<Button
variant="primary"
size="regular"
onClick={onSelectFile}
data-fs-file-upload-status-select-button
>
Select file
</Button>
{state === 'completed' && onSearch && (
<Button
type="button"
variant="primary"
size="regular"
onClick={onSearch}
data-fs-file-upload-status-search-button
>
Search
</Button>
)}
{state === 'error' && (
<div data-fs-file-upload-status-error-actions>
{onDownloadTemplate && (
<Button
type="button"
variant="secondary"
size="regular"
onClick={onDownloadTemplate}
data-fs-file-upload-status-download-button
>
Download template
</Button>
)}
{onSelectFile && (
<Button
type="button"
variant="primary"
size="regular"
onClick={onSelectFile}
data-fs-file-upload-status-select-button
>
Select file
</Button>
🤖 Prompt for AI Agents
In `@packages/components/src/molecules/FileUploadStatus/FileUploadStatus.tsx`
around lines 202 - 233, The action buttons in FileUploadStatus (the Search
button with data-fs-file-upload-status-search-button, and the Download template
and Select file buttons with data-fs-file-upload-status-download-button and
data-fs-file-upload-status-select-button) are missing an explicit type and will
default to submit when rendered inside a form; update each Button element
rendered under state === 'completed' (onSearch) and state === 'error'
(onDownloadTemplate, onSelectFile) to include type="button" so they do not
trigger form submission.

Comment on lines +133 to +143
const handleDownloadTemplate = () => {
console.log('Download template clicked')
// Create and download template file
const csvContent = 'Product ID,Quantity,Price\n001,10,99.99\n002,5,49.99'
const blob = new Blob([csvContent], { type: 'text/csv' })
const url = window.URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = 'template.csv'
a.click()
window.URL.revokeObjectURL(url)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Make the template download more browser‑compatible.

Some browsers (notably Firefox) can ignore clicks on detached anchors. Appending/removing the node improves reliability.

🔧 Proposed change
   const a = document.createElement('a')
   a.href = url
   a.download = 'template.csv'
-  a.click()
-  window.URL.revokeObjectURL(url)
+  document.body.appendChild(a)
+  a.click()
+  a.remove()
+  window.URL.revokeObjectURL(url)
📝 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
const handleDownloadTemplate = () => {
console.log('Download template clicked')
// Create and download template file
const csvContent = 'Product ID,Quantity,Price\n001,10,99.99\n002,5,49.99'
const blob = new Blob([csvContent], { type: 'text/csv' })
const url = window.URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = 'template.csv'
a.click()
window.URL.revokeObjectURL(url)
const handleDownloadTemplate = () => {
console.log('Download template clicked')
// Create and download template file
const csvContent = 'Product ID,Quantity,Price\n001,10,99.99\n002,5,49.99'
const blob = new Blob([csvContent], { type: 'text/csv' })
const url = window.URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = 'template.csv'
document.body.appendChild(a)
a.click()
a.remove()
window.URL.revokeObjectURL(url)
}
🤖 Prompt for AI Agents
In `@packages/storybook/stories/fileupload.stories.tsx` around lines 133 - 143,
The handleDownloadTemplate function currently triggers a download by clicking a
detached anchor which can be ignored by some browsers (e.g., Firefox); modify
handleDownloadTemplate to append the created <a> element to document.body before
calling a.click(), then remove the anchor after the click and revoke the object
URL (keep creating the Blob, setting a.href/a.download as-is) to improve
cross-browser reliability.

Comment on lines +69 to +78
right: 0.125rem;
display: flex;
gap: 0;
align-items: center;

@include media(">=notebook") {
position: absolute;
right: 0;
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Orphaned CSS properties will cause issues.

Lines 69-78 contain CSS properties (right, display, gap, align-items, media query) without a proper selector. This appears to be a merge conflict artifact or copy-paste error. These properties will either cause SCSS compilation errors or be ignored.

Remove orphaned properties

These lines should be removed as the same styling is already defined in [data-fs-search-input-field-actions] block (lines 48-59):

  [data-fs-search-input-field-input] {
    height: auto;
    padding-inline-end: var(--fs-search-input-field-input-padding-right);
    background-color: var(--fs-search-input-field-input-bkg-color);
    transition: box-shadow var(--fs-search-input-field-transition-timing) var(--fs-search-input-field-transition-timing), border var(--fs-search-input-field-transition-timing) var(--fs-search-input-field-transition-function);

    `@include` media("<notebook") { border: 0; }
  }
-    right: 0.125rem;
-    display: flex;
-    gap: 0;
-    align-items: center;
-
-    `@include` media(">=notebook") {
-      position: absolute;
-      right: 0;
-    }
-  }
📝 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
right: 0.125rem;
display: flex;
gap: 0;
align-items: center;
@include media(">=notebook") {
position: absolute;
right: 0;
}
}
🤖 Prompt for AI Agents
In `@packages/ui/src/components/molecules/SearchInputField/styles.scss` around
lines 69 - 78, Remove the orphaned CSS properties block (the standalone rules:
right, display, gap, align-items and the media query) that are present without a
selector; these are duplicate/leftover and will break SCSS. Locate the stray
block near the end of the file and delete it, keeping the existing valid styling
defined under the [data-fs-search-input-field-actions] selector (which already
defines the same rules), and ensure only the selector-scoped styles remain.

Comment on lines +109 to +122
[data-fs-search-input-field-input] {
height: auto;
padding-right: var(--fs-spacing-7);
background-color: var(--fs-search-input-field-input-bkg-color);
transition:
box-shadow var(--fs-search-input-field-transition-timing)
var(--fs-search-input-field-transition-timing),
border var(--fs-search-input-field-transition-timing)
var(--fs-search-input-field-transition-function);

@include media("<notebook") {
border: 0;
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Duplicate selector definition.

[data-fs-search-input-field-input] is defined twice (lines 61-68 and 109-122). The second definition uses padding-right while the first uses padding-inline-end (which is better for RTL support). These should be consolidated.

Consolidate duplicate definitions

Remove this duplicate block and keep the first definition at lines 61-68 which uses the more appropriate padding-inline-end:

-  [data-fs-search-input-field-input] {
-    height: auto;
-    padding-right: var(--fs-spacing-7);
-    background-color: var(--fs-search-input-field-input-bkg-color);
-    transition:
-      box-shadow var(--fs-search-input-field-transition-timing)
-      var(--fs-search-input-field-transition-timing),
-      border var(--fs-search-input-field-transition-timing)
-      var(--fs-search-input-field-transition-function);
-
-    `@include` media("<notebook") {
-      border: 0;
-    }
-  }
📝 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
[data-fs-search-input-field-input] {
height: auto;
padding-right: var(--fs-spacing-7);
background-color: var(--fs-search-input-field-input-bkg-color);
transition:
box-shadow var(--fs-search-input-field-transition-timing)
var(--fs-search-input-field-transition-timing),
border var(--fs-search-input-field-transition-timing)
var(--fs-search-input-field-transition-function);
@include media("<notebook") {
border: 0;
}
}
🤖 Prompt for AI Agents
In `@packages/ui/src/components/molecules/SearchInputField/styles.scss` around
lines 109 - 122, There is a duplicate CSS rule for the selector
[data-fs-search-input-field-input]; remove the second block (the one that sets
padding-right) and consolidate any unique properties (background-color,
transition, height, and the media query) into the first definition so the rule
uses padding-inline-end for RTL support; ensure the merged rule retains the
media query (`@include` media("<notebook") { border: 0; }) and the
transition/background-color/height settings from the duplicate block.

const [searchQuery, setSearchQuery] = useState('')

const handleDownloadTemplate = () => {
console.log('Download template clicked')
Copy link
Contributor

Choose a reason for hiding this comment

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

Podemos já apagar esse console.log, os outros pode deixar já que as outras funções estão sem ação ainda

Suggested change
console.log('Download template clicked')

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants