Skip to content

Conversation

@arsen-afaunov
Copy link

No description provided.

@gravity-ui
Copy link
Contributor

gravity-ui bot commented Nov 6, 2025

Preview is ready.

@gravity-ui
Copy link
Contributor

gravity-ui bot commented Nov 6, 2025

Visual Tests Report is ready.

@arsen-afaunov arsen-afaunov changed the title feat: add useDropZone hook feat: add FileDropZone component Nov 6, 2025
@arsen-afaunov arsen-afaunov force-pushed the DATAUI-3438-add-FileDropZone-component branch from e93907d to 8173352 Compare November 10, 2025 15:26
@korvin89
Copy link
Contributor

The state of the component is not reset with such a case:

Screen.Recording.2025-11-12.at.17.59.06.mov

export type UseFileInputProps = {
onUpdate?: (files: File[]) => void;
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
multiple?: boolean;
Copy link
Contributor

Choose a reason for hiding this comment

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

The multiple prop was not initially added, as the hook gives the basic configuration for the native input. That is, if the consumer needs multiple, then you need to add it yourself, otherwise it turns out to be a strange proxy that passes the prop through and returns it in the same form without using it in any way in calculations.

Copy link
Author

Choose a reason for hiding this comment

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

Done.

role: 'button',
};

export interface DroppableProps extends Required<typeof DROP_ZONE_BASE_ATTRIBUTES> {
Copy link
Contributor

Choose a reason for hiding this comment

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

We usually name props according to this logic <ComponentOrHookName> + details. This is necessary so that all types have their own name space + it improves DX when working with lib types

Copy link
Author

Choose a reason for hiding this comment

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

Done.

```

The `useDropZone` hook provides props for an element to act as a drop zone and also gives access to the dragging-over state.
Additionally, the hook supports a more imperative approach: it can accept a `ref` for cases where you don't have direct access to the HTML element you want to make a drop zone.
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you describe the minimum usage examples?

Copy link
Author

Choose a reason for hiding this comment

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

Done.


export interface UseDropZoneState {
isDraggingOver: boolean;
getDroppableProps: () => DroppableProps;
Copy link
Contributor

@korvin89 korvin89 Nov 12, 2025

Choose a reason for hiding this comment

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

There seems to be no reason to make this as function

Copy link
Author

Choose a reason for hiding this comment

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

I think the function will scale better in case parameterization is needed: you can just add optional arguments instead of overloading the type and turning it into a union. In my view, the function is a better fit here.

}

function typeMatchesPattern(actualMimeType: string, expectedMimeTypePattern: string): boolean {
const actualMimeTypeParts = actualMimeType.split('/');
Copy link
Contributor

Choose a reason for hiding this comment

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

Are we assuming that only files will be used?

Copy link
Author

Choose a reason for hiding this comment

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

I didn’t quite understand the question. MIME types allow specifying arbitrary data. For instance, in the Storybook example, text/plain is used, which enables you to drag and drop HTML elements. Moreover, this way you can add custom MIME types like application/x-my-custom-type to make sure that only permitted elements are drag-n-dropped —this can be useful, for example, for a Kanban board app.

description?: string;
buttonText?: string;
icon?: IconData | null;
errorIcon?: IconData | null;
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we really need more than one icon property?

Copy link
Author

Choose a reason for hiding this comment

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

It seemed to me that since the base icon and the “error” icon are semantically different, it makes sense to allow them to be customized separately. If we don't want to give the component user this flexibility in order to maintain visual consistency, that's not a problem—we can just remove this prop.

import i18n from '../i18n';

type FileDropZoneButtonProps = {
className?: string;
Copy link
Contributor

Choose a reason for hiding this comment

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

It seems to me that it would be convenient to have the children property so that explicitly using the button in the component layout, you can explicitly define the text in the button itself and not in its parent

buttonText is convenient to use as a parent зкщзукен if you don't explicitly use FileDropZoneButton

Copy link
Author

Choose a reason for hiding this comment

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

The motivation was as follows. First, I wanted to eliminate redundancy in the component's interface and make the parent's props the single source of truth in this regard. This way, the cognitive load on the component user is reduced, as they no longer need to keep two sets of props with the same meaning in mind. Second, this approach allows for the encapsulation of the logic related to text handling, ensuring consistency between the displayed texts and the component's internal states—for example, the user of the component won't need to handle error messages or grammatical pluralization themselves if the multiple prop is provided.

More generally, the compound component pattern here was intended solely to allow arbitrary layouts of elements, while fully hiding their internal implementation and their relationships.

import {useFileZoneContext} from '../FileDropZone.Provider';

type FileDropZoneDescriptionProps = {
className?: string;
Copy link
Contributor

@korvin89 korvin89 Nov 12, 2025

Choose a reason for hiding this comment

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

The same with the children property as in FileDropZoneButton

import i18n from '../i18n';

type FileDropZoneTitleProps = {
className?: string;
Copy link
Contributor

@korvin89 korvin89 Nov 12, 2025

Choose a reason for hiding this comment

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

The same with the children property as in FileDropZoneButton


export interface FileDropZoneProps extends Pick<BaseInputControlProps, 'validationState'> {
accept: UseDropZoneAccept;
onAdd: (files: File[]) => void;
Copy link
Contributor

Choose a reason for hiding this comment

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

We usually use the name onUpdate for such callbacks, this is a familiar pattern for users of our library

Copy link
Author

Choose a reason for hiding this comment

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

Done.

@arsen-afaunov arsen-afaunov force-pushed the DATAUI-3438-add-FileDropZone-component branch from 8173352 to b9fb7a6 Compare December 1, 2025 07:50
@gravity-ui
Copy link
Contributor

gravity-ui bot commented Dec 1, 2025

Preview is ready.

@gravity-ui
Copy link
Contributor

gravity-ui bot commented Dec 1, 2025

🎭 Component Tests Report is ready.

@arsen-afaunov
Copy link
Author

The state of the component is not reset with such a case:

Screen.Recording.2025-11-12.at.17.59.06.mov

Thank you for finding that! I’ll fix it.

@arsen-afaunov arsen-afaunov force-pushed the DATAUI-3438-add-FileDropZone-component branch from b9fb7a6 to dd99072 Compare December 1, 2025 10:36
@arsen-afaunov
Copy link
Author

The state of the component is not reset with such a case:

Screen.Recording.2025-11-12.at.17.59.06.mov

Fixed the nesting counter consistency when transitioning between states.

@arsen-afaunov arsen-afaunov force-pushed the DATAUI-3438-add-FileDropZone-component branch from 41621aa to 0cda828 Compare January 21, 2026 12:46
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.

3 participants