-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #783 from hubmapconsortium/create-expansion-panel
Create expansion panel
- Loading branch information
Showing
15 changed files
with
323 additions
and
4 deletions.
There are no files selected for viewing
23 changes: 23 additions & 0 deletions
23
...sign-system/button-toggle/src/lib/button-toggle-size/button-toggle-size.directive.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { Component } from '@angular/core'; | ||
import { render, screen } from '@testing-library/angular'; | ||
import { ToggleButtonSizeDirective } from './button-toggle-size.directive'; | ||
|
||
@Component({ | ||
template: `<div hraButtonToggleSize="large" data-testid="dir"></div>`, | ||
imports: [ToggleButtonSizeDirective], | ||
standalone: true, | ||
}) | ||
class ButtonToggleComponent {} | ||
|
||
describe('ButtonToggleSizeDirective', () => { | ||
it('should apply the styles based on the directive', async () => { | ||
await render(ButtonToggleComponent); | ||
|
||
const directive = screen.getByTestId('dir'); | ||
const styles = window.getComputedStyle(directive); | ||
const lineHeight = styles.getPropertyValue('--mat-standard-button-toggle-height'); | ||
const font = styles.getPropertyValue('font'); | ||
expect(font).toBe('var(--sys-label-large)'); | ||
expect(lineHeight).toBe('24px'); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# @hra-ui/design-system/expansion-panel | ||
|
||
Secondary entry point of `@hra-ui/design-system`. It can be used by importing from `@hra-ui/design-system/expansion-panel`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"lib": { | ||
"entryFile": "src/index.ts" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './lib/expansion-panel.component'; |
11 changes: 11 additions & 0 deletions
11
libs/design-system/expansion-panel/src/lib/expansion-panel-animations.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { animate, state, style, transition, trigger } from '@angular/animations'; | ||
|
||
/** Animation for the expansion panel */ | ||
export const EXPANSION_PANEL_ANIMATION_TIMING = '225ms cubic-bezier(0.4,0.0,0.2,1)'; | ||
|
||
/** Animation for Body Expansion */ | ||
export const BODY_EXPANSION = trigger('bodyExpansion', [ | ||
state('collapsed, void', style({ height: '0px', visibility: 'hidden' })), | ||
state('expanded', style({ height: '*', visibility: '' })), | ||
transition('expanded <=> collapsed, void => collapsed', animate(EXPANSION_PANEL_ANIMATION_TIMING)), | ||
]); |
46 changes: 46 additions & 0 deletions
46
libs/design-system/expansion-panel/src/lib/expansion-panel.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
<cdk-accordion> | ||
<cdk-accordion-item | ||
#accordionItem="cdkAccordionItem" | ||
tabindex="0" | ||
[attr.aria-expanded]="accordionItem.expanded" | ||
[attr.aria-controls]="bodyId" | ||
[expanded]="expanded" | ||
> | ||
<div class="header"> | ||
@if (!disabled()) { | ||
<button mat-icon-button data-testid="toggle" (click)="accordionItem.toggle()"> | ||
<mat-icon> | ||
{{ accordionItem.expanded ? 'remove' : 'add' }} | ||
</mat-icon> | ||
</button> | ||
} | ||
|
||
<span class="title" [attr.id]="titleId"> | ||
{{ title() }} | ||
</span> | ||
|
||
<span> | ||
<ng-content select="hra-expansion-panel-actions"> </ng-content> | ||
</span> | ||
<div class="filler"></div> | ||
<span> | ||
<ng-content select="hra-expansion-panel-header-content"></ng-content> | ||
</span> | ||
</div> | ||
<div | ||
role="region" | ||
class="content" | ||
[attr.id]="bodyId" | ||
[attr.aria-labelledby]="titleId" | ||
#body | ||
[@bodyExpansion]="accordionItem.expanded ? 'expanded' : 'collapsed'" | ||
(@bodyExpansion.start)="animationStart($event)" | ||
(@bodyExpansion.done)="animationDone($event)" | ||
data-testid="body" | ||
> | ||
<div class="expansion-body"> | ||
<ng-content></ng-content> | ||
</div> | ||
</div> | ||
</cdk-accordion-item> | ||
</cdk-accordion> |
21 changes: 21 additions & 0 deletions
21
libs/design-system/expansion-panel/src/lib/expansion-panel.component.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
:host { | ||
display: block; | ||
|
||
.header { | ||
display: flex; | ||
align-items: center; | ||
font: var(--sys-label-large); | ||
color: var(--sys-secondary); | ||
} | ||
|
||
.filler { | ||
flex-grow: 1; | ||
} | ||
|
||
.content { | ||
font: var(--sys-label-large); | ||
&[style*='visibility: hidden'] * { | ||
visibility: hidden !important; | ||
} | ||
} | ||
} |
47 changes: 47 additions & 0 deletions
47
libs/design-system/expansion-panel/src/lib/expansion-panel.component.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import { AnimationDriver } from '@angular/animations/browser'; | ||
import { MockAnimationDriver, MockAnimationPlayer } from '@angular/animations/browser/testing'; | ||
import { provideDesignSystem } from '@hra-ui/design-system'; | ||
import { render, RenderComponentOptions, screen } from '@testing-library/angular'; | ||
import { userEvent } from '@testing-library/user-event'; | ||
import { ExpansionPanelComponent } from './expansion-panel.component'; | ||
|
||
describe('ExpansionPanelComponent', () => { | ||
async function setup(options?: RenderComponentOptions<ExpansionPanelComponent>) { | ||
return render(ExpansionPanelComponent, { | ||
...options, | ||
inputs: { | ||
title: 'Test Title', | ||
...options?.inputs, | ||
}, | ||
providers: [ | ||
provideDesignSystem(), | ||
{ | ||
provide: AnimationDriver, | ||
useClass: MockAnimationDriver, | ||
}, | ||
], | ||
}); | ||
} | ||
|
||
it('should render the expansion panel', async () => { | ||
await setup(); | ||
const title = screen.getByText('Test Title'); | ||
expect(title).toBeInTheDocument(); | ||
}); | ||
|
||
it('should set inert attribute on body element when animation starts', async () => { | ||
await setup(); | ||
|
||
const button = screen.getByTestId('toggle'); | ||
await userEvent.click(button); | ||
|
||
const player = MockAnimationDriver.log.pop() as MockAnimationPlayer; | ||
const element = player.element as HTMLElement; | ||
|
||
expect(element).toHaveAttribute('inert'); | ||
player.finish(); | ||
expect(element).not.toHaveAttribute('inert'); | ||
expect(element.style.height).toBe('0px'); | ||
expect(element.style.visibility).toBe('hidden'); | ||
}); | ||
}); |
49 changes: 49 additions & 0 deletions
49
libs/design-system/expansion-panel/src/lib/expansion-panel.component.stories.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import { MatIconModule } from '@angular/material/icon'; | ||
import { provideDesignSystem } from '@hra-ui/design-system'; | ||
import { ButtonModule } from '@hra-ui/design-system/button'; | ||
import { applicationConfig, moduleMetadata, type Meta, type StoryObj } from '@storybook/angular'; | ||
import { | ||
ExpansionPanelActionsComponent, | ||
ExpansionPanelComponent, | ||
ExpansionPanelHeaderContentComponent, | ||
} from './expansion-panel.component'; | ||
|
||
const meta: Meta = { | ||
title: 'ExpansionPanel', | ||
decorators: [ | ||
moduleMetadata({ | ||
imports: [ | ||
MatIconModule, | ||
ButtonModule, | ||
ExpansionPanelActionsComponent, | ||
ExpansionPanelComponent, | ||
ExpansionPanelHeaderContentComponent, | ||
], | ||
}), | ||
applicationConfig({ | ||
providers: [provideDesignSystem()], | ||
}), | ||
], | ||
render: (args) => ({ | ||
props: args, | ||
template: ` | ||
<hra-expansion-panel [title]="'Title'"> | ||
<hra-expansion-panel-actions> | ||
<button mat-icon-button> | ||
<mat-icon class="material-symbols-rounded"> | ||
more_vert | ||
</mat-icon> | ||
</button> | ||
</hra-expansion-panel-actions> | ||
<hra-expansion-panel-header-content> | ||
Additional Actions | ||
</hra-expansion-panel-header-content> | ||
Actual Content | ||
</hra-expansion-panel> | ||
`, | ||
}), | ||
}; | ||
export default meta; | ||
type Story = StoryObj; | ||
|
||
export const Default: Story = {}; |
96 changes: 96 additions & 0 deletions
96
libs/design-system/expansion-panel/src/lib/expansion-panel.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
import { AnimationEvent } from '@angular/animations'; | ||
import { CdkAccordionModule } from '@angular/cdk/accordion'; | ||
import { CommonModule } from '@angular/common'; | ||
import { | ||
ANIMATION_MODULE_TYPE, | ||
booleanAttribute, | ||
ChangeDetectionStrategy, | ||
Component, | ||
computed, | ||
ElementRef, | ||
inject, | ||
input, | ||
Renderer2, | ||
viewChild, | ||
} from '@angular/core'; | ||
import { MatIconButton } from '@angular/material/button'; | ||
import { MatIconModule } from '@angular/material/icon'; | ||
import { IconButtonSizeDirective } from '@hra-ui/design-system/icon-button'; | ||
import { BODY_EXPANSION } from './expansion-panel-animations'; | ||
|
||
/** Counter to keep track of distinct panels */ | ||
let idCounter = 0; | ||
|
||
/** Expansion panel actions component */ | ||
@Component({ | ||
selector: 'hra-expansion-panel-actions', | ||
standalone: true, | ||
template: '<ng-content></ng-content>', | ||
changeDetection: ChangeDetectionStrategy.OnPush, | ||
}) | ||
export class ExpansionPanelActionsComponent {} | ||
|
||
/** Expansion panel header content component */ | ||
@Component({ | ||
selector: 'hra-expansion-panel-header-content', | ||
standalone: true, | ||
template: '<ng-content></ng-content>', | ||
changeDetection: ChangeDetectionStrategy.OnPush, | ||
}) | ||
export class ExpansionPanelHeaderContentComponent {} | ||
|
||
/** Expansion panel component */ | ||
@Component({ | ||
selector: 'hra-expansion-panel', | ||
standalone: true, | ||
imports: [CommonModule, CdkAccordionModule, IconButtonSizeDirective, MatIconButton, MatIconModule], | ||
animations: [BODY_EXPANSION], | ||
templateUrl: './expansion-panel.component.html', | ||
styleUrl: './expansion-panel.component.scss', | ||
changeDetection: ChangeDetectionStrategy.OnPush, | ||
}) | ||
export class ExpansionPanelComponent { | ||
/** Title of the expansion panel */ | ||
readonly title = input.required<string>(); | ||
|
||
/** Flag to check if the body is expanded */ | ||
readonly expanded = input(true, { transform: booleanAttribute }); | ||
|
||
/** Flag to denote panel as disabled */ | ||
readonly disabled = input(false, { transform: booleanAttribute }); | ||
|
||
/** Increments the counter on every declaration */ | ||
protected readonly id = idCounter++; | ||
|
||
/** Id attribute for title based on current id counter */ | ||
protected readonly titleId = `expansion-panel-title-${this.id}`; | ||
|
||
/** Id attribute for body based on current id counter */ | ||
protected readonly bodyId = `expansion-panel-body-${this.id}`; | ||
|
||
/** Instance of renderer */ | ||
private readonly renderer = inject(Renderer2); | ||
|
||
/** Instance of body element */ | ||
private readonly bodyElementRef = viewChild.required<ElementRef<HTMLElement>>('body'); | ||
|
||
/** Actual body element */ | ||
private readonly body = computed(() => this.bodyElementRef().nativeElement); | ||
|
||
/** Disable animations based on module type */ | ||
private readonly animationsDisabled = inject(ANIMATION_MODULE_TYPE) === 'NoopAnimations'; | ||
|
||
/** Sets attribute based on event state */ | ||
protected animationStart(event: AnimationEvent): void { | ||
if (event.fromState !== 'void' && !this.animationsDisabled) { | ||
this.renderer.setAttribute(this.body(), 'inert', ''); | ||
} | ||
} | ||
|
||
/** Removes attribute based on event state */ | ||
protected animationDone(event: AnimationEvent): void { | ||
if (event.fromState !== 'void' && !this.animationsDisabled) { | ||
this.renderer.removeAttribute(this.body(), 'inert'); | ||
} | ||
} | ||
} |
13 changes: 13 additions & 0 deletions
13
libs/design-system/expansion-panel/src/lib/expansion-panel.module.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { NgModule } from '@angular/core'; | ||
import { | ||
ExpansionPanelActionsComponent, | ||
ExpansionPanelComponent, | ||
ExpansionPanelHeaderContentComponent, | ||
} from './expansion-panel.component'; | ||
|
||
/** Expansion panel module */ | ||
@NgModule({ | ||
imports: [ExpansionPanelActionsComponent, ExpansionPanelHeaderContentComponent, ExpansionPanelComponent], | ||
exports: [ExpansionPanelActionsComponent, ExpansionPanelHeaderContentComponent, ExpansionPanelComponent], | ||
}) | ||
export class ExpansionPanelModule {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters