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

863 feat accordion group #864

Merged
merged 15 commits into from
Oct 26, 2023
Merged
Show file tree
Hide file tree
Changes from 10 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
@@ -0,0 +1,19 @@
<section
data-testid="ion-accordion-item"
[class.open]="show"
[class.close]="!show"
tabindex="0"
>
<header (click)="toggle()">
<div data-testid="ion-accordion-item__header">
<ng-container
[ngTemplateOutlet]="templateHeader"
[ngTemplateOutletContext]="{ $implicit: data }"
></ng-container>
</div>
<ion-icon *ngIf="show" [type]="show ? 'semi-up' : 'semi-down'" [size]="iconSize"></ion-icon>
</header>
<main *ngIf="show" data-testid="ion-accordion-item__main">
<ng-content></ng-content>
</main>
</section>
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
@import '../../../styles/index.scss';

@mixin accordion-style($bgColor, $color) {
header {
color: $color;
border-bottom: 1px solid $color;
background-color: $bgColor;

ion-icon {
::ng-deep svg {
fill: $color;
}
}
}
}

section {
@include accordion-style($neutral-1, $neutral-7);
header,
main {
padding: 16px 20px;
}

header {
display: flex;
justify-content: space-between;
align-items: center;
min-height: 64px;
box-sizing: border-box;
cursor: pointer;

div {
div {
font-size: 16px;
font-weight: 600;
line-height: 24px;
}
}
}

main {
background-color: $neutral-1;
}

&:hover {
@include accordion-style($neutral-2, $primary-3);
}

&:active {
@include accordion-style($primary-2, $primary-4);
}

&:focus-visible {
outline: 2px solid $primary-4;
}

&.open {
@include accordion-style($primary-1, $primary-4);
}

&.close {
header {
border-bottom: 1px solid $neutral-4;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { screen, fireEvent, render } from '@testing-library/angular';
import { Component, NgModule } from '@angular/core';
import { IonAccordionModule } from '../accordion.module';
import { TestBed, ComponentFixture } from '@angular/core/testing';
import { CommonModule } from '@angular/common';
import { IonIconModule } from '../../icon/icon.module';
import { IonAccordionItemComponent } from './accordion-item.component';
import { IonAccordionItemProps } from '../../core/types';
import { SafeAny } from '../../utils/safe-any';

@Component({
template: `<ion-accordion-item [templateHeader]="customHeader" [data]="data">
<p data-testid="ion-accordion-item__main-paragraph">Context Main</p>
</ion-accordion-item>
<ng-template #customHeader> {{ data.name }}</ng-template>`,
})
class AccordionItemTestComponent {
data = { name: 'Accordion header' };
}

@NgModule({
declarations: [AccordionItemTestComponent],
imports: [CommonModule, IonAccordionModule, IonIconModule],
})
class AccordionTestModule {}

describe('IonAccordionItem', () => {
let accordionTestComponent!: AccordionItemTestComponent;
let fixture!: ComponentFixture<AccordionItemTestComponent>;
beforeEach(async () => {
TestBed.configureTestingModule({
imports: [AccordionTestModule],
}).compileComponents();
fixture = TestBed.createComponent(AccordionItemTestComponent);
accordionTestComponent = fixture.componentInstance;
fixture.detectChanges();
});

afterEach(async () => {
fixture.destroy();
});

it('should render ion-accordion-item', async () => {
expect(screen.getByTestId('ion-accordion-item')).toBeTruthy();
});

it('should render the header with the name Brisanet', async () => {
const accordionHeader = 'Brisanet';
accordionTestComponent.data.name = accordionHeader;
fixture.detectChanges();
expect(screen.getByTestId('ion-accordion-item__header')).toHaveTextContent(
accordionHeader
);
});

it('should render main when clicking on header', async () => {
const header = screen.getByTestId('ion-accordion-item__header');
fireEvent.click(header);
fixture.detectChanges();
expect(screen.getByTestId('ion-accordion-item__main')).toBeTruthy();
expect(
screen.getByTestId('ion-accordion-item__main-paragraph')
).toHaveTextContent('Context Main');
});

it('should not render main when clicking on header twice', async () => {
const header = screen.getByTestId('ion-accordion-item__header');
fireEvent.click(header);
fireEvent.click(header);
fixture.detectChanges();
expect(screen.queryByTestId('ion-accordion-item__main')).not.toBeTruthy();
});
});

describe('IonAccordionItem - throw an error', () => {
const sut = async (
customProps: IonAccordionItemProps = {} as SafeAny
): Promise<void> => {
await render(IonAccordionItemComponent, {
componentProperties: { ...customProps },
imports: [CommonModule, IonIconModule],
});
};

it('should throw an error when templateHeader propertie do not exist', async () => {
try {
await sut();
} catch (error) {
expect(error.message).toBe(
'The name or templateHeader properties were not set correctly'
);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good case 👏

});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import {
Component,
Input,
OnInit,
Output,
EventEmitter,
TemplateRef,
} from '@angular/core';
import { SafeAny } from '../../utils/safe-any';

@Component({
selector: 'ion-accordion-item',
templateUrl: './accordion-item.component.html',
styleUrls: ['./accordion-item.component.scss'],
})
export class IonAccordionItemComponent implements OnInit {
@Input() templateHeader: TemplateRef<HTMLElement>;
@Input() show? = false;
@Input() data?: SafeAny;
@Output() activeChange? = new EventEmitter<void>();

iconSize = 24;

ngOnInit(): void {
if (!this.templateHeader) {
throw new Error(
'The name or templateHeader properties were not set correctly'
);
}
}

toggle(): void {
this.show = !this.show;

this.activeChange.emit();
}
}
39 changes: 14 additions & 25 deletions projects/ion/src/lib/accordion/accordion.component.html
Original file line number Diff line number Diff line change
@@ -1,26 +1,15 @@
<section
data-testid="ion-accordion"
[class.open]="show"
[class.close]="!show"
tabindex="0"
>
<header (click)="toggle()">
<div>
<div
*ngIf="!templateHeader; else headerCustom"
data-testid="ion-accordion__header-name"
>
{{ name }}
</div>
<ng-template
#headerCustom
[ngTemplateOutlet]="templateHeader"
></ng-template>
</div>
<ion-icon *ngIf="show" type="semi-up" [size]="iconSize"></ion-icon>
<ion-icon *ngIf="!show" type="semi-down" [size]="iconSize"></ion-icon>
</header>
<main *ngIf="show" data-testid="ion-accordion__main">
<ng-content></ng-content>
</main>
<section data-testid="ion-accordion">
<ion-accordion-item
*ngFor="let accordion of accordions; let i = index"
[show]="accordion.show"
[data]="accordion"
[templateHeader]="templateHeader"
(activeChange)="toggle(i)"
>
<ng-container
*ngIf="templateBody"
[ngTemplateOutlet]="templateBody"
[ngTemplateOutletContext]="{ $implicit: accordion }"
></ng-container>
</ion-accordion-item>
</section>
74 changes: 23 additions & 51 deletions projects/ion/src/lib/accordion/accordion.component.scss
Original file line number Diff line number Diff line change
@@ -1,66 +1,38 @@
@import '../../styles/index.scss';

@mixin accordion-style($bgColor, $color) {
header {
color: $color;
border-bottom: 1px solid $color;
background-color: $bgColor;

ion-icon {
::ng-deep svg {
fill: $color;
section {
::ng-deep ion-accordion-item:first-child {
section {
header {
border-radius: 8px 8px 0 0;
}
}
}
}

section {
@include accordion-style($neutral-1, $neutral-7);
header,
main {
padding: 16px 20px;
}

header {
display: flex;
justify-content: space-between;
align-items: center;
min-height: 64px;
box-sizing: border-box;
cursor: pointer;

div {
div {
font-size: 16px;
font-weight: 600;
line-height: 24px;
::ng-deep ion-accordion-item {
section {
header,
main {
border: 1px solid $neutral-4;
}
}
}

main {
background-color: $neutral-1;
}

&:hover {
@include accordion-style($neutral-2, $primary-3);
}

&:active {
@include accordion-style($primary-2, $primary-4);
}

&:focus-visible {
outline: 2px solid $primary-4;
}
::ng-deep ion-accordion-item:last-child {
section {
header {
border-radius: 0 0 8px 8px;
}
}

&.open {
@include accordion-style($primary-1, $primary-4);
}
section.open {
header {
border-radius: 0;
}

&.close {
header {
border-bottom: 1px solid $neutral-4;
main {
border-radius: 0 0 8px 8px;
}
}
}
}
Loading