Skip to content

Commit

Permalink
feat(progress): adiciona suporte a ações customizadas
Browse files Browse the repository at this point in the history
Adiciona suporte a ações customizadas no componente `po-progress` por meio das novas propriedades `p-custom-action` e `p-custom-action-click`.

Permite configurar ícone, label, tipo, visibilidade e estado de desabilitado do botão customizado.
Integra a funcionalidade aos componentes `po-upload` e `po-dynamic-form`, garantindo que a propriedade `p-custom-action-click` retorne o arquivo associado à ação.

Fixes DTHFUI-10272
  • Loading branch information
bruno-severino committed Dec 27, 2024
1 parent 0b12f25 commit d9f46ee
Show file tree
Hide file tree
Showing 22 changed files with 900 additions and 64 deletions.
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

0 comments on commit d9f46ee

Please sign in to comment.