Skip to content

Commit

Permalink
Merge branch 'main' into 833-add-alert-variation-with-description
Browse files Browse the repository at this point in the history
  • Loading branch information
iurynogueira authored Sep 29, 2023
2 parents 7f259fb + 63024a6 commit 576c3be
Show file tree
Hide file tree
Showing 20 changed files with 352 additions and 27 deletions.
7 changes: 6 additions & 1 deletion projects/ion/src/lib/core/types/popover.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { TemplateRef } from '@angular/core';

import { IonButtonProps } from './button';
import { IconType } from './icon';

Expand All @@ -16,10 +17,14 @@ export enum PopoverPosition {
DEFAULT = 'bottomRight',
}

export interface PopoverButtonsProps extends IonButtonProps {
keepOpenAfterAction?: boolean;
}

export interface PopoverProps {
ionPopoverTitle: string;
ionPopoverBody: TemplateRef<void>;
ionPopoverActions?: IonButtonProps[];
ionPopoverActions?: PopoverButtonsProps[];
ionPopoverIcon?: IconType;
ionPopoverIconColor?: string;
ionPopoverIconClose?: boolean;
Expand Down
1 change: 1 addition & 0 deletions projects/ion/src/lib/core/types/sidebar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ export interface IonSidebarProps {
logo: string;
logoAction?: () => void;
items: (Item & { options?: [Item, ...Item[]] })[];
closeOnSelect?: boolean;
}
21 changes: 21 additions & 0 deletions projects/ion/src/lib/modal/component/modal.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,27 @@
aria-modal="true"
>
<header tabindex="0">
<ion-button
*ngIf="
configuration.headerButton &&
!(configuration.headerButton.hidden
? configuration.headerButton.hidden()
: false)
"
[iconType]="configuration.headerButton.icon"
[label]="configuration.headerButton.label"
[disabled]="
configuration.headerButton.disabled
? configuration.headerButton.disabled()
: false
"
type="ghost"
[circularButton]="true"
data-testid="header-button"
(ionOnClick)="
emitHeaderButtonAction(this.getChildComponentPropertiesValue())
"
></ion-button>
<h4 data-testid="modalTitle">{{ configuration.title }}</h4>
<ion-button
label="Deletar"
Expand Down
2 changes: 2 additions & 0 deletions projects/ion/src/lib/modal/component/modal.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
display: flex;
justify-content: space-between;
align-items: center;
gap: 8px;

h4 {
margin: 0;
Expand All @@ -54,6 +55,7 @@
font-size: 20px;
line-height: 28px;
color: $neutral-color;
margin-right: auto;
}

.close-icon {
Expand Down
70 changes: 70 additions & 0 deletions projects/ion/src/lib/modal/component/modal.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,4 +196,74 @@ describe('IonModalComponent', () => {
fixture.nativeElement.querySelector('.modal-container').style.width;
expect(modalElement).toBe(`${modalConfig.width}px`);
});

describe('IonModalComponent - Header left button', () => {
const configuration: IonModalConfiguration = {
id: '1',
title: 'Ion Test',

footer: {
showDivider: false,
primaryButton: {
label: 'Ion Cancel',
iconType: 'icon',
},
secondaryButton: {
label: 'Ion Confirm',
iconType: 'icon',
},
},

headerButton: {
icon: 'left',
label: 'voltar',
},
};

it('should not be rendered as default', () => {
expect(screen.queryByTestId('btn-voltar')).not.toBeInTheDocument();
});

it('should emit event when call emitHeaderButtonAction function', () => {
jest.spyOn(component.ionOnHeaderButtonAction, 'emit');
component.emitHeaderButtonAction(
component.getChildComponentPropertiesValue()
);
expect(component.ionOnHeaderButtonAction.emit).toHaveBeenCalled();
});

it('should be visible as default if the config is informed', () => {
component.setConfig(configuration);
fixture.detectChanges();
expect(screen.getByTestId('btn-voltar')).toBeVisible();
});

it('should be enabled as default', () => {
component.setConfig(configuration);
fixture.detectChanges();
expect(screen.getByTestId('btn-voltar')).toBeEnabled();
});

it('should be disabled when informed', () => {
configuration.headerButton.disabled = (): boolean => true;
component.setConfig(configuration);
fixture.detectChanges();
expect(screen.getByTestId('btn-voltar')).toBeDisabled();
});

it('should render the specified icon', () => {
component.setConfig(configuration);
fixture.detectChanges();
const icon = document.getElementById('ion-icon-left');
expect(icon).toBeVisible();
});

it('should be hidden when informed', () => {
configuration.headerButton.hidden = (): boolean => true;

component.setConfig(configuration);
fixture.detectChanges();
expect(screen.queryByTestId('btn-voltar')).not.toBeInTheDocument();
});
});
});
5 changes: 5 additions & 0 deletions projects/ion/src/lib/modal/component/modal.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export class IonModalComponent implements OnInit, OnDestroy {
@Input() configuration: IonModalConfiguration = {};

@Output()
ionOnHeaderButtonAction = new EventEmitter<IonModalResponse | undefined>();
ionOnClose = new EventEmitter<IonModalResponse | undefined>();

public DEFAULT_WIDTH = 500;
Expand Down Expand Up @@ -88,6 +89,10 @@ export class IonModalComponent implements OnInit, OnDestroy {
Object.assign(instance, params);
}

emitHeaderButtonAction(valueToEmit: IonModalResponse | undefined): void {
this.ionOnHeaderButtonAction.emit(valueToEmit);
}

ngOnInit(): void {
this.setDefaultConfig();
const factory = this.resolver.resolveComponentFactory(this.componentToBody);
Expand Down
18 changes: 18 additions & 0 deletions projects/ion/src/lib/modal/modal.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,22 @@ describe('ModalService', () => {
state: 'ceara',
});
});

