diff --git a/projects/showcase/src/app/components/accordion/showcase-accordion.component.html b/projects/showcase/src/app/components/accordion/showcase-accordion.component.html
new file mode 100644
index 000000000..86daa8918
--- /dev/null
+++ b/projects/showcase/src/app/components/accordion/showcase-accordion.component.html
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/projects/showcase/src/app/components/accordion/showcase-accordion.component.ts b/projects/showcase/src/app/components/accordion/showcase-accordion.component.ts
new file mode 100644
index 000000000..56cb906b0
--- /dev/null
+++ b/projects/showcase/src/app/components/accordion/showcase-accordion.component.ts
@@ -0,0 +1,10 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'showcase-accordion',
+ templateUrl: 'showcase-accordion.component.html'
+})
+export class ShowcaseAccordion {
+ constructor() {
+ }
+}
\ No newline at end of file
diff --git a/projects/showcase/src/app/components/showcase-components.component.html b/projects/showcase/src/app/components/showcase-components.component.html
index 19195c2cf..0eb018d9d 100644
--- a/projects/showcase/src/app/components/showcase-components.component.html
+++ b/projects/showcase/src/app/components/showcase-components.component.html
@@ -107,6 +107,9 @@
Progress bar
+ Accordion
+
+
Percentage Circle
diff --git a/projects/showcase/src/app/showcase.module.ts b/projects/showcase/src/app/showcase.module.ts
index 449732c0e..4689d6cba 100644
--- a/projects/showcase/src/app/showcase.module.ts
+++ b/projects/showcase/src/app/showcase.module.ts
@@ -115,6 +115,7 @@ import {
} from './components/progress-bars/progressbar-with-text-dialog/showcase-progressbar-with-text-dialog.component';
import { ShowcaseImageViewerComponent } from './components/image-viewer/showcase-image-viewer.component';
import { CdkTreeModule } from '@angular/cdk/tree';
+import { ShowcaseAccordion } from './components/accordion/showcase-accordion.component';
@NgModule({ declarations: [
ShowcaseComponent,
@@ -204,6 +205,7 @@ import { CdkTreeModule } from '@angular/cdk/tree';
ShowcaseInteractiveComponent,
ShowcaseProgressBarWithTextDialog,
ShowcaseImageViewerComponent,
+ ShowcaseAccordion
],
bootstrap: [ShowcaseComponent],
imports: [
diff --git a/projects/systelab-components/package.json b/projects/systelab-components/package.json
index 95b492b14..3560eaf49 100644
--- a/projects/systelab-components/package.json
+++ b/projects/systelab-components/package.json
@@ -1,6 +1,6 @@
{
"name": "systelab-components",
- "version": "18.0.4",
+ "version": "18.1.0",
"license": "MIT",
"keywords": [
"Angular",
diff --git a/projects/systelab-components/src/lib/accordion/README.md b/projects/systelab-components/src/lib/accordion/README.md
new file mode 100644
index 000000000..d45a88d55
--- /dev/null
+++ b/projects/systelab-components/src/lib/accordion/README.md
@@ -0,0 +1,32 @@
+# systelab-accordion
+
+Component to create accordion with internal content.
+
+## Using the template
+
+```html
+
+
+
+```
+The accordion component wraps the internal component, adding accordion functionality that allows us to expand or collapse its content.
+
+If you want the defaults the template will look like:
+
+```html
+
+
+
+```
+
+
+## Properties
+
+| Name | Type | Default | Description |
+| ---- |:------:|:---------:|-------------------------------------------------------------------|
+| headerTitle | string | '' | Sets the header title of the accordion.|
+| preferenceName | string | undefined | The preference name where the isCollapsed state will be stored. |
+| contentMaxHeight | number | 300 | The maximum height of the accordion content. |
+| withOverflow | number | false | When set to true, enables vertical scrolling within the container. |
+| headerColor | number | undefined | The background color of the accordion header. |
+| iconColor | number | undefined | The color of the expand/collapse icon. |
diff --git a/projects/systelab-components/src/lib/accordion/accordion.component.html b/projects/systelab-components/src/lib/accordion/accordion.component.html
new file mode 100644
index 000000000..43b3f7083
--- /dev/null
+++ b/projects/systelab-components/src/lib/accordion/accordion.component.html
@@ -0,0 +1,10 @@
+
\ No newline at end of file
diff --git a/projects/systelab-components/src/lib/accordion/accordion.component.scss b/projects/systelab-components/src/lib/accordion/accordion.component.scss
new file mode 100644
index 000000000..40eadead0
--- /dev/null
+++ b/projects/systelab-components/src/lib/accordion/accordion.component.scss
@@ -0,0 +1,32 @@
+.accordion-header {
+ background-color: lightgrey;
+ border-radius: 4px 4px 0 0;
+ height: 40px;
+ i {
+ &:after {
+ content: '';
+ background-color: white;
+ width: 15px;
+ height: 15px;
+ z-index: 1;
+ position: absolute;
+ }
+ &:before {
+ z-index: 2;
+ }
+ font-size: 25px;
+ &:hover {
+ cursor: pointer;
+ }
+ }
+}
+
+.collapsible-content {
+ transition: max-height 0.3s ease-out, opacity 0.3s ease-out;
+ opacity: 1;
+ border: 1px solid var(--slab_table_border_color) !important;
+}
+
+.collapsed {
+ opacity: 0;
+}
\ No newline at end of file
diff --git a/projects/systelab-components/src/lib/accordion/accordion.component.spec.ts b/projects/systelab-components/src/lib/accordion/accordion.component.spec.ts
new file mode 100644
index 000000000..b2d3b36da
--- /dev/null
+++ b/projects/systelab-components/src/lib/accordion/accordion.component.spec.ts
@@ -0,0 +1,83 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { BrowserModule, By } from '@angular/platform-browser';
+import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
+import { Accordion } from './accordion.component';
+import { PreferencesService } from 'systelab-preferences';
+
+describe('Systelab Accordion', () => {
+ let fixture: ComponentFixture;
+ let component: Accordion;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [],
+ imports: [BrowserModule],
+ providers: [
+ PreferencesService,
+ provideHttpClient(withInterceptorsFromDi())
+ ]
+ }).compileComponents();
+
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(Accordion);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should be instantiate', () => {
+ expect(fixture.componentInstance).toBeDefined();
+ });
+
+ it('should be height 0 when isCollapse is true', async () => {
+ component.isCollapsed = true;
+ fixture.detectChanges();
+ await fixture.whenStable();
+ const collapsibleContent = fixture.debugElement.query(By.css('.collapsible-content'));
+ const maxHeight = parseInt(getComputedStyle(collapsibleContent.nativeElement).maxHeight, 10);
+ expect(maxHeight).toEqual(0);
+ });
+
+ it('should be height contentMaxHeight when isCollapse is false', async () => {
+ component.isCollapsed = false;
+ fixture.detectChanges();
+ await fixture.whenStable();
+ const collapsibleContent = fixture.debugElement.query(By.css('.collapsible-content'));
+ const maxHeight = parseInt(getComputedStyle(collapsibleContent.nativeElement).maxHeight, 10);
+ expect(maxHeight).toEqual(component.contentMaxHeight);
+ });
+
+ it('should be collapsed if is expanded and clicks icon button', async () => {
+ component.isCollapsed = false;
+ const collapseExpandIcon = fixture.debugElement.query(By.css('.accordion-header'));
+ collapseExpandIcon.triggerEventHandler('click', null);
+ fixture.detectChanges();
+ await fixture.whenStable();
+ expect(component.isCollapsed).toBeTrue();
+ });
+
+ it('should be expanded if is collapsed and clicks icon button', async () => {
+ component.isCollapsed = true;
+ const collapseExpandIcon = fixture.debugElement.query(By.css('.accordion-header'));
+ collapseExpandIcon.triggerEventHandler('click', null);
+ fixture.detectChanges();
+ await fixture.whenStable();
+ expect(component.isCollapsed).toBeFalse();
+ });
+
+ it('should be get isCollapsed value from preferences id flag preferenceName exists', async () => {
+ const valueReturned: boolean = true;
+ const initialPreferenceName = 'testName';
+ component.isCollapsed = false;
+ component.preferenceName = initialPreferenceName;
+ const preferenceServiceGetSpy = spyOn(component['preferenceService'], 'get').and.returnValue(valueReturned);
+ const preferenceServicePutSpy = spyOn(component['preferenceService'], 'put').and.callThrough();
+ component.ngOnInit();
+ await fixture.whenStable();
+ expect(component.preferenceName).toEqual(`${initialPreferenceName}.${component['preferenceSuffix']}`);
+ expect(preferenceServiceGetSpy).toHaveBeenCalledWith(component.preferenceName, false);
+ expect(component.isCollapsed).toEqual(valueReturned);
+ expect(preferenceServicePutSpy).toHaveBeenCalledWith(component.preferenceName, valueReturned);
+ });
+});
\ No newline at end of file
diff --git a/projects/systelab-components/src/lib/accordion/accordion.component.ts b/projects/systelab-components/src/lib/accordion/accordion.component.ts
new file mode 100644
index 000000000..66f99f36e
--- /dev/null
+++ b/projects/systelab-components/src/lib/accordion/accordion.component.ts
@@ -0,0 +1,30 @@
+import { Component, Input, OnInit } from '@angular/core';
+import { PreferencesService } from 'systelab-preferences';
+
+@Component({
+ selector: 'systelab-accordion',
+ templateUrl: './accordion.component.html',
+ styleUrls: ['./accordion.component.scss']
+})
+// eslint-disable-next-line @angular-eslint/component-class-suffix
+export class Accordion implements OnInit {
+ @Input() headerTitle: string = '';
+ @Input() preferenceName: string;
+ @Input() contentMaxHeight: number = 300;
+ @Input() withOverflow: boolean = false;
+ @Input() headerColor: string;
+ @Input() iconColor: string;
+ public isCollapsed: boolean = false;
+ private preferenceSuffix: string = 'accordionStatus';
+
+ constructor(private readonly preferenceService: PreferencesService) {
+ }
+
+ public ngOnInit() {
+ if(this.preferenceName) {
+ this.preferenceName = `${this.preferenceName}.${this.preferenceSuffix}`;
+ this.isCollapsed = this.preferenceService.get(this.preferenceName, false);
+ this.preferenceService.put(this.preferenceName, this.isCollapsed);
+ }
+ }
+}
\ No newline at end of file
diff --git a/projects/systelab-components/src/lib/systelab-components.module.ts b/projects/systelab-components/src/lib/systelab-components.module.ts
index 0fc543cbd..ce6b2f652 100644
--- a/projects/systelab-components/src/lib/systelab-components.module.ts
+++ b/projects/systelab-components/src/lib/systelab-components.module.ts
@@ -104,6 +104,7 @@ import {
PositiveIntegerInputCellEditorComponent
} from './grid/custom-cells/positive-integer/positive-integer-input-cell-editor.component';
import { TestIdDirective } from './directives/test-id.directive';
+import { Accordion } from './accordion/accordion.component';
export const factory = () => {
const systelabComponentsModuleCreated = (factory as any)._systelabComponentsModuleCreated || false;
@@ -223,6 +224,7 @@ const providers = [
PositiveIntegerInputCellEditorComponent,
NumpadDecimalNumericDirective,
TestIdDirective,
+ Accordion,
],
exports: [
SliderComponent,
@@ -310,7 +312,8 @@ const providers = [
NumpadDecimalNumericDirective,
TestIdDirective,
NumpadDecimalNumericDirective,
- PositiveIntegerInputCellEditorComponent
+ PositiveIntegerInputCellEditorComponent,
+ Accordion
],
})
export class SystelabComponentsModule {
diff --git a/projects/systelab-components/src/public-api.ts b/projects/systelab-components/src/public-api.ts
index 41d22a734..97bc1c5ab 100644
--- a/projects/systelab-components/src/public-api.ts
+++ b/projects/systelab-components/src/public-api.ts
@@ -121,6 +121,7 @@ export * from './lib/listbox/abstract-listbox.component';
export * from './lib/listbox/abstract-api-listbox.component';
export * from './lib/listbox/abstract-api-tree-listbox.component';
export * from './lib/sortable-list/abstract-sortable-list.component';
+export * from './lib/accordion/accordion.component';
export * from './lib/add-remove-list/abstract-add-remove-list.component';
export * from './lib/tree/tree-node';
export * from './lib/tree/abstract-tree.component';