From bdeb1c9f2dcd47e19d30af3bdf7e7f5a8691d5d4 Mon Sep 17 00:00:00 2001 From: pedro-martins-brisa <138057813+pedro-martins-brisa@users.noreply.github.com> Date: Thu, 28 Sep 2023 09:36:01 -0300 Subject: [PATCH 1/3] feat: add param to not close popover when click in action #837 (#838) :sparkles: * feat: keep open action * fix: changing interface button props popover * refactor: change action close validator * refactor: change name validator action close * refactor: change private function --------- Co-authored-by: Iury Nogueira --- projects/ion/src/lib/core/types/popover.ts | 7 +++- .../popover/component/popover.component.scss | 1 + .../popover/component/popover.component.ts | 7 ++-- .../keep-open-popover-action.component.ts | 41 +++++++++++++++++++ .../src/lib/popover/popover.directive.spec.ts | 28 +++++++++++-- .../ion/src/lib/popover/popover.directive.ts | 39 +++++++++++------- stories/PopoverDirectiveKeepAction.stories.ts | 41 +++++++++++++++++++ 7 files changed, 142 insertions(+), 22 deletions(-) create mode 100644 projects/ion/src/lib/popover/mock/keep-open-popover-action.component.ts create mode 100644 stories/PopoverDirectiveKeepAction.stories.ts diff --git a/projects/ion/src/lib/core/types/popover.ts b/projects/ion/src/lib/core/types/popover.ts index 1d9860593..424b1306e 100644 --- a/projects/ion/src/lib/core/types/popover.ts +++ b/projects/ion/src/lib/core/types/popover.ts @@ -1,4 +1,5 @@ import { TemplateRef } from '@angular/core'; + import { IonButtonProps } from './button'; import { IconType } from './icon'; @@ -16,10 +17,14 @@ export enum PopoverPosition { DEFAULT = 'bottomRight', } +export interface PopoverButtonsProps extends IonButtonProps { + keepOpenAfterAction?: boolean; +} + export interface PopoverProps { ionPopoverTitle: string; ionPopoverBody: TemplateRef; - ionPopoverActions?: IonButtonProps[]; + ionPopoverActions?: PopoverButtonsProps[]; ionPopoverIcon?: IconType; ionPopoverIconColor?: string; ionPopoverIconClose?: boolean; diff --git a/projects/ion/src/lib/popover/component/popover.component.scss b/projects/ion/src/lib/popover/component/popover.component.scss index 2b5d01baa..cbf3dfcad 100644 --- a/projects/ion/src/lib/popover/component/popover.component.scss +++ b/projects/ion/src/lib/popover/component/popover.component.scss @@ -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); diff --git a/projects/ion/src/lib/popover/component/popover.component.ts b/projects/ion/src/lib/popover/component/popover.component.ts index 81f17ef21..0bae1b74e 100644 --- a/projects/ion/src/lib/popover/component/popover.component.ts +++ b/projects/ion/src/lib/popover/component/popover.component.ts @@ -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'; @@ -15,7 +16,7 @@ export class IonPopoverComponent { @Input() ionPopoverTitle: string; @Input() ionPopoverKeep: boolean; @Input() ionPopoverBody: TemplateRef; - @Input() ionPopoverActions?: IonButtonProps[]; + @Input() ionPopoverActions?: PopoverButtonsProps[]; @Input() ionPopoverIcon?: IconType; @Input() ionPopoverIconColor? = PRIMARY_6; @Input() ionPopoverIconClose = false; diff --git a/projects/ion/src/lib/popover/mock/keep-open-popover-action.component.ts b/projects/ion/src/lib/popover/mock/keep-open-popover-action.component.ts new file mode 100644 index 000000000..b664a3f98 --- /dev/null +++ b/projects/ion/src/lib/popover/mock/keep-open-popover-action.component.ts @@ -0,0 +1,41 @@ +import { Component } from '@angular/core'; +import { PopoverPosition } from '../../core/types/popover'; + +@Component({ + template: ` + +
+ + + {{ args.ionPopoverBody }} +
+ `, +}) +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 }, + ], + }; +} diff --git a/projects/ion/src/lib/popover/popover.directive.spec.ts b/projects/ion/src/lib/popover/popover.directive.spec.ts index 5c6102820..44b1047c7 100644 --- a/projects/ion/src/lib/popover/popover.directive.spec.ts +++ b/projects/ion/src/lib/popover/popover.directive.spec.ts @@ -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'; @@ -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; @@ -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)); @@ -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 () => { diff --git a/projects/ion/src/lib/popover/popover.directive.ts b/projects/ion/src/lib/popover/popover.directive.ts index b22460b44..9c5d4006c 100644 --- a/projects/ion/src/lib/popover/popover.directive.ts +++ b/projects/ion/src/lib/popover/popover.directive.ts @@ -1,32 +1,33 @@ +import { DOCUMENT } from '@angular/common'; import { - Directive, - Input, - HostListener, + ApplicationRef, ComponentFactoryResolver, - Injector, - Inject, ComponentRef, - ApplicationRef, - Output, + Directive, EventEmitter, - ViewContainerRef, + HostListener, + Inject, + Injector, + Input, OnDestroy, + Output, TemplateRef, + ViewContainerRef, } from '@angular/core'; -import { DOCUMENT } from '@angular/common'; +import { pick } from 'lodash'; + +import { IconType } from '../core/types'; +import { PopoverButtonsProps, PopoverPosition } from '../core/types/popover'; import { SafeAny } from './../utils/safe-any'; import { IonPopoverComponent } from './component/popover.component'; -import { PopoverPosition } from '../core/types/popover'; import { getPositionsPopover } from './utilsPopover'; -import { IonButtonProps, IconType } from '../core/types'; -import { pick } from 'lodash'; @Directive({ selector: '[ionPopover]' }) export class IonPopoverDirective implements OnDestroy { @Input() ionPopoverTitle: string; @Input() ionPopoverKeep = false; @Input() ionPopoverBody: TemplateRef; - @Input() ionPopoverActions?: IonButtonProps[]; + @Input() ionPopoverActions?: PopoverButtonsProps[]; @Input() ionPopoverIcon?: IconType; @Input() ionPopoverIconColor?: string; @Input() ionPopoverIconClose? = false; @@ -89,14 +90,22 @@ export class IonPopoverDirective implements OnDestroy { ['ionOnSecondAction', this.ionOnSecondAction], ['ionOnClose', this.ionOnClose], ]; - eventSubscriptions.forEach(([event, emitter]) => { + eventSubscriptions.forEach(([event, emitter], index) => { popoverInstance[event].subscribe(() => { - this.closePopover(); + this.handlePopoverAction(index); + emitter.emit(); }); }); } + handlePopoverAction(index: number): void { + const action = this.ionPopoverActions && this.ionPopoverActions[index]; + if (!action || !action.keepOpenAfterAction) { + this.closePopover(); + } + } + setComponentPosition(hostElement: DOMRect): void { const hostPositions = pick(hostElement, ['left', 'right', 'top', 'bottom']); const positions = getPositionsPopover( diff --git a/stories/PopoverDirectiveKeepAction.stories.ts b/stories/PopoverDirectiveKeepAction.stories.ts new file mode 100644 index 000000000..bf1d9efcd --- /dev/null +++ b/stories/PopoverDirectiveKeepAction.stories.ts @@ -0,0 +1,41 @@ +import { CommonModule } from '@angular/common'; +import { Meta, Story } from '@storybook/angular/types-6-0'; + +import { PopoverPosition } from '../projects/ion/src/lib/core/types/popover'; +import { KeepOpenPopoverActionComponent } from '../projects/ion/src/lib/popover/mock/keep-open-popover-action.component'; +import { + IonPopoverModule, + IonSharedModule, +} from '../projects/ion/src/public-api'; + +const Template: Story = ( + args: KeepOpenPopoverActionComponent +) => ({ + component: KeepOpenPopoverActionComponent, + props: { + ...args, + }, + moduleMetadata: { + declarations: [KeepOpenPopoverActionComponent], + imports: [CommonModule, IonSharedModule, IonPopoverModule], + entryComponents: [KeepOpenPopoverActionComponent], + }, +}); + +export const DirectiveWithActionsKeepAction = Template.bind({}); +DirectiveWithActionsKeepAction.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 }, + ], +}; + +export default { + title: 'Ion/Data Display/Popover', + component: KeepOpenPopoverActionComponent, +} as Meta; From 2acb462c598484235a036bbe57f22446dfa315da Mon Sep 17 00:00:00 2001 From: larissa-kamily-brisa <138057627+larissa-kamily-brisa@users.noreply.github.com> Date: Thu, 28 Sep 2023 14:00:50 -0300 Subject: [PATCH 2/3] feat: adding close when options is selected behavior (#834) (#836) :hammer: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: adding close when options is selected behavior (#834) * refactor: config close on select (#834) * refactor: close on select verification * test: correcting test of close on select (#834) --------- Co-authored-by: “larissa-kamily-brisa” <“larissa.kamily@grupobrisanet.com.br”> Co-authored-by: Iury Nogueira --- projects/ion/src/lib/core/types/sidebar.ts | 1 + .../src/lib/sidebar/sidebar.component.html | 4 +-- .../ion/src/lib/sidebar/sidebar.component.ts | 9 ++++- projects/ion/src/lib/sidebar/sidebar.spec.ts | 34 ++++++++++++++++++- stories/Sidebar.stories.ts | 1 + 5 files changed, 45 insertions(+), 4 deletions(-) diff --git a/projects/ion/src/lib/core/types/sidebar.ts b/projects/ion/src/lib/core/types/sidebar.ts index 8e708abaa..30eacaf50 100644 --- a/projects/ion/src/lib/core/types/sidebar.ts +++ b/projects/ion/src/lib/core/types/sidebar.ts @@ -12,4 +12,5 @@ export interface IonSidebarProps { logo: string; logoAction?: () => void; items: (Item & { options?: [Item, ...Item[]] })[]; + closeOnSelect?: boolean; } diff --git a/projects/ion/src/lib/sidebar/sidebar.component.html b/projects/ion/src/lib/sidebar/sidebar.component.html index 0c0c4a64e..7d3eb0a30 100644 --- a/projects/ion/src/lib/sidebar/sidebar.component.html +++ b/projects/ion/src/lib/sidebar/sidebar.component.html @@ -16,7 +16,7 @@ iconType="left3" size="lg" type="ghost" - (ionOnClick)="toggleVisibility()" + (ionOnClick)="toggleSidebarVisibility()" >
@@ -53,6 +53,6 @@ iconType="sandwich" size="lg" type="ghost" - (ionOnClick)="toggleVisibility()" + (ionOnClick)="toggleSidebarVisibility()" > diff --git a/projects/ion/src/lib/sidebar/sidebar.component.ts b/projects/ion/src/lib/sidebar/sidebar.component.ts index eff85832f..b5093bf4d 100644 --- a/projects/ion/src/lib/sidebar/sidebar.component.ts +++ b/projects/ion/src/lib/sidebar/sidebar.component.ts @@ -11,6 +11,7 @@ export class IonSidebarComponent { @Input() logo!: string; @Input() logoAction?: () => void; @Input() items: IonSidebarProps['items'] = []; + @Input() closeOnSelect = false; public closed = true; @@ -27,7 +28,7 @@ export class IonSidebarComponent { } }; - public toggleVisibility(): void { + public toggleSidebarVisibility(): void { this.closed = !this.closed; if (!this.closed) { setTimeout(() => { @@ -41,10 +42,16 @@ export class IonSidebarComponent { public itemSelected(itemIndex: number): void { selectItemByIndex(this.items, itemIndex); + if (this.closeOnSelect) { + this.toggleSidebarVisibility(); + } } public itemOnGroupSelected(groupIndex: number): void { unselectAllItems(this.items, groupIndex); + if (this.closeOnSelect) { + this.toggleSidebarVisibility(); + } } public groupSelected(groupIndex: number): void { diff --git a/projects/ion/src/lib/sidebar/sidebar.spec.ts b/projects/ion/src/lib/sidebar/sidebar.spec.ts index 912791556..94ecb55b5 100644 --- a/projects/ion/src/lib/sidebar/sidebar.spec.ts +++ b/projects/ion/src/lib/sidebar/sidebar.spec.ts @@ -21,6 +21,8 @@ const getByTestId = (key: keyof typeof components): HTMLElement => { const logo: IonSidebarProps['logo'] = 'logo.svg'; +const closeOnSelectConfig: IonSidebarProps['closeOnSelect'] = true; + const actionMock = jest.fn(); const items: IonSidebarProps['items'] = [ @@ -54,7 +56,7 @@ const items: IonSidebarProps['items'] = [ ]; const sut = async ( - props: IonSidebarProps = { logo: '', items: [] } + props: IonSidebarProps = { logo: '', items: [], closeOnSelect: false } ): Promise => { await render(IonSidebarComponent, { componentProperties: { ...props }, @@ -269,4 +271,34 @@ describe('Sidebar', () => { expect(actionMock).not.toHaveBeenCalled(); }); }); + describe('Close on select config', () => { + const selectedItemClass = 'ion-sidebar-item--selected'; + let item1: HTMLElement; + let itemGroup2: HTMLElement; + beforeEach(async () => { + await sut({ items: items, logo, closeOnSelect: closeOnSelectConfig }); + userEvent.click(getByTestId('toggleVisibility').firstElementChild); + expect(getByTestId('sidebar')).toHaveClass('ion-sidebar--opened'); + }); + it('should close sidebar when option is clicked', async () => { + item1 = screen.getByRole('button', { + name: items[0].title, + }); + + userEvent.click(item1); + expect(item1).toHaveClass(selectedItemClass); + expect(getByTestId('sidebar')).not.toHaveClass('ion-sidebar--opened'); + }); + it('should close sidebar when options is clicked', async () => { + userEvent.click(screen.getByTestId('sidebar-group__toggle-icon')); + + itemGroup2 = screen.getByRole('button', { + name: items[2].options[1].title, + }); + + userEvent.click(itemGroup2); + expect(itemGroup2).toHaveClass(selectedItemClass); + expect(getByTestId('sidebar')).not.toHaveClass('ion-sidebar--opened'); + }); + }); }); diff --git a/stories/Sidebar.stories.ts b/stories/Sidebar.stories.ts index 0f496ce08..fa52c47a1 100644 --- a/stories/Sidebar.stories.ts +++ b/stories/Sidebar.stories.ts @@ -25,6 +25,7 @@ const Template: Story = (args: IonSidebarComponent) => ({ export const Default = Template.bind({}); Default.args = { logo: require('./assets/sidebar-logo.svg'), + closeOnSelect: false, items: [ { title: 'Fila de atendimento', From 63024a619d1d1709a1da6d7228db32d4e15c1e41 Mon Sep 17 00:00:00 2001 From: Allan Oliveira <138060158+allan-chagas-brisa@users.noreply.github.com> Date: Thu, 28 Sep 2023 17:09:43 -0300 Subject: [PATCH 3/3] 835 add left button to ion header (#840) * feat: modal header button * fix: changes on mock * fix: header spacing * feat: modal body content emition and test adjustments * refactor: readonly added to the subject * docs: service subscription * test: pr sugested adjustments * refactor: pr sugested adjustments --------- Co-authored-by: Iury Nogueira --- .../lib/modal/component/modal.component.html | 21 ++++++ .../lib/modal/component/modal.component.scss | 2 + .../modal/component/modal.component.spec.ts | 70 +++++++++++++++++++ .../lib/modal/component/modal.component.ts | 5 ++ .../ion/src/lib/modal/modal.service.spec.ts | 18 +++++ projects/ion/src/lib/modal/modal.service.ts | 12 ++++ .../src/lib/modal/models/modal.interface.ts | 10 ++- stories/Modal.stories.mdx | 28 ++++++++ 8 files changed, 165 insertions(+), 1 deletion(-) diff --git a/projects/ion/src/lib/modal/component/modal.component.html b/projects/ion/src/lib/modal/component/modal.component.html index 975a5be1c..c952b0427 100644 --- a/projects/ion/src/lib/modal/component/modal.component.html +++ b/projects/ion/src/lib/modal/component/modal.component.html @@ -16,6 +16,27 @@ aria-modal="true" >
+

{{ configuration.title }}

{ 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(); + }); + }); }); diff --git a/projects/ion/src/lib/modal/component/modal.component.ts b/projects/ion/src/lib/modal/component/modal.component.ts index b41748dad..b83585328 100644 --- a/projects/ion/src/lib/modal/component/modal.component.ts +++ b/projects/ion/src/lib/modal/component/modal.component.ts @@ -34,6 +34,7 @@ export class IonModalComponent implements OnInit, OnDestroy { @Input() configuration: IonModalConfiguration = {}; @Output() + ionOnHeaderButtonAction = new EventEmitter(); ionOnClose = new EventEmitter(); public DEFAULT_WIDTH = 500; @@ -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); diff --git a/projects/ion/src/lib/modal/modal.service.spec.ts b/projects/ion/src/lib/modal/modal.service.spec.ts index b68b08a7d..c203ac137 100644 --- a/projects/ion/src/lib/modal/modal.service.spec.ts +++ b/projects/ion/src/lib/modal/modal.service.spec.ts @@ -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', + }); + }); }); diff --git a/projects/ion/src/lib/modal/modal.service.ts b/projects/ion/src/lib/modal/modal.service.ts index 5d2bbe73e..dcfb07943 100644 --- a/projects/ion/src/lib/modal/modal.service.ts +++ b/projects/ion/src/lib/modal/modal.service.ts @@ -21,6 +21,7 @@ import { providedIn: 'root', }) export class IonModalService { + public readonly ionOnHeaderButtonAction = new Subject(); private modalComponentRef!: ComponentRef; private componentSubscriber!: Subject; @@ -62,6 +63,13 @@ export class IonModalService { this.emitValueAndCloseModal(valueFromModal); } ); + + this.modalComponentRef.instance.ionOnHeaderButtonAction.subscribe( + (valueFromModal: IonModalResponse) => { + this.emitHeaderAction(valueFromModal); + } + ); + this.componentSubscriber = new Subject(); return this.componentSubscriber.asObservable(); } @@ -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); diff --git a/projects/ion/src/lib/modal/models/modal.interface.ts b/projects/ion/src/lib/modal/models/modal.interface.ts index a254decd4..e6fb34b31 100644 --- a/projects/ion/src/lib/modal/models/modal.interface.ts +++ b/projects/ion/src/lib/modal/models/modal.interface.ts @@ -1,4 +1,4 @@ -import { IonButtonProps } from '../../core/types'; +import { IconType, IonButtonProps } from '../../core/types'; import { SafeAny } from '../../utils/safe-any'; export interface IonModalConfiguration { @@ -8,6 +8,7 @@ export interface IonModalConfiguration { showOverlay?: boolean; overlayCanDismiss?: boolean; ionParams?: SafeAny; + headerButton?: IonModalHeaderButton; footer?: IonModalFooterConfiguration; } @@ -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; } diff --git a/stories/Modal.stories.mdx b/stories/Modal.stories.mdx index 92827c5ad..c9074a076 100644 --- a/stories/Modal.stories.mdx +++ b/stories/Modal.stories.mdx @@ -49,6 +49,14 @@ Função responsável pela renderização do modal. Ela recebe como parâmetros: height="350px" args={{ componentToBody: SelectMockComponent, + modalConfig: { + headerButton: { + icon: 'left', + label: 'test', + disabled: () => false, + hidden: () => false, + }, + }, }} decorators={[ moduleMetadata({ @@ -64,6 +72,26 @@ Função responsável pela renderização do modal. Ela recebe como parâmetros:
+## ionOnHeaderButtonAction + +Um subject que irá informar ao Body Component do clique no HeaderLeftButton. O usuário deve instanciar o ModalService no componente usado como body e se inscrever nesse subject. + +> Ideal para casos onde precisamos que o bodyComponent altere seu estado a partir da action do headerButton. Veja o exemplo abaixo: + + + ## EmitValueAndCloseModal Recebe como parâmetro um valor, seguindo a interface _IonModalResponse_, a ser emitido e faz o fechamento do modal.