it('should call emitHeaderAction when ionOnHeaderButtonAction fires', () => {
jest.spyOn(modalService, 'emitHeaderAction');

modalService.open(SelectMockComponent, {
headerButton: {
icon: 'left',
label: 'voltar',
},
});

fireEvent.click(screen.getByTestId('btn-voltar'));
fixture.detectChanges();

expect(modalService.emitHeaderAction).toHaveBeenCalledWith({
state: 'ceara',
});
});
});
12 changes: 12 additions & 0 deletions projects/ion/src/lib/modal/modal.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
providedIn: 'root',
})
export class IonModalService {
public readonly ionOnHeaderButtonAction = new Subject<SafeAny>();
private modalComponentRef!: ComponentRef<IonModalComponent>;
private componentSubscriber!: Subject<IonModalResponse | unknown>;

Expand Down Expand Up @@ -62,6 +63,13 @@ export class IonModalService {
this.emitValueAndCloseModal(valueFromModal);
}
);

this.modalComponentRef.instance.ionOnHeaderButtonAction.subscribe(
(valueFromModal: IonModalResponse) => {
this.emitHeaderAction(valueFromModal);
}
);

this.componentSubscriber = new Subject<IonModalResponse | unknown>();
return this.componentSubscriber.asObservable();
}
Expand All @@ -71,6 +79,10 @@ export class IonModalService {
this.closeModal();
}

emitHeaderAction(valueToEmit: IonModalResponse | unknown): void {
this.ionOnHeaderButtonAction.next(valueToEmit);
}

