From 817d9abdaed4af7d18179cbff9f2ff44f10d3724 Mon Sep 17 00:00:00 2001 From: Pablo Lopez Date: Thu, 29 Aug 2024 16:25:53 -0300 Subject: [PATCH 1/3] expander component --- .../expander-section.component.html | 15 +++++ .../expander-section.component.scss | 31 ++++++++++ .../expander-section.component.spec.ts | 22 ++++++++ .../expander-section.component.ts | 41 ++++++++++++++ .../expander-section.stories.ts | 56 +++++++++++++++++++ src/interface/src/styleguide/index.ts | 1 + 6 files changed, 166 insertions(+) create mode 100644 src/interface/src/styleguide/expander-section/expander-section.component.html create mode 100644 src/interface/src/styleguide/expander-section/expander-section.component.scss create mode 100644 src/interface/src/styleguide/expander-section/expander-section.component.spec.ts create mode 100644 src/interface/src/styleguide/expander-section/expander-section.component.ts create mode 100644 src/interface/src/styleguide/expander-section/expander-section.stories.ts diff --git a/src/interface/src/styleguide/expander-section/expander-section.component.html b/src/interface/src/styleguide/expander-section/expander-section.component.html new file mode 100644 index 000000000..40fd5a7a9 --- /dev/null +++ b/src/interface/src/styleguide/expander-section/expander-section.component.html @@ -0,0 +1,15 @@ +
+ {{ title }} +
+
+ + +
+
+
diff --git a/src/interface/src/styleguide/expander-section/expander-section.component.scss b/src/interface/src/styleguide/expander-section/expander-section.component.scss new file mode 100644 index 000000000..809e3d089 --- /dev/null +++ b/src/interface/src/styleguide/expander-section/expander-section.component.scss @@ -0,0 +1,31 @@ +button { + display: block; +} + +summary { + border-bottom: 1px solid black; + padding: 0 14px; + cursor: pointer; +} + +.base-options { + padding: 16px 14px; +} + +.base-option { + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 8px; + text-transform: capitalize; + + input { + margin: 0; + cursor: pointer; + } + + label { + cursor: pointer; + + } +} diff --git a/src/interface/src/styleguide/expander-section/expander-section.component.spec.ts b/src/interface/src/styleguide/expander-section/expander-section.component.spec.ts new file mode 100644 index 000000000..865bca01d --- /dev/null +++ b/src/interface/src/styleguide/expander-section/expander-section.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ExpanderSectionComponent } from './expander-section.component'; + +describe('ExpanderSectionComponent', () => { + let component: ExpanderSectionComponent; + let fixture: ComponentFixture>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ExpanderSectionComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(ExpanderSectionComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/interface/src/styleguide/expander-section/expander-section.component.ts b/src/interface/src/styleguide/expander-section/expander-section.component.ts new file mode 100644 index 000000000..09a3eaeba --- /dev/null +++ b/src/interface/src/styleguide/expander-section/expander-section.component.ts @@ -0,0 +1,41 @@ +import { + ChangeDetectionStrategy, + Component, + EventEmitter, + Input, + Output, +} from '@angular/core'; +import { NgForOf } from '@angular/common'; + +export type OptionType = { value: string | number; text: string } | string; + +@Component({ + selector: 'sg-expander-section', + standalone: true, + imports: [NgForOf], + templateUrl: './expander-section.component.html', + styleUrl: './expander-section.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ExpanderSectionComponent { + @Input() isOpen?: boolean; + @Input() title: string = ''; + @Input() groupName = ''; + @Input() options: T[] = []; + @Input() defaultOption?: T; + @Output() optionSelected = new EventEmitter(); + + getValue(item: OptionType) { + if (typeof item === 'string') { + return item; + } + return item.value; + } + + getText(item: OptionType) { + if (typeof item === 'string') { + return item; + } + return item.text; + } +} diff --git a/src/interface/src/styleguide/expander-section/expander-section.stories.ts b/src/interface/src/styleguide/expander-section/expander-section.stories.ts new file mode 100644 index 000000000..d61b5ddb7 --- /dev/null +++ b/src/interface/src/styleguide/expander-section/expander-section.stories.ts @@ -0,0 +1,56 @@ +import type { Meta, StoryObj } from '@storybook/angular'; +import { argsToTemplate } from '@storybook/angular'; +import { ExpanderSectionComponent, OptionType } from '@styleguide'; + +const meta: Meta> = { + title: 'Components/Expander Section', + component: ExpanderSectionComponent, + tags: ['autodocs'], + render: (args) => ({ + props: args, + template: ``, + }), +}; + +export default meta; +type Story = StoryObj>; + +type StoryKeyValue = StoryObj< + ExpanderSectionComponent<{ value: number; text: string }> +>; + +export const Default: Story = { + args: { + title: 'Pick your color', + options: ['black', 'red', 'blue', 'green'], + defaultOption: 'red', + groupName: 'options-colors', + }, +}; + +export const Open: Story = { + args: { + isOpen: true, + title: 'Pick your size', + options: ['small', 'medium', 'large', 'x-large'], + defaultOption: 'small', + groupName: 'options-pick-size', + }, +}; + +const options = [ + { value: 1, text: 'One' }, + { value: 2, text: 'Two' }, + { value: 3, text: 'Three' }, + { value: 4, text: 'Four' }, +]; + +export const WithKeyValue: StoryKeyValue = { + args: { + isOpen: true, + title: 'Pick your size', + options: options, + defaultOption: options[1], + groupName: 'options-keyvalue', + }, +}; diff --git a/src/interface/src/styleguide/index.ts b/src/interface/src/styleguide/index.ts index 2aa62d85d..ce65ffd7f 100644 --- a/src/interface/src/styleguide/index.ts +++ b/src/interface/src/styleguide/index.ts @@ -4,3 +4,4 @@ export * from './input/input-field.component'; export * from './status-chip/status-chip.component'; export * from './filter-dropdown/filter-dropdown.component'; export * from './paginator/paginator.component'; +export * from './expander-section/expander-section.component'; From 0a0c8f4e458762c645ee863e8c0033d698a0bff2 Mon Sep 17 00:00:00 2001 From: Pablo Lopez Date: Thu, 29 Aug 2024 17:33:38 -0300 Subject: [PATCH 2/3] using icon --- .../expander-section.component.html | 28 +++++++++--------- .../expander-section.component.scss | 29 +++++++++++++++---- .../expander-section.component.ts | 4 +-- 3 files changed, 40 insertions(+), 21 deletions(-) diff --git a/src/interface/src/styleguide/expander-section/expander-section.component.html b/src/interface/src/styleguide/expander-section/expander-section.component.html index 40fd5a7a9..d5a7cb454 100644 --- a/src/interface/src/styleguide/expander-section/expander-section.component.html +++ b/src/interface/src/styleguide/expander-section/expander-section.component.html @@ -1,15 +1,15 @@ -
- {{ title }} -
-
- - -
+
+ {{ title }} +
+
+
+ +
-
+ diff --git a/src/interface/src/styleguide/expander-section/expander-section.component.scss b/src/interface/src/styleguide/expander-section/expander-section.component.scss index 809e3d089..5526b8b3e 100644 --- a/src/interface/src/styleguide/expander-section/expander-section.component.scss +++ b/src/interface/src/styleguide/expander-section/expander-section.component.scss @@ -1,13 +1,30 @@ -button { - display: block; -} +@import "mixins"; -summary { +.title { + @include xs-label(); border-bottom: 1px solid black; - padding: 0 14px; + padding: 8px 8px 8px 14px; cursor: pointer; + display: flex; + align-items: center; + + &::before { + display: block; + width: 20px; + height: 20px; + content: ''; + background: url('data:image/svg+xml,') no-repeat 50% 50%; + background-size: 6px; + } + + &.open { + &::before { + transform: rotate(90deg); + } + } } + .base-options { padding: 16px 14px; } @@ -21,10 +38,12 @@ summary { input { margin: 0; + width: 20px; cursor: pointer; } label { + @include small-paragraph(); cursor: pointer; } diff --git a/src/interface/src/styleguide/expander-section/expander-section.component.ts b/src/interface/src/styleguide/expander-section/expander-section.component.ts index 09a3eaeba..aa45e3acc 100644 --- a/src/interface/src/styleguide/expander-section/expander-section.component.ts +++ b/src/interface/src/styleguide/expander-section/expander-section.component.ts @@ -5,14 +5,14 @@ import { Input, Output, } from '@angular/core'; -import { NgForOf } from '@angular/common'; +import { NgClass, NgForOf, NgIf } from '@angular/common'; export type OptionType = { value: string | number; text: string } | string; @Component({ selector: 'sg-expander-section', standalone: true, - imports: [NgForOf], + imports: [NgForOf, NgIf, NgClass], templateUrl: './expander-section.component.html', styleUrl: './expander-section.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, From 718898024b05e2cebe2c6c045363719506feb831 Mon Sep 17 00:00:00 2001 From: Pablo Lopez Date: Fri, 30 Aug 2024 14:45:50 -0300 Subject: [PATCH 3/3] refactor expander-section to use expander-item to be more flexible and allow tooltips via content projection --- .../expander-item.component.html | 20 +++++ .../expander-item.component.scss | 35 +++++++++ .../expander-item.component.spec.ts | 22 ++++++ .../expander-item/expander-item.component.ts | 50 ++++++++++++ .../expander-section.component.html | 11 +-- .../expander-section.component.scss | 20 ----- .../expander-section.component.spec.ts | 6 +- .../expander-section.component.ts | 37 +++------ .../expander-section.stories.ts | 77 ++++++++++++------- 9 files changed, 190 insertions(+), 88 deletions(-) create mode 100644 src/interface/src/styleguide/expander-item/expander-item.component.html create mode 100644 src/interface/src/styleguide/expander-item/expander-item.component.scss create mode 100644 src/interface/src/styleguide/expander-item/expander-item.component.spec.ts create mode 100644 src/interface/src/styleguide/expander-item/expander-item.component.ts diff --git a/src/interface/src/styleguide/expander-item/expander-item.component.html b/src/interface/src/styleguide/expander-item/expander-item.component.html new file mode 100644 index 000000000..a11b2b120 --- /dev/null +++ b/src/interface/src/styleguide/expander-item/expander-item.component.html @@ -0,0 +1,20 @@ + + + + + +
+ +
+
+
diff --git a/src/interface/src/styleguide/expander-item/expander-item.component.scss b/src/interface/src/styleguide/expander-item/expander-item.component.scss new file mode 100644 index 000000000..7b3285159 --- /dev/null +++ b/src/interface/src/styleguide/expander-item/expander-item.component.scss @@ -0,0 +1,35 @@ +@import "mixins"; + +:host { + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 8px; + text-transform: capitalize; +} + +input { + margin: 0; + width: 20px; + cursor: pointer; +} + +label { + @include small-paragraph(); + cursor: pointer; +} + +.info { + width: 22px; + height: 22px; + font-size: 22px; + line-height: 0; +} + +button.info { + margin-left: auto; +} + +.tooltip { + padding: 20px; +} diff --git a/src/interface/src/styleguide/expander-item/expander-item.component.spec.ts b/src/interface/src/styleguide/expander-item/expander-item.component.spec.ts new file mode 100644 index 000000000..66d976cbf --- /dev/null +++ b/src/interface/src/styleguide/expander-item/expander-item.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ExpanderItemComponent } from './expander-item.component'; + +describe('ExpanderItemComponent', () => { + let component: ExpanderItemComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ExpanderItemComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(ExpanderItemComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/interface/src/styleguide/expander-item/expander-item.component.ts b/src/interface/src/styleguide/expander-item/expander-item.component.ts new file mode 100644 index 000000000..d831ef634 --- /dev/null +++ b/src/interface/src/styleguide/expander-item/expander-item.component.ts @@ -0,0 +1,50 @@ +import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { MatIconModule } from '@angular/material/icon'; +import { MatLegacyButtonModule } from '@angular/material/legacy-button'; +import { MatLegacyMenuModule } from '@angular/material/legacy-menu'; +import { NgIf, NgSwitchCase } from '@angular/common'; + +/** + * This component is used in combination with ``. + * It shows a radio button with its label, and additionally a tooltip. + * The markup provided in content projection will be used for the tooltip. + */ +@Component({ + selector: 'sg-expander-item', + standalone: true, + imports: [ + MatIconModule, + MatLegacyButtonModule, + MatLegacyMenuModule, + NgSwitchCase, + NgIf, + ], + templateUrl: './expander-item.component.html', + styleUrl: './expander-item.component.scss', +}) +export class ExpanderItemComponent { + /** + * The label for the radio button + */ + @Input() label: string = ''; + /** + * The value for the radio button + */ + @Input() value: string | number = ''; + /** + * The name of the group (the input name) for the radio button + */ + @Input() groupName: string = ''; + /** + * Whether the item is checked (selected) + */ + @Input() checked = false; + /** + * Whether it shows the tooltip icon + */ + @Input() showTooltip = false; + /** + * Emitted when the user changes the selected option + */ + @Output() optionSelected = new EventEmitter(); +} diff --git a/src/interface/src/styleguide/expander-section/expander-section.component.html b/src/interface/src/styleguide/expander-section/expander-section.component.html index d5a7cb454..9d94dc5cc 100644 --- a/src/interface/src/styleguide/expander-section/expander-section.component.html +++ b/src/interface/src/styleguide/expander-section/expander-section.component.html @@ -2,14 +2,5 @@ {{ title }}
-
- - -
+
diff --git a/src/interface/src/styleguide/expander-section/expander-section.component.scss b/src/interface/src/styleguide/expander-section/expander-section.component.scss index 5526b8b3e..d1df9bbb1 100644 --- a/src/interface/src/styleguide/expander-section/expander-section.component.scss +++ b/src/interface/src/styleguide/expander-section/expander-section.component.scss @@ -28,23 +28,3 @@ .base-options { padding: 16px 14px; } - -.base-option { - display: flex; - align-items: center; - gap: 8px; - margin-bottom: 8px; - text-transform: capitalize; - - input { - margin: 0; - width: 20px; - cursor: pointer; - } - - label { - @include small-paragraph(); - cursor: pointer; - - } -} diff --git a/src/interface/src/styleguide/expander-section/expander-section.component.spec.ts b/src/interface/src/styleguide/expander-section/expander-section.component.spec.ts index 865bca01d..bae2a985e 100644 --- a/src/interface/src/styleguide/expander-section/expander-section.component.spec.ts +++ b/src/interface/src/styleguide/expander-section/expander-section.component.spec.ts @@ -3,15 +3,15 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ExpanderSectionComponent } from './expander-section.component'; describe('ExpanderSectionComponent', () => { - let component: ExpanderSectionComponent; - let fixture: ComponentFixture>; + let component: ExpanderSectionComponent; + let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ imports: [ExpanderSectionComponent], }).compileComponents(); - fixture = TestBed.createComponent(ExpanderSectionComponent); + fixture = TestBed.createComponent(ExpanderSectionComponent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/src/interface/src/styleguide/expander-section/expander-section.component.ts b/src/interface/src/styleguide/expander-section/expander-section.component.ts index aa45e3acc..a665f4913 100644 --- a/src/interface/src/styleguide/expander-section/expander-section.component.ts +++ b/src/interface/src/styleguide/expander-section/expander-section.component.ts @@ -1,14 +1,13 @@ -import { - ChangeDetectionStrategy, - Component, - EventEmitter, - Input, - Output, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; import { NgClass, NgForOf, NgIf } from '@angular/common'; -export type OptionType = { value: string | number; text: string } | string; - +/** + * The expander section component shows a title with a control to expand and collapse + * inner content. + * This component accepts html via content projection. + * Its content can be any arbitrary html, or you can provide `` to display + * a list of radio buttons with optional tooltips. + */ @Component({ selector: 'sg-expander-section', standalone: true, @@ -17,25 +16,7 @@ export type OptionType = { value: string | number; text: string } | string; styleUrl: './expander-section.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, }) -export class ExpanderSectionComponent { +export class ExpanderSectionComponent { @Input() isOpen?: boolean; @Input() title: string = ''; - @Input() groupName = ''; - @Input() options: T[] = []; - @Input() defaultOption?: T; - @Output() optionSelected = new EventEmitter(); - - getValue(item: OptionType) { - if (typeof item === 'string') { - return item; - } - return item.value; - } - - getText(item: OptionType) { - if (typeof item === 'string') { - return item; - } - return item.text; - } } diff --git a/src/interface/src/styleguide/expander-section/expander-section.stories.ts b/src/interface/src/styleguide/expander-section/expander-section.stories.ts index d61b5ddb7..cf6e4a7e8 100644 --- a/src/interface/src/styleguide/expander-section/expander-section.stories.ts +++ b/src/interface/src/styleguide/expander-section/expander-section.stories.ts @@ -1,56 +1,79 @@ import type { Meta, StoryObj } from '@storybook/angular'; -import { argsToTemplate } from '@storybook/angular'; -import { ExpanderSectionComponent, OptionType } from '@styleguide'; +import { + applicationConfig, + argsToTemplate, + moduleMetadata, +} from '@storybook/angular'; +import { ExpanderSectionComponent } from '@styleguide'; +import { provideAnimations } from '@angular/platform-browser/animations'; +import { ExpanderItemComponent } from '../expander-item/expander-item.component'; -const meta: Meta> = { +const meta: Meta = { title: 'Components/Expander Section', component: ExpanderSectionComponent, + decorators: [ + applicationConfig({ + providers: [provideAnimations()], + }), + moduleMetadata({ imports: [ExpanderItemComponent] }), + ], tags: ['autodocs'], render: (args) => ({ props: args, - template: ``, + template: ` + + + +`, }), }; export default meta; -type Story = StoryObj>; - -type StoryKeyValue = StoryObj< - ExpanderSectionComponent<{ value: number; text: string }> ->; +type Story = StoryObj; export const Default: Story = { args: { title: 'Pick your color', - options: ['black', 'red', 'blue', 'green'], - defaultOption: 'red', - groupName: 'options-colors', }, }; export const Open: Story = { args: { + title: 'Pick your color', isOpen: true, - title: 'Pick your size', - options: ['small', 'medium', 'large', 'x-large'], - defaultOption: 'small', - groupName: 'options-pick-size', }, }; -const options = [ - { value: 1, text: 'One' }, - { value: 2, text: 'Two' }, - { value: 3, text: 'Three' }, - { value: 4, text: 'Four' }, -]; - -export const WithKeyValue: StoryKeyValue = { +export const WithTooltips: Story = { args: { + title: 'Pick your size', isOpen: true, + }, + render: (args) => ({ + props: args, + template: ` + + Some tooltip content + + + Another tooltip content + + + This can be html + +`, + }), +}; + +export const AnyContent: Story = { + args: { title: 'Pick your size', - options: options, - defaultOption: options[1], - groupName: 'options-keyvalue', + isOpen: true, }, + render: (args) => ({ + props: args, + template: ` + This can actually be whatever content we want. +`, + }), };