Skip to content

Commit 5ab1efc

Browse files
committed
Merge remote-tracking branch 'origin/master' into feat/concurrent-dev-command
2 parents 595df83 + 6a93ad3 commit 5ab1efc

File tree

7 files changed

+100
-26
lines changed

7 files changed

+100
-26
lines changed

.github/workflows/main.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,13 @@ jobs:
225225
env:
226226
# Storybook is started by Playwright's webServer config on port 6007
227227
TOKEN_ENDPOINT: http://localhost:3000/api/upload
228+
GOOGLE_CLIENT_ID: ${{ secrets.GOOGLE_CLIENT_ID }}
229+
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
230+
GOOGLE_APP_ID: ${{ secrets.GOOGLE_APP_ID }}
231+
ONEDRIVE_CLIENT_ID: ${{ secrets.ONEDRIVE_CLIENT_ID }}
232+
DROPBOX_CLIENT_ID: ${{ secrets.DROPBOX_CLIENT_ID }}
233+
DROPBOX_APP_SECRET: ${{ secrets.DROPBOX_APP_SECRET }}
234+
DROPBOX_REDIRECT_URI: ${{ secrets.DROPBOX_REDIRECT_URI }}
228235
run: pnpm run test:e2e --reporter=list
229236

230237
- name: Upload Playwright report

.storybook/main.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ const config = {
113113
GOOGLE_APP_ID: process.env.GOOGLE_APP_ID!,
114114
ONEDRIVE_CLIENT_ID: process.env.ONEDRIVE_CLIENT_ID!,
115115
DROPBOX_CLIENT_ID: process.env.DROPBOX_CLIENT_ID!,
116-
DROPBOX_REDIRECT_URI: process.env.dropbox_redirect_uri!,
116+
DROPBOX_REDIRECT_URI: process.env.DROPBOX_REDIRECT_URI!,
117117
}),
118118
}
119119

src/frontend/components/AdapterSelector.tsx

Lines changed: 82 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from 'react'
1+
import React, { useCallback } from 'react'
22
import { useRootContext } from '../context/RootContext'
33
import useAdapterSelector from '../hooks/useAdapterSelector'
44
import { cn } from '../lib/tailwind'
@@ -15,15 +15,65 @@ export default function AdapterSelector() {
1515
maxFileSize,
1616
dark,
1717
classNames,
18-
allowFolderUpload,
18+
showSelectFolderButton,
1919
},
2020
isAddingMore,
2121
setIsAddingMore,
2222
inputRef,
23+
setFiles,
2324
} = useRootContext()
2425
const { chosenAdapters, handleAdapterClick, handleInputFileChange } =
2526
useAdapterSelector()
2627

