Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(progress): adiciona suporte a ações customizadas #2343

Merged
merged 1 commit into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
PoMultiselectFilterMode,
PoMultiselectLiterals,
PoSwitchLabelPosition,
PoUploadFile,
PoUploadFileRestrictions,
PoUploadLiterals
} from '../../po-field';
Expand All @@ -20,6 +21,7 @@ import { PoLookupColumn } from '../../po-field/po-lookup/interfaces/po-lookup-co
import { PoMultiselectOption } from '../../po-field/po-multiselect/po-multiselect-option.interface';
import { PoSelectOption } from '../../po-field/po-select/po-select-option.interface';
import { ForceBooleanComponentEnum, ForceOptionComponentEnum } from '../po-dynamic-field-force-component.enum';
import { PoProgressAction } from '../../po-progress/';

import { Observable } from 'rxjs';
import { PoDynamicField } from '../po-dynamic-field.interface';
Expand Down Expand Up @@ -691,6 +693,46 @@ export interface PoDynamicFormField extends PoDynamicField {
*/
showRequired?: boolean;

/**
* Define uma ação personalizada no componente `po-upload`, adicionando um botão no canto inferior direito
* de cada barra de progresso associada aos arquivos enviados ou em envio.
*
* **Componente compatível**: `po-upload`,
*
* **Exemplo de configuração**:
* ```typescript
* customAction: {
* label: 'Baixar',
* icon: 'ph-download',
* type: 'default',
* visible: true,
* disabled: false
* };
* ```
*/
customAction?: PoProgressAction;

/**
* Evento emitido ao clicar na ação personalizada configurada no `p-custom-action`.
*
* **Componente compatível**: `po-upload`,
*
* Este evento é emitido quando o botão de ação personalizada é clicado na barra de progresso associada a um arquivo.
* O arquivo relacionado à barra de progresso será passado como parâmetro do evento, permitindo executar operações específicas para aquele arquivo.
*
* **Parâmetro do evento**:
* - `file`: O arquivo associado ao botão de ação. Este objeto é da classe `PoUploadFile` e contém informações sobre o arquivo, como nome, status e progresso.
*
* **Exemplo de uso**:
* ```typescript
* customActionClick: (file: PoUploadFile) => {
* console.log('Ação personalizada clicada para o arquivo:', file.name);
* // Lógica de download ou outra ação relacionada ao arquivo
* }
* ```
*/
customActionClick?: (file: PoUploadFile) => void;

/**
* Evento será disparado quando ocorrer algum erro no envio do arquivo.
* > Por parâmetro será passado o objeto do retorno que é do tipo `HttpErrorResponse`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,8 @@
[p-label]="field.label"
[p-literals]="field.literals"
[name]="field.property"
[p-custom-action]="field.customAction"
(p-custom-action-click)="field.customActionClick($event)"
(p-error)="field.onError($event)"
(p-success)="field.onSuccess($event)"
(p-upload)="field.onUpload($event)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import {
PoDynamicFormFieldChanged,
PoDynamicFormValidation,
PoNotificationService,
ForceBooleanComponentEnum
ForceBooleanComponentEnum,
PoUploadFile
} from '@po-ui/ng-components';
import { PoDynamicFormContainerService } from './sample-po-dynamic-form-container.service';

Expand Down Expand Up @@ -193,7 +194,11 @@ export class SamplePoDynamicFormContainerComponent implements OnInit {
gridSmColumns: 12,
label: 'Upload your background',
optional: true,
url: 'https://po-sample-api.onrender.com/v1/uploads/addFile'
url: 'https://po-sample-api.onrender.com/v1/uploads/addFile',
customAction: { icon: 'ph ph-download', visible: true },
customActionClick: (file: PoUploadFile) => {
console.log('Iniciar download para o arquivo:', file.name);
}
}
];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import * as ValidatorsFunctions from '../validators';
import { PoUploadBaseComponent, poUploadLiteralsDefault } from './po-upload-base.component';
import { PoUploadFile } from './po-upload-file';
import { PoUploadService } from './po-upload.service';
import { PoProgressAction } from '../../po-progress';

@Component({
selector: 'po-upload',
Expand Down Expand Up @@ -501,6 +502,15 @@ describe('PoUploadBaseComponent:', () => {

expect(files.splice).not.toHaveBeenCalled();
});

it('callCustomAction: should emit customActionClick event with the provided file', () => {
const mockFile = { name: 'mock-file.txt', size: 12345 } as PoUploadFile;
spyOn(component.customActionClick, 'emit');

component.customActionClick.emit(mockFile);

expect(component.customActionClick.emit).toHaveBeenCalledWith(mockFile);
});
});

describe('Properties:', () => {
Expand Down Expand Up @@ -770,5 +780,45 @@ describe('PoUploadBaseComponent:', () => {

expect(component.isMultiple).toBe(true);
});

it('p-custom-action: should assign a valid PoProgressAction object', () => {
const validAction: PoProgressAction = {
label: 'Download',
icon: 'ph-download',
type: 'default',
disabled: false,
visible: true
};

component.customAction = validAction;

expect(component.customAction).toEqual(validAction);
});

it('p-custom-action: should handle undefined or null values for customAction', () => {
const invalidValues = [null, undefined];
invalidValues.forEach(value => {
component.customAction = value;
fixture.detectChanges();

expect(component.customAction).toBeFalsy();
});
});

it('p-custom-action: should handle partial PoProgressAction objects', () => {
const partialAction: PoProgressAction = { label: 'Partial Action' };
component.customAction = partialAction;

expect(component.customAction).toEqual(partialAction);
});

it('p-custom-action-click: should emit event when called', () => {
const mockFile = { name: 'mock-file.txt', size: 12345 } as PoUploadFile;
spyOn(component.customActionClick, 'emit');

component.customActionClick.emit(mockFile);

expect(component.customActionClick.emit).toHaveBeenCalledWith(mockFile);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { PoUploadLiterals } from './interfaces/po-upload-literals.interface';
import { PoUploadFile } from './po-upload-file';
import { PoUploadStatus } from './po-upload-status.enum';
import { PoUploadService } from './po-upload.service';
import { PoProgressAction } from '../../po-progress';

export const poUploadLiteralsDefault = {
en: <PoUploadLiterals>{
Expand Down Expand Up @@ -239,6 +240,86 @@ export abstract class PoUploadBaseComponent implements ControlValueAccessor, Val
@Input({ alias: 'p-required-url', transform: convertToBoolean })
requiredUrl: boolean = true;

/**
* @optional
*
* @description
*
* Define uma ação personalizada no componente `po-upload`, adicionando um botão no canto inferior direito
* de cada barra de progresso associada aos arquivos enviados ou em envio.
*
* A ação deve implementar a interface **PoProgressAction**, permitindo configurar propriedades como:
* - `label`: Texto do botão.
* - `icon`: Ícone a ser exibido no botão.
* - `type`: Tipo de botão (ex.: `danger` ou `default`).
* - `disabled`: Indica se o botão deve estar desabilitado.
* - `visible`: Indica se o botão deve estar visível.
*
* **Exemplo de uso:**
*
* ```html
* <po-upload
* [p-custom-action]="customAction"
* (p-custom-action-click)="onCustomActionClick($event)">
* </po-upload>
* ```
*
* ```typescript
* customAction: PoProgressAction = {
* label: 'Baixar',
* icon: 'ph ph-download',
* type: 'default',
* visible: true
* };
*
* onCustomActionClick(file: PoUploadFile) {
* console.log(`Ação personalizada clicada para o arquivo: ${file.name}`);
* }
* ```
*/
@Input('p-custom-action') customAction?: PoProgressAction;

/**
* @optional
*
* @description
*
* Evento emitido ao clicar na ação personalizada configurada no `p-custom-action`.
*
* O evento retorna o arquivo associado à barra de progresso onde a ação foi clicada,
* permitindo executar operações específicas para aquele arquivo.
*
* **Exemplo de uso:**
*
* ```html
* <po-upload
* [p-custom-action]="customAction"
* (p-custom-action-click)="onCustomActionClick($event)">
* </po-upload>
* ```
*
* ```typescript
* customAction: PoProgressAction = {
* label: 'Baixar',
* icon: 'ph ph-download',
* type: 'default',
* visible: true
* };
*
* onCustomActionClick(file: PoUploadFile) {
* console.log(`Ação personalizada clicada para o arquivo: ${file.name}`);
* // Lógica para download do arquivo
* this.downloadFile(file);
* }
*
* downloadFile(file: PoUploadFile) {
* // Exemplo de download
* console.log(`Iniciando o download do arquivo: ${file.name}`);
* }
* ```
*/
@Output('p-custom-action-click') customActionClick: EventEmitter<any> = new EventEmitter();

/**
* @optional
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@
[p-status]="progressStatusByFileStatus[file.status]"
[p-text]="file.displayName"
[p-value]="file.percent"
[p-custom-action]="customAction"
(p-custom-action-click)="customClick(file)"
(p-cancel)="cancel(file)"
(p-retry)="uploadFiles([file])"
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { PoUploadFile } from './po-upload-file';
import { PoUploadFileRestrictionsComponent } from './po-upload-file-restrictions/po-upload-file-restrictions.component';
import { PoUploadService } from './po-upload.service';
import { PoUploadStatus } from './po-upload-status.enum';
import { PoProgressAction } from '../../po-progress';

describe('PoUploadComponent:', () => {
let component: PoUploadComponent;
Expand Down Expand Up @@ -1015,6 +1016,28 @@ describe('PoUploadComponent:', () => {
);
expect(component.renderer.removeAttribute).toHaveBeenCalledTimes(1);
});

it('customClick: should emit customActionClick with the provided file if customAction is defined', () => {
const mockFile = { name: 'mock-file.txt' } as PoUploadFile;
component.customAction = { label: 'Download', icon: 'ph-download' } as PoProgressAction;

spyOn(component.customActionClick, 'emit');

component.customClick(mockFile);

expect(component.customActionClick.emit).toHaveBeenCalledWith(mockFile);
});

it('customClick: should not emit customActionClick if customAction is undefined', () => {
const mockFile = { name: 'mock-file.txt' } as PoUploadFile;
component.customAction = undefined;

spyOn(component.customActionClick, 'emit');

component.customClick(mockFile);

expect(component.customActionClick.emit).not.toHaveBeenCalled();
});
});

describe('Templates:', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ import { PoUploadService } from './po-upload.service';
* <file name="sample-po-upload-rs/sample-po-upload-rs.component.html"> </file>
* <file name="sample-po-upload-rs/sample-po-upload-rs.component.ts"> </file>
* </example>
*
* <example name="po-upload-download" title="PO Upload - with Download Button">
* <file name="sample-po-upload-download/sample-po-upload-download.component.html"> </file>
* <file name="sample-po-upload-download/sample-po-upload-download.component.ts"> </file>
* </example>
*/
@Component({
selector: 'po-upload',
Expand Down Expand Up @@ -341,6 +346,12 @@ export class PoUploadComponent extends PoUploadBaseComponent implements AfterVie
);
}

customClick(file: PoUploadFile) {
if (this.customAction) {
this.customActionClick.emit(file);
}
}

private cleanInputValue() {
this.calledByCleanInputValue = true;
this.inputFile.nativeElement.value = '';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<po-upload
name="upload"
p-url="https://po-sample-api.onrender.com/v1/uploads/addFile"
[p-custom-action]="customAction"
(p-custom-action-click)="onCustomActionClick($event)"
[p-multiple]="true"
(p-success)="uploadSuccess()"
></po-upload>
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Component, OnInit } from '@angular/core';
import { PoProgressAction } from '@po-ui/ng-components';

@Component({
selector: 'sample-po-upload-download',
templateUrl: 'sample-po-upload-download.component.html'
})
export class SamplePoUploadDownloadComponent {
customAction: PoProgressAction = {
icon: 'ph ph-download',
type: 'default',
visible: false
};

uploadSuccess() {
this.customAction.visible = true;
}

onCustomActionClick(file: { rawFile: File }) {
if (!file.rawFile) {
console.error('Arquivo inválido ou não encontrado.');
return;
}

this.downloadFile(file.rawFile);
}

downloadFile(rawFile: File) {
// Cria uma URL temporária para o arquivo
const url = URL.createObjectURL(rawFile);

// Cria um link <a> temporário para iniciar o download
const anchor = document.createElement('a');
anchor.href = url;
anchor.download = rawFile.name; // Define o nome do arquivo para o download
anchor.style.display = 'none';

// Adiciona o link ao DOM, aciona o clique e remove o link
document.body.appendChild(anchor);
anchor.click();
document.body.removeChild(anchor);

// Libera a memória utilizada pela URL temporária
URL.revokeObjectURL(url);
}
}
Loading
Loading