closeModal(): void {
if (this.modalComponentRef) {
this.appRef.detachView(this.modalComponentRef.hostView);
Expand Down
10 changes: 9 additions & 1 deletion projects/ion/src/lib/modal/models/modal.interface.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { IonButtonProps } from '../../core/types';
import { IconType, IonButtonProps } from '../../core/types';
import { SafeAny } from '../../utils/safe-any';

export interface IonModalConfiguration {
Expand All @@ -8,6 +8,7 @@ export interface IonModalConfiguration {
showOverlay?: boolean;
overlayCanDismiss?: boolean;
ionParams?: SafeAny;
headerButton?: IonModalHeaderButton;

footer?: IonModalFooterConfiguration;
}
Expand All @@ -20,6 +21,13 @@ export interface IonModalFooterConfiguration {
secondaryButton?: IonButtonProps;
}

interface IonModalHeaderButton {
icon: IconType;
label?: string;
disabled?: () => boolean;
hidden?: () => boolean;
}

export interface IonModalResponse {
[key: string]: unknown;
}
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ $arrow-size: 4.5px;
display: flex;
background-color: $neutral-2;
justify-content: space-between;
border-radius: spacing(1) spacing(1) 0 0;
align-items: center;
padding: spacing(1.5) spacing(2);

Expand Down
7 changes: 4 additions & 3 deletions projects/ion/src/lib/popover/component/popover.component.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Component, Input, TemplateRef } from '@angular/core';
import { Subject } from 'rxjs';
import { PopoverPosition } from '../../core/types/popover';
import { IconType, IonButtonProps } from '../../core/types';

import { IconType } from '../../core/types';
import { PopoverButtonsProps, PopoverPosition } from '../../core/types/popover';

const PRIMARY_6 = '#0858ce';

Expand All @@ -15,7 +16,7 @@ export class IonPopoverComponent {
@Input() ionPopoverTitle: string;
@Input() ionPopoverKeep: boolean;
@Input() ionPopoverBody: TemplateRef<void>;
@Input() ionPopoverActions?: IonButtonProps[];
@Input() ionPopoverActions?: PopoverButtonsProps[];
@Input() ionPopoverIcon?: IconType;
@Input() ionPopoverIconColor? = PRIMARY_6;
@Input() ionPopoverIconClose = false;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Component } from '@angular/core';
import { PopoverPosition } from '../../core/types/popover';

@Component({
template: `
<style>
div {
height: 400px;
display: flex;
justify-content: center;
align-items: center;
}
</style>
<div>
<ion-button
ionPopover
[ionPopoverTitle]="args.ionPopoverTitle"
[ionPopoverBody]="BodyTemplate"
[ionPopoverIconClose]="args.ionPopoverIconClose"
[ionPopoverPosition]="args.ionPopoverPosition"
[ionPopoverActions]="args.ionPopoverActions"
label="click me"
>
</ion-button>
<ng-template #BodyTemplate> {{ args.ionPopoverBody }} </ng-template>
</div>
`,
})
export class KeepOpenPopoverActionComponent {
args = {
ionPopoverTitle: 'Desafio na Jornada',
ionPopoverBody:
'Você pode escolher avançar corajosamente para a próxima etapa da jornada ou optar por explorar o caminho anterior.',
ionPopoverPosition: PopoverPosition.DEFAULT,
ionPopoverIconClose: true,
ionPopoverActions: [
{ label: 'voltar', keepOpenAfterAction: true },
{ label: 'continuar', keepOpenAfterAction: true },
],
};
}
28 changes: 25 additions & 3 deletions projects/ion/src/lib/popover/popover.directive.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ import { FormsModule } from '@angular/forms';
import { By } from '@angular/platform-browser';
import { BrowserDynamicTestingModule } from '@angular/platform-browser-dynamic/testing';
import { fireEvent, render, screen } from '@testing-library/angular';

import { IonButtonModule } from '../button/button.module';
import { PopoverPosition } from '../core/types/popover';
import { PopoverButtonsProps, PopoverPosition } from '../core/types/popover';
import { IonDividerModule } from '../divider/divider.module';
import { IonSharedModule } from '../shared.module';
import { IonPopoverComponent } from './component/popover.component';
Expand Down Expand Up @@ -63,7 +64,10 @@ class HostTestComponent {
ionPopoverKeep = false;
ionPopoverIconClose = true;
ionPopoverIcon = 'condominium';
ionPopoverActions = [{ label: 'action 1' }, { label: 'action 2' }];
ionPopoverActions: PopoverButtonsProps[] = [
{ label: 'action 1' },
{ label: 'action 2' },
];

ionOnFirstAction = firstAction;
ionOnSecondAction = secondAction;
Expand Down Expand Up @@ -181,6 +185,24 @@ describe('Directive: popover', () => {
expect(screen.queryByTestId(`popover-${type.dataTestId}`)).toBeNull();
});

it.each([
{ dataTestId: 'action-1', label: 'voltar' },
{ dataTestId: 'action-2', label: 'continuar' },
])(
'should not close pop when click in $label when to have keepOpenAfterAction',
async (type) => {
await sut({
ionPopoverActions: [
{ label: 'voltar', keepOpenAfterAction: true },
{ label: 'continuar', keepOpenAfterAction: true },
],
});
fireEvent.click(screen.getByText(textButton));
fireEvent.click(screen.getByTestId(`btn-${type.label}`));
expect(screen.getByTestId('ion-popover')).toBeInTheDocument();
}
);

it('should emit an event when click on action-1', async () => {
await sut();
fireEvent.click(screen.getByText(textButton));
Expand Down Expand Up @@ -211,7 +233,7 @@ describe('Directive: popover', () => {
await sut();
fireEvent.click(screen.getByText(textButton));
fireEvent.click(document);
expect(screen.queryByTestId('ion-popover')).toBeFalsy();
expect(screen.queryByTestId('ion-popover')).not.toBeInTheDocument();
});

it('should render popover with custom class', async () => {
Expand Down
Loading

0 comments on commit 576c3be

Please sign in to comment.