Skip to content

Commit 10e7c8c

Browse files
committed
feat: implement array property upload functionality
1 parent dd857f7 commit 10e7c8c

11 files changed

+278
-150
lines changed

src/features/upload-file/components/edit.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
import React, { FC, useState, useEffect } from 'react'
22
import { EditPropertyProps, flat } from 'admin-bro'
33
import { DropZone, FormGroup, Label, DropZoneItem } from '@admin-bro/design-system'
4-
import PropertyCustom from '../types/property-custom.type'
4+
import buildCustom from '../utils/build-custom'
55

66
const Edit: FC<EditPropertyProps> = ({ property, record, onChange }) => {
77
const { params } = record
8-
const { custom } = property as unknown as { custom: PropertyCustom }
8+
const custom = buildCustom(property)
99

1010
const path = flat.get(params, custom.filePathProperty)
1111
const key = flat.get(params, custom.keyProperty)
1212
const file = flat.get(params, custom.fileProperty)
1313

1414
const [originalKey, setOriginalKey] = useState(key)
15-
const [filesToUpload, setFilesToUpload] = useState<Array<File>>([])
15+
const [filesToUpload, setFilesToUpload] = useState<Array<File>>(file || [])
1616

1717
useEffect(() => {
1818
// it means means that someone hit save and new file has been uploaded

src/features/upload-file/components/file.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Icon, Button, Box } from '@admin-bro/design-system'
55
import { ShowPropertyProps, flat } from 'admin-bro'
66
import { ImageMimeTypes, AudioMimeTypes } from '../types/mime-types.type'
77
import PropertyCustom from '../types/property-custom.type'
8+
import buildCustom from '../utils/build-custom'
89

910
type Props = ShowPropertyProps & {
1011
width?: number | string;
@@ -47,7 +48,7 @@ const SingleFile: FC<SingleFileProps> = (props) => {
4748
}
4849

4950
const File: FC<Props> = ({ width, record, property }) => {
50-
const { custom } = property as unknown as { custom: PropertyCustom }
51+
const custom = buildCustom(property)
5152

5253
const path = flat.get(record?.params, custom.filePathProperty)
5354

src/features/upload-file/factories/strip-payload-factory.ts

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,40 @@ export const stripPayloadFactory = (
2626
request: ActionRequest,
2727
context: ActionContext,
2828
): Promise<ActionRequest> => {
29-
const { properties } = uploadOptionsWithDefault
29+
const { properties, parentArray } = uploadOptionsWithDefault
3030

3131
if (request?.payload) {
3232
let data: ContextNamespace = context[CONTEXT_NAMESPACE] || {}
3333

34+
if (parentArray) {
35+
const parent = flat.get(request.payload, parentArray)
36+
if (parent) {
37+
for (let index = 0; index < parent.length; index += 1) {
38+
data = {
39+
...data,
40+
...[properties.file, properties.filesToDelete].reduce((memo, prop) => {
41+
const fullProp = [parentArray, index, prop].join(flat.DELIMITER)
42+
return {
43+
...memo,
44+
[fullProp]: flat.get(request.payload, fullProp),
45+
}
46+
}, {}),
47+
}
48+
}
49+
}
50+
} else {
51+
data = {
52+
...data,
53+
[properties.file]: flat.get(request.payload, properties.file),
54+
[properties.filesToDelete]: flat.get(request.payload, properties.filesToDelete),
55+
}
56+
}
57+
3458
data = {
3559
...data,
36-
[properties.file]: flat.get(request.payload, properties.file),
37-
[properties.filesToDelete]: flat.get(request.payload, properties.filesToDelete),
3860
__invocations: [
3961
...(data.__invocations || []),
40-
{ properties },
62+
{ properties, parentArray },
4163
],
4264
}
4365

src/features/upload-file/factories/update-record-factory.spec.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ describe('updateRecordFactory', () => {
4545
recordStub = createStubInstance(BaseRecord, {
4646
id: sinon.stub<any, string>().returns('1'),
4747
isValid: sinon.stub<any, boolean>().returns(true),
48-
update: sinon.stub<any, Promise<BaseRecord>>().returnsThis(),
48+
save: sinon.stub<[], Promise<BaseRecord>>().returnsThis(),
49+
storeParams: sinon.stub<any, void>(),
4950
})
5051
recordStub.params = {}
5152
})
@@ -92,13 +93,14 @@ describe('updateRecordFactory', () => {
9293
it('updates all fields in the record', async () => {
9394
await updateRecord(response, request, actionContext)
9495

95-
expect(recordStub.update).to.have.been.calledWith(sinon.match({
96+
expect(recordStub.storeParams).to.have.been.calledWith(sinon.match({
9697
[uploadOptions.properties.key]: expectedKey,
9798
[uploadOptions.properties.bucket as string]: provider.bucket,
9899
[uploadOptions.properties.size as string]: File.size.toString(),
99100
[uploadOptions.properties.mimeType as string]: File.type,
100101
[uploadOptions.properties.filename as string]: File.name,
101102
}))
103+
expect(recordStub.save).to.have.been.calledWith()
102104
})
103105

104106
it('does not delete any old file if there were not file before', async () => {
@@ -138,13 +140,14 @@ describe('updateRecordFactory', () => {
138140

139141
expect(provider.delete).to.have.been.calledWith(expectedKey, storedBucket)
140142

141-
expect(recordStub.update).to.have.been.calledWith(sinon.match({
143+
expect(recordStub.storeParams).to.have.been.calledWith(sinon.match({
142144
[uploadOptions.properties.key]: null,
143145
[uploadOptions.properties.bucket as string]: null,
144146
[uploadOptions.properties.size as string]: null,
145147
[uploadOptions.properties.mimeType as string]: null,
146148
[uploadOptions.properties.filename as string]: null,
147149
}))
150+
expect(recordStub.save).to.have.been.calledWith()
148151
})
149152
})
150153

@@ -188,11 +191,12 @@ describe('updateRecordFactory', () => {
188191
[`${uploadOptions.properties.filename}.${index}` as string]: Files[index].name,
189192
})
190193

191-
expect(recordStub.update).to.have.been.calledWith(sinon.match({
194+
expect(recordStub.storeParams).to.have.been.calledWith(sinon.match({
192195
...values(0),
193196
...values(1),
194197
...values(2),
195198
}))
199+
expect(recordStub.save).to.have.been.calledWith()
196200
})
197201
})
198202

@@ -226,19 +230,22 @@ describe('updateRecordFactory', () => {
226230
},
227231
record: new BaseRecord(oldParams, {} as BaseResource),
228232
} as unknown as ActionContext
229-
sinon.stub(BaseRecord.prototype, 'update')
233+
sinon.stub(BaseRecord.prototype, 'save')
234+
sinon.stub(BaseRecord.prototype, 'storeParams')
235+
sinon.stub(BaseRecord.prototype, 'toJSON')
230236

231237
updateRecord = updateRecordFactory(uploadOptions, provider)
232238
})
233239

234240
it('removes files from the database', async () => {
235241
await updateRecord(response, request, actionContext)
236242

237-
expect(BaseRecord.prototype.update).to.have.been.calledWith({
243+
expect(BaseRecord.prototype.storeParams).to.have.been.calledWith({
238244
'media.key.0': 'key1',
239245
'media.bucket.0': 'bucket1',
240246
'media.type.0': 'mime1',
241247
})
248+
expect(BaseRecord.prototype.save).to.have.been.calledWith()
242249
})
243250

244251
it('removes files from the adapter store', async () => {

0 commit comments

Comments
 (0)