-
Notifications
You must be signed in to change notification settings - Fork 467
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(ui): create a collapse breadcrumb (#29379)
### Parent Issue #29358 ### Proposed Changes * Create new component named DotCollapseBreadcrumbComponent * Create DotCollapseBreadcrumbComponent Storybook * Create basic functionality to collapse items based on maxItems input * Fix bug with dot-crumbtrail scope styles ### Checklist - [x] Tests - [x] Translations - [x] Security Implications Contemplated (add notes if applicable) ### Goal Create a reusable breadcrumb component in the @dotcms/ui package that extends the official PrimeNG [breadcrumb](https://www.primefaces.org/primeng-v15-lts/breadcrumb) by adding collapsible items. ### References <img width="675" alt="Screenshot 2024-07-10 at 12 16 04 PM" src="https://github.com/dotCMS/core/assets/1909643/4e7c60f6-91ef-47ec-b8c4-68c96b8e3d01"> <img width="1022" alt="Screenshot 2024-07-10 at 12 16 12 PM" src="https://github.com/dotCMS/core/assets/1909643/dfbd7d37-d2f2-4b26-9ead-27228d6e0795"> <img width="982" alt="Screenshot 2024-07-10 at 12 16 19 PM" src="https://github.com/dotCMS/core/assets/1909643/838c292d-b993-41c1-a7bf-7434abe2707d"> ### Result ![Screenshot 2024-07-29 at 3 29 45 PM](https://github.com/user-attachments/assets/f8d526c9-813f-4cbc-8f8e-02b4391f165b) ![Screenshot 2024-07-29 at 3 29 56 PM](https://github.com/user-attachments/assets/a3032765-da68-44da-b65e-b5c6acaa6c54) ![Screenshot 2024-07-29 at 3 30 08 PM](https://github.com/user-attachments/assets/169bb329-e951-4f25-9429-845de7e17d92)
- Loading branch information
Showing
10 changed files
with
359 additions
and
47 deletions.
There are no files selected for viewing
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 |
---|---|---|
|
@@ -5,6 +5,7 @@ | |
/target | ||
/tmp | ||
/tools | ||
/.yarn | ||
|
||
package.json | ||
package-lock.json | ||
|
2 changes: 1 addition & 1 deletion
2
core-web/apps/dotcms-ui/src/app/view/components/dot-crumbtrail/dot-crumbtrail.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
81 changes: 81 additions & 0 deletions
81
core-web/apps/dotcms-ui/src/stories/dotcms/menu/DotCollapseBreadcrumb.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,81 @@ | ||
/* eslint-disable no-console */ | ||
import { | ||
Meta, | ||
StoryObj, | ||
moduleMetadata, | ||
componentWrapperDecorator, | ||
argsToTemplate, | ||
applicationConfig | ||
} from '@storybook/angular'; | ||
|
||
import { importProvidersFrom } from '@angular/core'; | ||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; | ||
|
||
import { MenuItem } from 'primeng/api'; | ||
import { ToastModule } from 'primeng/toast'; | ||
|
||
import { DotCollapseBreadcrumbComponent } from '@dotcms/ui'; | ||
|
||
type Args = DotCollapseBreadcrumbComponent & { | ||
model: MenuItem[]; | ||
maxItems: number; | ||
}; | ||
|
||
const meta: Meta<Args> = { | ||
title: 'DotCMS/Menu/DotCollapseBreadcrumb', | ||
component: DotCollapseBreadcrumbComponent, | ||
decorators: [ | ||
applicationConfig({ | ||
providers: [importProvidersFrom(BrowserAnimationsModule)] | ||
}), | ||
moduleMetadata({ | ||
imports: [BrowserAnimationsModule, ToastModule] | ||
}), | ||
componentWrapperDecorator( | ||
(story) => | ||
`<div class="card flex justify-content-center w-50rem h-10rem relative">${story}</div>` | ||
) | ||
], | ||
parameters: { | ||
layout: 'centered', | ||
docs: { | ||
description: { | ||
component: | ||
'Breadcrumb provides contextual information about page hierarchy.: https://primefaces.org/primeng/showcase/#/breadcrumb' | ||
} | ||
} | ||
}, | ||
args: { | ||
maxItems: 4, | ||
model: [ | ||
{ label: 'Electronics', command: console.log }, | ||
{ label: 'Computer', command: console.log }, | ||
{ label: 'Accessories', command: console.log }, | ||
{ label: 'Keyboard', command: console.log }, | ||
{ label: 'Wireless', command: console.log } | ||
] | ||
}, | ||
argTypes: { | ||
model: { | ||
description: 'Menu items to display' | ||
}, | ||
maxItems: { | ||
description: 'Max items to display', | ||
control: { type: 'number' } | ||
} | ||
}, | ||
render: (args: Args) => { | ||
return { | ||
props: { | ||
...args | ||
}, | ||
template: `<dot-collapse-breadcrumb ${argsToTemplate(args)} />` | ||
}; | ||
} | ||
}; | ||
|
||
export default meta; | ||
|
||
type Story = StoryObj<Args>; | ||
|
||
export const Default: Story = {}; |
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
19 changes: 19 additions & 0 deletions
19
...libs/ui/src/lib/components/dot-collapse-breadcrumb/dot-collapse-breadcrumb.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,19 @@ | ||
<div class="flex align-items-center"> | ||
@if ($isCollapsed()) { | ||
<div class="flex align-items-center"> | ||
<div> | ||
<p-menu #menu [model]="$itemsToHide()" [popup]="true" appendTo="body" /> | ||
<p-button | ||
(click)="menu.toggle($event)" | ||
icon="pi pi-ellipsis-h" | ||
type="button" | ||
data-testid="btn-collapse" | ||
styleClass="p-button-rounded p-button-text p-button-sm outline-none" /> | ||
</div> | ||
<div class="mx-2"> | ||
<ChevronRightIcon /> | ||
</div> | ||
</div> | ||
} | ||
<p-breadcrumb [model]="$itemsToShow()" (onItemClick)="onItemClick.emit($event)" /> | ||
</div> |
101 changes: 101 additions & 0 deletions
101
...s/ui/src/lib/components/dot-collapse-breadcrumb/dot-collapse-breadcrumb.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,101 @@ | ||
import { byTestId, createRoutingFactory, Spectator } from '@ngneat/spectator'; | ||
|
||
import { ActivatedRoute } from '@angular/router'; | ||
|
||
import { ButtonModule } from 'primeng/button'; | ||
import { MenuModule } from 'primeng/menu'; | ||
|
||
import { DotCollapseBreadcrumbComponent } from './dot-collapse-breadcrumb.component'; | ||
import { MAX_ITEMS } from './dot-collapse-breadcrumb.costants'; | ||
|
||
describe('DotCollapseBreadcrumbComponent', () => { | ||
let spectator: Spectator<DotCollapseBreadcrumbComponent>; | ||
|
||
const createComponent = createRoutingFactory({ | ||
component: DotCollapseBreadcrumbComponent, | ||
providers: [ActivatedRoute], | ||
imports: [MenuModule, ButtonModule] | ||
}); | ||
|
||
beforeEach(() => { | ||
spectator = createComponent(); | ||
}); | ||
|
||
it('should be created', () => { | ||
spectator.detectChanges(); | ||
expect(spectator.component).toBeTruthy(); | ||
}); | ||
|
||
describe('MaxItems', () => { | ||
it('should have the default', () => { | ||
spectator.detectChanges(); | ||
expect(spectator.component.$maxItems()).toBe(MAX_ITEMS); | ||
}); | ||
|
||
it('should show the options without btn collapse', () => { | ||
spectator.setInput('maxItems', 5); | ||
spectator.setInput('model', [ | ||
{ label: 'Electronics', url: '' }, | ||
{ label: 'Computer', url: '' }, | ||
{ label: 'Accessories', url: '' }, | ||
{ label: 'Keyboard', url: '' }, | ||
{ label: 'Wireless', url: '' } | ||
]); | ||
spectator.detectChanges(); | ||
expect(spectator.query(byTestId('btn-collapse'))).toBeNull(); | ||
expect(spectator.queryAll('.p-menuitem-link').length).toBe(5); | ||
}); | ||
|
||
it('should show maxItems options with btn collapse', () => { | ||
spectator.setInput('maxItems', 4); | ||
spectator.setInput('model', [ | ||
{ label: 'Electronics', url: '' }, | ||
{ label: 'Computer', url: '' }, | ||
{ label: 'Accessories', url: '' }, | ||
{ label: 'Keyboard', url: '' }, | ||
{ label: 'Wireless', url: '' } | ||
]); | ||
spectator.detectChanges(); | ||
expect(spectator.query(byTestId('btn-collapse'))).toBeTruthy(); | ||
expect(spectator.queryAll('.p-menuitem-link').length).toBe(4); | ||
}); | ||
}); | ||
|
||
it('should call itemClick when click home ', () => { | ||
spectator.setInput('maxItems', 5); | ||
spectator.setInput('model', [ | ||
{ label: 'Electronics', url: '' }, | ||
{ label: 'Computer', url: '' }, | ||
{ label: 'Accessories', url: '' }, | ||
{ label: 'Keyboard', url: '' }, | ||
{ label: 'Wireless', url: '' } | ||
]); | ||
spectator.detectChanges(); | ||
|
||
const itemClickSpy = spyOn(spectator.component.onItemClick, 'emit'); | ||
const firstEl = spectator.query('.p-menuitem-link'); | ||
spectator.click(firstEl); | ||
spectator.detectChanges(); | ||
|
||
expect(itemClickSpy).toHaveBeenCalled(); | ||
}); | ||
|
||
it('should call itemClick when click home with routerLink', () => { | ||
spectator.setInput('maxItems', 5); | ||
spectator.setInput('model', [ | ||
{ label: 'Electronics', routerLink: '/' }, | ||
{ label: 'Computer', routerLink: '/' }, | ||
{ label: 'Accessories', routerLink: '/' }, | ||
{ label: 'Keyboard', routerLink: '/' }, | ||
{ label: 'Wireless', routerLink: '/' } | ||
]); | ||
spectator.detectChanges(); | ||
|
||
const itemClickSpy = spyOn(spectator.component.onItemClick, 'emit'); | ||
const firstEl = spectator.query('.p-menuitem-link'); | ||
spectator.click(firstEl); | ||
spectator.detectChanges(); | ||
|
||
expect(itemClickSpy).toHaveBeenCalled(); | ||
}); | ||
}); |
73 changes: 73 additions & 0 deletions
73
...b/libs/ui/src/lib/components/dot-collapse-breadcrumb/dot-collapse-breadcrumb.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,73 @@ | ||
import { ChangeDetectionStrategy, Component, computed, input, output } from '@angular/core'; | ||
import { RouterModule } from '@angular/router'; | ||
|
||
import { MenuItem } from 'primeng/api'; | ||
import { BreadcrumbModule } from 'primeng/breadcrumb'; | ||
import { ButtonModule } from 'primeng/button'; | ||
import { ChevronRightIcon } from 'primeng/icons/chevronright'; | ||
import { MenuModule } from 'primeng/menu'; | ||
|
||
import { MAX_ITEMS } from './dot-collapse-breadcrumb.costants'; | ||
/** | ||
* Component to display a breadcrumb with a collapse button | ||
* | ||
* @export | ||
* @class DotCollapseBreadcrumbComponent | ||
*/ | ||
@Component({ | ||
imports: [ChevronRightIcon, ButtonModule, MenuModule, RouterModule, BreadcrumbModule], | ||
standalone: true, | ||
selector: 'dot-collapse-breadcrumb', | ||
templateUrl: './dot-collapse-breadcrumb.component.html', | ||
changeDetection: ChangeDetectionStrategy.OnPush | ||
}) | ||
export class DotCollapseBreadcrumbComponent { | ||
/** | ||
* Menu items to display | ||
* | ||
* @memberof DotCollapseBreadcrumbComponent | ||
*/ | ||
$model = input<MenuItem[]>([], { alias: 'model' }); | ||
/** | ||
* Max items to display | ||
* | ||
* @memberof DotCollapseBreadcrumbComponent | ||
*/ | ||
$maxItems = input<number>(MAX_ITEMS, { alias: 'maxItems' }); | ||
/** | ||
* Items to show | ||
* | ||
* @memberof DotCollapseBreadcrumbComponent | ||
*/ | ||
$itemsToShow = computed(() => { | ||
const items = this.$model(); | ||
const size = items.length; | ||
const maxItems = this.$maxItems(); | ||
|
||
return size > maxItems ? items.slice(size - maxItems) : items; | ||
}); | ||
/** | ||
* Items to hide | ||
* | ||
* @memberof DotCollapseBreadcrumbComponent | ||
*/ | ||
$itemsToHide = computed(() => { | ||
const items = this.$model(); | ||
const size = items.length; | ||
const maxItems = this.$maxItems(); | ||
|
||
return size > maxItems ? items.slice(0, size - maxItems) : []; | ||
}); | ||
/** | ||
* Indicates if the menu is collapsed | ||
* | ||
* @memberof DotCollapseBreadcrumbComponent | ||
*/ | ||
$isCollapsed = computed(() => this.$itemsToHide().length > 0); | ||
/** | ||
* Event emitted when a menu item is clicked | ||
* | ||
* @memberof DotCollapseBreadcrumbComponent | ||
*/ | ||
onItemClick = output<{ originalEvent: Event; item: MenuItem }>(); | ||
} |
1 change: 1 addition & 0 deletions
1
...eb/libs/ui/src/lib/components/dot-collapse-breadcrumb/dot-collapse-breadcrumb.costants.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 const MAX_ITEMS = 4; |
Oops, something went wrong.