28+
const handleBrowseFilesClick = useCallback(() => {
29+
if (inputRef.current) {
30+
inputRef.current.removeAttribute('webkitdirectory')
31+
inputRef.current.removeAttribute('directory')
32+
inputRef.current.click()
33+
}
34+
}, [inputRef])
35+
36+
const handleSelectFolderClick = useCallback(async () => {
37+
const anyWindow = window as any
38+
if (anyWindow.showDirectoryPicker) {
39+
try {
40+
const directoryHandle = await anyWindow.showDirectoryPicker()
41+
const files: File[] = []
42+
43+
async function getFiles(dirHandle: any, path = '') {
44+
for await (const entry of dirHandle.values()) {
45+
const newPath = path
46+
? `${path}/${entry.name}`
47+
: entry.name
48+
if (entry.kind === 'file') {
49+
const file = await entry.getFile()
50+
;(file as any).webkitRelativePath = newPath
51+
files.push(file)
52+
} else if (entry.kind === 'directory') {
53+
await getFiles(entry, newPath)
54+
}
55+
}
56+
}
57+
await getFiles(directoryHandle)
58+
if (files.length > 0) {
59+
setFiles(files)
60+
if (inputRef.current) {
61+
inputRef.current.value = ''
62+
}
63+
}
64+
} catch {
65+
// User cancelled, do nothing.
66+
}
67+
} else {
68+
// Fallback to existing behavior
69+
if (inputRef.current) {
70+
inputRef.current.setAttribute('webkitdirectory', 'true')
71+
inputRef.current.setAttribute('directory', 'true')
72+
inputRef.current.click()
73+
}
74+
}
75+
}, [inputRef, setFiles])
76+
2777
return (
2878
<div
2979
className={cn(
@@ -84,10 +134,6 @@ export default function AdapterSelector() {
84134
data-testid="upup-file-input"
85135
ref={inputRef}
86136
multiple={multiple}
87-
// Allow selecting folders when enabled (webkitdirectory works in Chromium-based browsers)
88-
{...((allowFolderUpload
89-
? { webkitdirectory: true, directory: true }
90-
: {}) as any)}
91137
onChange={handleInputFileChange}
92138
/>
93139
<div className="upup-flex upup-flex-col upup-items-center upup-gap-1 upup-text-center md:upup-gap-2 md:upup-px-[30px]">
@@ -112,23 +158,38 @@ export default function AdapterSelector() {
112158
dark,
113159
},
114160
)}
115-
onClick={() => inputRef.current?.click()}
161+
onClick={handleBrowseFilesClick}
116162
>
117-
browse
163+
browse files
118164
</button>
119-
{allowFolderUpload && (
120-
<span
121-
className={cn(
122-
'upup-text-xs upup-text-[#0B0B0B] md:upup-text-sm',
123-
{
124-
'upup-text-white dark:upup-text-white':
125-
dark,
126-
},
127-
)}
128-
>
129-
{' '}
130-
or folder
131-
</span>
165+
{showSelectFolderButton && (
166+
<>
167+
<span
168+
className={cn(
169+
'upup-text-xs upup-text-[#0B0B0B] md:upup-text-sm',
170+
{
171+
'upup-text-white dark:upup-text-white':
172+
dark,
173+
},
174+
)}
175+
>
176+
{' '}
177+
or
178+
</span>
179+
<button
180+
type="button"
181+
className={cn(
182+
'upup-cursor-pointer upup-text-xs upup-font-semibold upup-text-[#0E2ADD] md:upup-text-sm',
183+
{
184+
'upup-text-[#59D1F9] dark:upup-text-[#59D1F9]':
185+
dark,
186+
},
187+
)}
188+
onClick={handleSelectFolderClick}
189+
>
190+
select a folder
191+
</button>
192+
</>
132193
)}
133194
</div>
134195
<p

src/frontend/context/RootContext.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ type ContextProps = Required<
5050
| 'dark'
5151
| 'classNames'
5252
| 'icons'
53-
| 'allowFolderUpload'
53+
| 'showSelectFolderButton'
5454
>
5555
> &
5656
Pick<UpupUploaderProps, 'maxFileSize'> & {

src/frontend/hooks/useRootProvider.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export default function useRootProvider({
3636
limit: propLimit = 1,
3737
isProcessing = false,
3838
allowPreview = true,
39-
allowFolderUpload = true,
39+
showSelectFolderButton = false,
4040
maxFileSize,
4141
shouldCompress = false,
4242
uploadAdapters = [UploadAdapter.INTERNAL, UploadAdapter.LINK],
@@ -391,7 +391,7 @@ export default function useRootProvider({
391391
limit,
392392
isProcessing,
393393
allowPreview,
394-
allowFolderUpload,
394+
showSelectFolderButton,
395395
multiple,
396396
icons: {
397397
ContainerAddMoreIcon: icons.ContainerAddMoreIcon || TbPlus,

src/shared/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ export type UpupUploaderProps = {
122122
tokenEndpoint: string
123123

124124
// Optional Props
125-
allowFolderUpload?: boolean
125+
showSelectFolderButton?: boolean // Controls the "select a folder" button visibility
126126
enableAutoCorsConfig?: boolean
127127
uploadAdapters?: UploadAdapter[]
128128
driveConfigs?: {

stories/UploaderWithButton.stories.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ const meta = {
3636
UploadAdapter.LINK,
3737
],
3838
},
39+
showSelectFolderButton: {
40+
control: 'boolean',
41+
description:
42+
'Show "select a folder" button for browsing folders via file explorer',
43+
},
3944
},
4045
render: (args, context) => {
4146
const isDarkMode = context.globals.theme !== 'light'
@@ -105,6 +110,7 @@ export const UploaderWithButton: Story = {
105110
'accept',
106111
'limit',
107112
'mini',
113+
'showSelectFolderButton',
108114
],
109115
},
110116
},

0 commit comments

Comments
 (0)