Say that a file is stored where it is chosen, not on Save
Replacing a file uploads it straight away, while every other input waits for the Save at the foot of the panel. Nothing said so, so a replacement left the footer reading "All changes saved" with Save greyed out - which is exactly what an edit that failed to register looks like. The row now says it, and a test covers the path it describes: Replace reopens the picker and the chosen file uploads on its own. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b4302bf7db
commit
f1305c16e7
|
|
@ -398,6 +398,13 @@
|
|||
word-break: break-all;
|
||||
}
|
||||
|
||||
.inputs-panel-upload-note {
|
||||
margin-top: 4px;
|
||||
color: #94a3b8;
|
||||
font-size: 10.5px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.inputs-panel-items {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
|
|
|||
|
|
@ -277,6 +277,13 @@
|
|||
{{ uploadError }}
|
||||
</span>
|
||||
</div>
|
||||
} @else if (executionInput.provided && !isInputSaving(executionInput.key)) {
|
||||
<!--
|
||||
A file is stored the moment it is chosen, while every other input waits for the Save below.
|
||||
Without saying so, replacing one left the footer reading "All changes saved" with Save greyed -
|
||||
which is what an edit that did not register would look like.
|
||||
-->
|
||||
<div class="inputs-panel-upload-note">Stored as soon as it is chosen; the Save below is for the other inputs.</div>
|
||||
}
|
||||
} @else if (isMultipleInput(executionInput)) {
|
||||
@if (itemsOpen(executionInput)) {
|
||||
|
|
|
|||
|
|
@ -168,6 +168,44 @@ describe('TaskExecutionInputsPanelComponent', () => {
|
|||
expect(text).toContain('Node inputs');
|
||||
});
|
||||
|
||||
it('uploads again when an already-provided file is replaced', async () => {
|
||||
const fixture = await build([makeInput({
|
||||
key: 'global:document',
|
||||
inputName: 'document',
|
||||
type: 'FILE',
|
||||
multiple: false,
|
||||
value: '/tmp/upload-1/plan-01.pdf',
|
||||
provided: true
|
||||
})]);
|
||||
// Nothing is missing, so the group starts closed: open it to reach the row.
|
||||
fixture.componentInstance.toggleGlobals();
|
||||
fixture.detectChanges();
|
||||
|
||||
// A provided file shows what it holds, with Replace rather than a picker to hunt for.
|
||||
expect(fixture.nativeElement.textContent).toContain('plan-01.pdf');
|
||||
const replace = fixture.nativeElement.querySelector('.inputs-panel-upload-replace') as HTMLButtonElement;
|
||||
expect(replace).toBeTruthy();
|
||||
|
||||
const picker = fixture.nativeElement.querySelector('input[type="file"]') as HTMLInputElement;
|
||||
const clicked = vi.fn();
|
||||
picker.addEventListener('click', clicked);
|
||||
replace.click();
|
||||
expect(clicked).toHaveBeenCalled();
|
||||
|
||||
const uploads = vi.fn();
|
||||
fixture.componentInstance.fileInputChange.subscribe(uploads);
|
||||
const replacement = new File(['%PDF-1.7'], 'plan-02.pdf', { type: 'application/pdf' });
|
||||
Object.defineProperty(picker, 'files', { value: [replacement], configurable: true });
|
||||
picker.dispatchEvent(new Event('change'));
|
||||
|
||||
// The replacement uploads on its own: a file is stored where it is chosen, not on a later save.
|
||||
expect(uploads).toHaveBeenCalledTimes(1);
|
||||
expect(uploads.mock.calls[0][0].files[0].name).toBe('plan-02.pdf');
|
||||
// And the row says so, since the footer's Save stays greyed and would otherwise read as an
|
||||
// edit that did not register.
|
||||
expect(fixture.nativeElement.textContent).toContain('Stored as soon as it is chosen');
|
||||
});
|
||||
|
||||
it('offers a single save for every pending edit', async () => {
|
||||
const fixture = await build(
|
||||
[makeInput({ key: 'g:a', inputName: 'a' }), makeInput({ key: 'g:b', inputName: 'b' })],
|
||||
|
|
@ -519,4 +557,5 @@ describe('parseJsonArrayInput', () => {
|
|||
it('converts plain numbers and booleans to text', () => {
|
||||
expect(parseJsonArrayInput('[1, true]').values).toEqual(['1', 'true']);
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue