Skip to content

Commit 439ab1c

Browse files
feat(uploadFiles): add support of files loading to links + fix lint issues
1 parent e14485b commit 439ab1c

File tree

11 files changed

+646
-61
lines changed

11 files changed

+646
-61
lines changed

datahub-web-react/src/alchemy-components/components/Editor/extensions/fileDragDrop/fileUtils.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,6 @@ export const handleFileDownload = (url: string, name: string): void => {
243243
* @returns true if the URL is a file URL
244244
*/
245245
export const isFileUrl = (url: string): boolean => {
246-
return true;
247246
return url.includes('/openapi/v1/'); // Our internal file API
248247
};
249248

datahub-web-react/src/alchemy-components/components/FileNode/FileIcon.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import React, { useMemo } from 'react';
22
import styled from 'styled-components';
33

4+
import { getFileIconFromExtension } from '@components/components/FileNode/utils';
45
import { Icon } from '@components/components/Icon';
56

6-
import { getFileIconFromExtension } from './utils';
7-
87
const StyledIcon = styled(Icon)`
98
flex-shrink: 0;
109
`;

datahub-web-react/src/alchemy-components/components/FileNode/FileNode.stories.tsx

Lines changed: 12 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { Meta, StoryObj } from '@storybook/react';
33
import React from 'react';
44

55
import { GridList } from '@components/.docs/mdx-components';
6-
import { FileNode } from './FileNode';
6+
import { FileNode } from '@components/components/FileNode/FileNode';
77

88
const meta = {
99
title: 'Components / FileNode',
@@ -14,7 +14,8 @@ const meta = {
1414
layout: 'centered',
1515
badges: [BADGE.STABLE],
1616
docs: {
17-
subtitle: 'FileNode displays a file with its icon, name, and optional actions like closing or custom right content.',
17+
subtitle:
18+
'FileNode displays a file with its icon, name, and optional actions like closing or custom right content.',
1819
},
1920
},
2021

@@ -100,53 +101,35 @@ export const withFileName = () => (
100101

101102
export const loadingState = () => (
102103
<GridList>
103-
<FileNode fileName="document.pdf" loading={true} />
104+
<FileNode fileName="document.pdf" loading />
104105
</GridList>
105106
);
106107

107108
export const withBorder = () => (
108109
<GridList>
109-
<FileNode fileName="document.pdf" border={true} />
110-
<FileNode fileName="presentation.pptx" border={true} />
110+
<FileNode fileName="document.pdf" border />
111+
<FileNode fileName="presentation.pptx" border />
111112
</GridList>
112113
);
113114

114115
export const withCloseButton = () => (
115116
<GridList>
116-
<FileNode
117-
fileName="document.pdf"
118-
onClose={() => console.log('Document closed')}
119-
/>
120-
<FileNode
121-
fileName="presentation.pptx"
122-
onClose={() => console.log('Presentation closed')}
123-
/>
117+
<FileNode fileName="document.pdf" onClose={() => console.log('Document closed')} />
118+
<FileNode fileName="presentation.pptx" onClose={() => console.log('Presentation closed')} />
124119
</GridList>
125120
);
126121

127122
export const withOnClick = () => (
128123
<GridList>
129-
<FileNode
130-
fileName="document.pdf"
131-
onClick={() => console.log('Document clicked')}
132-
/>
133-
<FileNode
134-
fileName="presentation.pptx"
135-
onClick={() => console.log('Presentation clicked')}
136-
/>
124+
<FileNode fileName="document.pdf" onClick={() => console.log('Document clicked')} />
125+
<FileNode fileName="presentation.pptx" onClick={() => console.log('Presentation clicked')} />
137126
</GridList>
138127
);
139128

140129
export const withExtraRightContent = () => (
141130
<GridList>
142-
<FileNode
143-
fileName="document.pdf"
144-
extraRightContent={<span style={{ color: 'green' }}></span>}
145-
/>
146-
<FileNode
147-
fileName="presentation.pptx"
148-
extraRightContent={<span style={{ color: 'blue' }}>!</span>}
149-
/>
131+
<FileNode fileName="document.pdf" extraRightContent={<span style={{ color: 'green' }}></span>} />
132+
<FileNode fileName="presentation.pptx" extraRightContent={<span style={{ color: 'blue' }}>!</span>} />
150133
</GridList>
151134
);
152135

datahub-web-react/src/alchemy-components/components/FileNode/FileNode.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,7 @@ import { Typography } from 'antd';
33
import React, { useCallback, useMemo } from 'react';
44
import styled from 'styled-components';
55

6-
import {
7-
getExtensionFromFileName,
8-
getFileNameFromUrl,
9-
} from '@components/components/Editor/extensions/fileDragDrop/fileUtils';
6+
import { getExtensionFromFileName } from '@components/components/Editor/extensions/fileDragDrop/fileUtils';
107
import { FileIcon } from '@components/components/FileNode/FileIcon';
118
import { FileNodeProps } from '@components/components/FileNode/types';
129
import { getFontSize } from '@components/theme/utils';
@@ -94,7 +91,7 @@ export function FileNode({
9491
<Container $border={border} className={className} $fontSize={fontSize}>
9592
<FileDetails>
9693
<Loading height={18} width={20} marginTop={0} />
97-
<FileName ellipsis={{ tooltip: name }}>Uploading {name}...</FileName>
94+
<FileName ellipsis={{ tooltip: fileName }}>Uploading {fileName}...</FileName>
9895
</FileDetails>
9996
</Container>
10097
);
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
import { Button, Icon, Text, colors } from '@components';
2+
import React, { useCallback, useRef, useState } from 'react';
3+
import styled from 'styled-components';
4+
5+
const Container = styled.div<{ $dragActive?: boolean }>`
6+
padding: 16px;
7+
8+
display: flex;
9+
flex-direction: column;
10+
align-items: center;
11+
12+
border: 1px dashed ${(props) => (props.$dragActive ? colors.primary[500] : colors.gray[100])};
13+
border-radius: 12px;
14+
`;
15+
16+
const InnerContainer = styled.div`
17+
display: flex;
18+
flex-direction: column;
19+
align-items: center;
20+
21+
gap: 8px;
22+
`;
23+
24+
const IconContainer = styled.div`
25+
display: flex;
26+
align-items: center;
27+
justify-content: center;
28+
29+
width: 32px;
30+
height: 32px;
31+
border-radius: 100%;
32+
background-color: ${colors.gray[1000]};
33+
`;
34+
35+
const ActionTextContainer = styled.div`
36+
display: flex;
37+
gap: 4px;
38+
`;
39+
40+
const InlineButton = styled(Button)`
41+
display: inline;
42+
padding: 0px;
43+
background: none;
44+
45+
&:hover {
46+
background: none;
47+
}
48+
`;
49+
50+
const Description = styled.div``;
51+
52+
interface Props {
53+
onFilesUpload?: (files: File[]) => Promise<void>;
54+
className?: string;
55+
}
56+
57+
export function FileDragAndDropArea({ onFilesUpload, className }: Props) {
58+
const [dragActive, setDragActive] = useState<boolean>(false);
59+
60+
const inputRef = useRef<HTMLInputElement>(null);
61+
62+
const handleDrop = useCallback(
63+
async (e: React.DragEvent) => {
64+
e.preventDefault();
65+
e.stopPropagation();
66+
setDragActive(false);
67+
68+
await onFilesUpload?.(Array.from(e.dataTransfer.files));
69+
},
70+
[onFilesUpload],
71+
);
72+
73+
const onFileInputChange = useCallback(
74+
async (e: React.ChangeEvent<HTMLInputElement>) => {
75+
const { files } = e.target;
76+
if (files) await onFilesUpload?.(Array.from(files));
77+
},
78+
[onFilesUpload],
79+
);
80+
81+
const onButtonClick = (e: React.MouseEvent) => {
82+
e.stopPropagation();
83+
e.preventDefault();
84+
inputRef.current?.click();
85+
};
86+
87+
const handleDrag = useCallback((e: React.DragEvent) => {
88+
e.preventDefault();
89+
e.stopPropagation();
90+
if (e.type === 'dragenter' || e.type === 'dragover') {
91+
setDragActive(true);
92+
} else if (e.type === 'dragleave') {
93+
// Check if the next target is still inside our drop zone
94+
const dropZone = e.currentTarget;
95+
const related = e.relatedTarget;
96+
97+
// If relatedTarget is null (e.g., leaving window) or outside drop zone
98+
if (!related || !dropZone.contains(related as Node)) {
99+
setDragActive(false);
100+
}
101+
}
102+
}, []);
103+
104+
return (
105+
<>
106+
<Container
107+
onDrop={handleDrop}
108+
onDragEnter={handleDrag}
109+
onDragOver={handleDrag}
110+
onDragLeave={handleDrag}
111+
$dragActive={dragActive}
112+
className={className}
113+
>
114+
<InnerContainer>
115+
<IconContainer onDragLeave={(e) => e.stopPropagation()}>
116+
<Icon icon="UploadSimple" source="phosphor" color="primary" size="2xl" />
117+
</IconContainer>
118+
<ActionTextContainer>
119+
<Text size="sm" weight="semiBold">
120+
Drag a file or
121+
</Text>{' '}
122+
<InlineButton variant="text" size="sm" onClick={onButtonClick}>
123+
click to upload
124+
</InlineButton>
125+
</ActionTextContainer>
126+
<Description>
127+
<Text size="sm" color="gray">
128+
Max Size: 2GB
129+
</Text>
130+
</Description>
131+
</InnerContainer>
132+
</Container>
133+
134+
<input ref={inputRef} type="file" multiple onChange={onFileInputChange} style={{ display: 'none' }} />
135+
</>
136+
);
137+
}

datahub-web-react/src/app/entityV2/summary/links/LinkFormWrapper.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import { Input } from '@components';
22
import { Form } from 'antd';
33
import React, { useCallback, useEffect, useMemo } from 'react';
44

5-
import { LinkFormData, LinkFormVariant } from '@app/entityV2/shared/components/styled/LinkFormModal/types';
65
import { UploadFileForm } from '@app/entityV2/summary/links/UploadFileForm';
76
import { UrlLinkForm } from '@app/entityV2/summary/links/UrlLinkForm';
7+
import { LinkFormData, LinkFormVariant } from '@app/entityV2/summary/links/types';
88
import ButtonTabs from '@app/homeV3/modules/shared/ButtonTabs/ButtonTabs';
99
import { useIsDocumentationFileUploadV1Enabled } from '@app/shared/hooks/useIsDocumentationFileUploadV1Enabled';
1010

datahub-web-react/src/app/entityV2/summary/links/UploadFileForm.tsx

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ import styled from 'styled-components';
44

55
import { getFileNameFromUrl } from '@components/components/Editor/extensions/fileDragDrop/fileUtils';
66
import { FileNode } from '@components/components/FileNode/FileNode';
7+
import { FontSizeOptions } from '@components/theme/config';
78

8-
import { FileDragAndDropArea } from '@app/entityV2/shared/components/styled/LinkFormModal/FileDragAndDropArea';
9+
import { FileDragAndDropArea } from '@app/entityV2/summary/links/FileDragAndDropArea';
910
import { LinkFormData, LinkFormVariant } from '@app/entityV2/summary/links/types';
10-
import { FontSizeOptions } from '@components/theme/config';
11-
import { useUploadFileHandler } from './useUploadFileHandler';
11+
import { useUploadFileHandler } from '@app/entityV2/summary/links/useUploadFileHandler';
1212

1313
const StyledFileDragAndDropArea = styled(FileDragAndDropArea)``;
1414

@@ -90,13 +90,7 @@ export function UploadFileForm({ initialValues, fontSize: _fontSize }: Props) {
9090
>
9191
{shouldShowDragAndDropArea && <StyledFileDragAndDropArea onFilesUpload={onFilesUpload} />}
9292
{!shouldShowDragAndDropArea && (
93-
<FileNode
94-
fileName={fileName}
95-
onClose={onFileRemove}
96-
loading={isFileUploading}
97-
size="md"
98-
border
99-
/>
93+
<FileNode fileName={fileName} onClose={onFileRemove} loading={isFileUploading} size="md" border />
10094
)}
10195
<input type="text" hidden />
10296
</Form.Item>

0 commit comments

Comments
 (0)