AAE-40269 Add storybook v10 (#11401)

This commit is contained in:
Wojciech Duda
2026-01-09 18:17:43 +00:00
committed by GitHub
parent f0c6cc0ff7
commit 5f9de1fde8
119 changed files with 11010 additions and 753 deletions
@@ -0,0 +1,114 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Meta, StoryObj, moduleMetadata } from '@storybook/angular';
import { AboutComponent } from './about.component';
import { AuthenticationService } from '../auth/services/authentication.service';
import { AuthenticationMock } from '../auth/mock/authentication.service.mock';
import { AppExtensionService, ExtensionRef, ViewerExtensionRef } from '@alfresco/adf-extensions';
import { AppConfigService } from '../app-config/app-config.service';
import { AppConfigServiceMock } from '../common/mock/app-config.service.mock';
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class AppExtensionServiceMock {
references$: Observable<ExtensionRef[]>;
private _references = new BehaviorSubject<ExtensionRef[]>([]);
constructor() {
this.references$ = this._references.asObservable();
}
getViewerExtensions(): ViewerExtensionRef[] {
return [];
}
}
type AboutStoryArgs = AboutComponent & {
dev?: boolean;
pkg?: any;
regexp?: string;
};
const meta: Meta<AboutStoryArgs> = {
component: AboutComponent,
title: 'Core/About/About',
decorators: [
moduleMetadata({
imports: [AboutComponent],
providers: [
{ provide: AuthenticationService, useClass: AuthenticationMock },
{ provide: AppExtensionService, useClass: AppExtensionServiceMock },
{ provide: AppConfigService, useClass: AppConfigServiceMock }
]
})
],
argTypes: {
dev: {
control: 'boolean',
description: 'If active show more information about the app and the platform useful in debug.',
defaultValue: false,
table: {
type: { summary: 'boolean' },
defaultValue: { summary: 'false' }
}
},
pkg: {
control: 'object',
description: 'pkg json.',
table: {
type: { summary: 'object' }
}
},
regexp: {
control: 'text',
description: 'Regular expression for filtering dependencies packages.',
defaultValue: '^(@alfresco)',
table: {
type: { summary: 'string' },
defaultValue: { summary: '^(@alfresco)' }
}
}
}
};
export default meta;
type Story = StoryObj<AboutStoryArgs>;
export const About: Story = {
render: (args) => ({
props: args
}),
args: {
pkg: {
name: 'My Storybook App',
commit: 'my-commit-value',
version: '1.0.0',
dependencies: {
'@alfresco/adf-content-services': '4.7.0',
'@alfresco/adf-core': '4.7.0',
'@alfresco/adf-extensions': '4.7.0',
'@alfresco/adf-process-services': '4.7.0',
'@alfresco/adf-process-services-cloud': '4.7.0',
'@alfresco/js-api': '4.7.0-3976'
}
}
}
};
@@ -0,0 +1,57 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Observable, of, throwError } from 'rxjs';
import { Injectable } from '@angular/core';
import { AuthenticationService } from '../services/authentication.service';
@Injectable({
providedIn: 'root'
})
export class AuthenticationMock extends AuthenticationService {
login(username: string, password: string): Observable<{ type: string; ticket: any }> {
if (username === 'fake-username' && password === 'fake-password') {
return of({ type: 'type', ticket: 'ticket' });
}
if (username === 'fake-username-CORS-error' && password === 'fake-password') {
return throwError(() => ({
error: {
crossDomain: true,
message: 'ERROR: the network is offline, Origin is not allowed by Access-Control-Allow-Origin'
}
}));
}
if (username === 'fake-username-CSRF-error' && password === 'fake-password') {
return throwError(() => ({ message: 'ERROR: Invalid CSRF-token', status: 403 }));
}
if (username === 'fake-username-ECM-access-error' && password === 'fake-password') {
return throwError(() => ({
message: 'ERROR: 00170728 Access Denied. The system is currently in read-only mode',
status: 403
}));
}
return throwError(() => 'Fake server error');
}
logout(): Observable<any> {
return of({});
}
}
@@ -0,0 +1,61 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Meta, StoryObj } from '@storybook/angular';
import { CardViewArrayItemComponent } from './card-view-arrayitem.component';
import { CardViewArrayItemModel } from '../../public-api';
import { of } from 'rxjs';
import { cardViewSharedMeta } from '../../stories/card-view-shared-meta';
const meta: Meta<CardViewArrayItemComponent> = {
...cardViewSharedMeta,
component: CardViewArrayItemComponent,
title: 'Core/Card View/Card View Array Item',
argTypes: {
...cardViewSharedMeta.argTypes,
property: {
description: 'Card View Item Model with data',
table: {
type: { summary: 'CardViewArrayItemModel' }
}
}
}
};
export default meta;
type Story = StoryObj<CardViewArrayItemComponent>;
export const CardViewArrayItem: Story = {
render: (args) => ({
props: args
}),
args: {
property: new CardViewArrayItemModel({
label: 'CardView Array of items',
value: of([
{ icon: 'directions_bike', value: 'Zlatan' },
{ icon: 'directions_bike', value: 'Lionel Messi' },
{ value: 'Mohamed', directions_bike: 'save' },
{ value: 'Ronaldo' }
]),
key: 'array',
icon: 'edit',
default: 'Empty',
noOfItemsToDisplay: 2
})
}
};
@@ -0,0 +1,54 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Meta, StoryObj } from '@storybook/angular';
import { CardViewBoolItemComponent } from './card-view-boolitem.component';
import { CardViewBoolItemModel } from '../../public-api';
import { cardViewSharedMeta } from '../../stories/card-view-shared-meta';
const meta: Meta<CardViewBoolItemComponent> = {
...cardViewSharedMeta,
component: CardViewBoolItemComponent,
title: 'Core/Card View/Card View Bool Item',
argTypes: {
...cardViewSharedMeta.argTypes,
property: {
description: 'Card View Item Model with data',
table: {
type: { summary: 'CardViewBoolItemModel' }
}
}
}
};
export default meta;
type Story = StoryObj<CardViewBoolItemComponent>;
export const CardViewBoolItem: Story = {
render: (args) => ({
props: args
}),
args: {
property: new CardViewBoolItemModel({
label: 'Agree to all terms and conditions',
value: true,
key: 'boolean',
default: false,
editable: true
})
}
};
@@ -0,0 +1,107 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Meta, StoryObj } from '@storybook/angular';
import { CardViewDateItemComponent } from './card-view-dateitem.component';
import { CardViewDateItemModel, CardViewDatetimeItemModel } from '../../public-api';
import { cardViewSharedMeta } from '../../stories/card-view-shared-meta';
const meta: Meta<CardViewDateItemComponent> = {
...cardViewSharedMeta,
component: CardViewDateItemComponent,
title: 'Core/Card View/Card View Date Item',
argTypes: {
...cardViewSharedMeta.argTypes,
property: {
description: 'Card View Item Model with data',
table: {
type: {
summary: 'CardViewDateItemModel | CardViewDatetimeItemModel'
}
}
}
}
};
export default meta;
type Story = StoryObj<CardViewDateItemComponent>;
export const SingleValuedDateItemCardView: Story = {
render: (args) => ({
props: args
}),
args: {
property: new CardViewDateItemModel({
label: 'CardView Date Item',
value: [new Date(1983, 11, 24, 10, 0, 30)],
key: 'date',
default: new Date(1983, 11, 24, 10, 0, 30),
format: 'shortDate',
editable: true
})
}
};
export const MultiValuedDateItemCardView: Story = {
render: (args) => ({
props: args
}),
args: {
property: new CardViewDateItemModel({
label: 'CardView Date Item - Multivalue (chips)',
value: [new Date(1983, 11, 24, 10, 0, 30)],
key: 'date',
default: new Date(1983, 11, 24, 10, 0, 30),
format: 'shortDate',
editable: true,
multivalued: true
})
}
};
export const SingleValuedDatetimeItemCardView: Story = {
render: (args) => ({
props: args
}),
args: {
property: new CardViewDatetimeItemModel({
label: 'CardView Datetime Item',
value: undefined,
key: 'datetime',
default: undefined,
format: 'short',
editable: true
})
}
};
export const MultiValuedDatetimeItemCardView: Story = {
render: (args) => ({
props: args
}),
args: {
property: new CardViewDatetimeItemModel({
label: 'CardView Datetime Item - Multivalue (chips)',
value: undefined,
key: 'datetime',
default: undefined,
format: 'short',
editable: true,
multivalued: true
})
}
};
@@ -0,0 +1,56 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Meta, StoryObj } from '@storybook/angular';
import { CardViewKeyValuePairsItemComponent } from './card-view-keyvaluepairsitem.component';
import { CardViewKeyValuePairsItemModel } from '../../public-api';
import { cardViewSharedMeta } from '../../stories/card-view-shared-meta';
const meta: Meta<CardViewKeyValuePairsItemComponent> = {
...cardViewSharedMeta,
component: CardViewKeyValuePairsItemComponent,
title: 'Core/Card View/Card View Key Value Pairs Item',
argTypes: {
...cardViewSharedMeta.argTypes,
property: {
description: 'Card View Item Model with data',
table: {
type: { summary: 'CardViewKeyValuePairsItemModel' }
}
}
}
};
export default meta;
type Story = StoryObj<CardViewKeyValuePairsItemComponent>;
export const CardViewKeyValuePairsItem: Story = {
render: (args) => ({
props: args
}),
args: {
property: new CardViewKeyValuePairsItemModel({
label: 'CardView Key-Value Pairs Item',
value: [
{ name: 'hey', value: 'you' },
{ name: 'hey', value: 'you' }
],
key: 'key-value-pairs',
editable: true
})
}
};
@@ -0,0 +1,67 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Meta, StoryObj } from '@storybook/angular';
import { CardViewMapItemComponent } from './card-view-mapitem.component';
import { CardViewMapItemModel } from '../../public-api';
import { cardViewSharedMeta } from '../../stories/card-view-shared-meta';
const meta: Meta<CardViewMapItemComponent> = {
...cardViewSharedMeta,
component: CardViewMapItemComponent,
title: 'Core/Card View/Card View Map Item',
argTypes: {
...cardViewSharedMeta.argTypes,
property: {
description: 'Card View Item Model with data',
table: {
type: { summary: 'CardViewMapItemModel' }
}
}
}
};
export default meta;
type Story = StoryObj<CardViewMapItemComponent>;
export const CardViewMapItem: Story = {
render: (args) => ({
props: args
}),
args: {
property: new CardViewMapItemModel({
label: 'My map',
value: new Map([['999', 'My Value']]),
key: 'map',
default: 'default map value'
})
}
};
export const EmptyCardViewMapItem: Story = {
render: (args) => ({
props: args
}),
args: {
property: new CardViewMapItemModel({
label: 'My map',
value: [],
key: 'map',
default: 'default map value'
})
}
};
@@ -0,0 +1,71 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Meta, StoryObj } from '@storybook/angular';
import { CardViewSelectItemComponent } from './card-view-selectitem.component';
import { CardViewSelectItemModel } from '../../public-api';
import { of } from 'rxjs';
import { cardViewSharedMeta } from '../../stories/card-view-shared-meta';
const meta: Meta<CardViewSelectItemComponent> = {
...cardViewSharedMeta,
component: CardViewSelectItemComponent,
title: 'Core/Card View/Card View Select Item',
argTypes: {
...cardViewSharedMeta.argTypes,
options$: {
control: { disable: true },
description: 'Data displayed in select element',
table: {
type: {
summary: 'Observable<CardViewSelectItemOption<string | number>[]>'
}
}
},
property: {
description: 'Card View Item Model with data',
table: {
type: { summary: 'CardViewSelectItemModel' }
}
}
},
args: {
...cardViewSharedMeta.args,
editable: false
}
};
export default meta;
type Story = StoryObj<CardViewSelectItemComponent>;
export const CardViewSelectItem: Story = {
render: (args) => ({
props: args
}),
args: {
property: new CardViewSelectItemModel({
label: 'CardView Select Item',
value: 'one',
options$: of([
{ key: 'one', label: 'One' },
{ key: 'two', label: 'Two' }
]),
key: 'select',
editable: true
})
}
};
@@ -0,0 +1,128 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Meta, StoryObj } from '@storybook/angular';
import { CardViewTextItemComponent } from './card-view-textitem.component';
import { CardViewTextItemModel } from '../../public-api';
import { cardViewSharedMeta } from '../../stories/card-view-shared-meta';
const meta: Meta<CardViewTextItemComponent> = {
...cardViewSharedMeta,
component: CardViewTextItemComponent,
title: 'Core/Card View/Card View Text Item',
argTypes: {
...cardViewSharedMeta.argTypes
},
args: {
...cardViewSharedMeta.args,
editable: false
}
};
export default meta;
type Story = StoryObj<CardViewTextItemComponent>;
export const ClickableCardViewTextItem: Story = {
render: (args) => ({
props: args
}),
args: {
property: new CardViewTextItemModel({
label: 'CardView Text Item - Clickable template',
value: 'click here',
key: 'click',
default: 'click here',
editable: true,
clickable: true,
icon: 'close'
})
}
};
export const ChipsCardViewTextItem: Story = {
render: (args) => ({
props: args
}),
args: {
property: new CardViewTextItemModel({
label: 'CardView Text Item - Chips template',
value: [1, 2, 3, 4],
key: 'name',
default: 'default bar',
multiline: true,
multivalued: true,
icon: 'icon',
editable: true
}),
displayLabelForChips: false
}
};
export const EmptyCardViewTextItem: Story = {
render: (args) => ({
props: args
}),
args: {
property: new CardViewTextItemModel({
label: 'CardView Text Item - Empty template',
value: undefined,
key: 'empty',
default: '',
icon: 'icon',
editable: false
}),
editable: false,
displayEmpty: false
}
};
export const DefaultCardViewTextItem: Story = {
render: (args) => ({
props: args
}),
args: {
property: new CardViewTextItemModel({
label: 'CardView Text Item - Default template',
value: 'input here',
key: 'default',
default: 'input here',
editable: true,
clickable: false,
icon: 'close',
multiline: false
})
}
};
export const DisplayLabelForChipsCardTextItem: Story = {
render: (args) => ({
props: args
}),
args: {
property: new CardViewTextItemModel({
label: 'CardView Text Item - Multi-Valued Chips template',
value: ['Chip 1', 'Chip 2', 'Chip 3'],
key: 'multivalued',
default: 'default value',
multiline: true,
multivalued: true,
icon: 'icon',
editable: true
}),
displayLabelForChips: false
}
};
@@ -0,0 +1,59 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Meta, StoryObj } from '@storybook/angular';
import { CardViewComponent } from './card-view.component';
import { cardViewDataSource, cardViewUndefinedValues } from '../../mock/card-view-content.mock';
import { cardViewSharedMeta } from '../../stories/card-view-shared-meta';
const meta: Meta<CardViewComponent> = {
...cardViewSharedMeta,
component: CardViewComponent,
title: 'Core/Card View/Card View',
argTypes: {
...cardViewSharedMeta.argTypes,
editable: {
...cardViewSharedMeta.argTypes.editable,
table: {
...cardViewSharedMeta.argTypes.editable.table,
defaultValue: { summary: 'true' }
}
}
}
};
export default meta;
type Story = StoryObj<CardViewComponent>;
export const DefaultCardView: Story = {
render: (args) => ({
props: args
}),
args: {
properties: cardViewDataSource
}
};
export const EmptyCardView: Story = {
render: (args) => ({
props: args
}),
args: {
properties: cardViewUndefinedValues,
editable: false
}
};
@@ -0,0 +1,185 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
CardViewArrayItemModel,
CardViewBoolItemModel,
CardViewDateItemModel,
CardViewDatetimeItemModel,
CardViewIntItemModel,
CardViewKeyValuePairsItemModel,
CardViewMapItemModel,
CardViewSelectItemModel,
CardViewTextItemModel
} from '../public-api';
import { of } from 'rxjs';
export const cardViewDataSource = [
new CardViewTextItemModel({
label: 'CardView Text Item - Multivalue (chips)',
value: [1, 2, 3, 4],
key: 'name',
default: 'default bar',
multiline: true,
multivalued: true,
icon: 'icon',
editable: true
}),
new CardViewDateItemModel({
label: 'CardView Date Item - Multivalue (chips)',
value: [new Date(1983, 11, 24, 10, 0, 30)],
key: 'date',
default: new Date(1983, 11, 24, 10, 0, 30),
format: 'shortDate',
editable: true,
multivalued: true
}),
new CardViewDatetimeItemModel({
label: 'CardView Datetime Item - Multivalue (chips)',
value: [new Date(1983, 11, 24, 10, 0, 0)],
key: 'datetime',
default: new Date(1983, 11, 24, 10, 0, 0),
format: 'short',
editable: true,
multivalued: true
}),
new CardViewBoolItemModel({
label: 'Agree to all terms and conditions',
value: true,
key: 'boolean',
default: false,
editable: true
}),
new CardViewIntItemModel({
label: 'CardView Int Item',
value: 213,
key: 'int',
default: 1,
editable: true
}),
new CardViewKeyValuePairsItemModel({
label: 'CardView Key-Value Pairs Item',
value: [
{ name: 'hey', value: 'you' },
{ name: 'hey', value: 'you' }
],
key: 'key-value-pairs',
editable: true
}),
new CardViewSelectItemModel({
label: 'CardView Select Item',
value: 'one',
options$: of([
{ key: 'one', label: 'One' },
{ key: 'two', label: 'Two' }
]),
key: 'select',
editable: true
}),
new CardViewMapItemModel({
label: 'My map',
value: new Map([['999', 'My Value']]),
key: 'map',
default: 'default map value'
}),
new CardViewTextItemModel({
label: 'This is clickable ',
value: 'click here',
key: 'click',
default: 'click here',
editable: true,
clickable: true,
icon: 'close'
}),
new CardViewArrayItemModel({
label: 'CardView Array of items',
value: of([
{ icon: 'directions_bike', value: 'Zlatan' },
{ icon: 'directions_bike', value: 'Lionel Messi' },
{ value: 'Mohamed', directions_bike: 'save' },
{ value: 'Ronaldo' }
]),
key: 'array',
icon: 'edit',
default: 'Empty',
noOfItemsToDisplay: 2,
editable: true
})
];
export const cardViewUndefinedValues = [
new CardViewTextItemModel({
label: 'CardView Text Item - Multivalue (chips)',
value: undefined,
key: 'name',
default: undefined,
multiline: true,
multivalued: true,
icon: 'icon',
editable: true
}),
new CardViewDateItemModel({
label: 'CardView Date Item - Multivalue (chips)',
value: undefined,
key: 'date',
default: undefined,
format: 'shortDate',
editable: true,
multivalued: true
}),
new CardViewDatetimeItemModel({
label: 'CardView Datetime Item - Multivalue (chips)',
value: undefined,
key: 'datetime',
default: undefined,
format: 'short',
editable: true,
multivalued: true
}),
new CardViewIntItemModel({
label: 'CardView Int Item',
value: undefined,
key: 'int',
default: undefined,
editable: true
}),
new CardViewSelectItemModel({
label: 'CardView Select Item',
value: undefined,
options$: of([
{ key: 'one', label: 'One' },
{ key: 'two', label: 'Two' }
]),
key: 'select',
editable: true
}),
new CardViewMapItemModel({
label: 'My map',
value: undefined,
key: 'map',
default: undefined
}),
new CardViewTextItemModel({
label: 'This is clickable ',
value: undefined,
key: 'click',
default: undefined,
editable: true,
clickable: true,
icon: 'close'
})
];
@@ -0,0 +1,132 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { applicationConfig, moduleMetadata, Decorator, type ArgTypes } from '@storybook/angular';
import { CARD_VIEW_DIRECTIVES } from '../public-api';
import { provideStoryCore } from '../../stories/core-story.providers';
/**
* Common decorators used across all Card View component stories.
* Includes module metadata with Card View directives and application config with core providers.
*/
export const cardViewDecorators: Decorator[] = [
moduleMetadata({
imports: [...CARD_VIEW_DIRECTIVES]
}),
applicationConfig({
providers: [...provideStoryCore()]
})
];
/**
* Common argTypes definitions shared across Card View component stories.
* These can be spread into component-specific meta configurations.
*/
export const cardViewArgTypes: ArgTypes = {
editable: {
control: 'boolean',
description: 'Defines if CardView item is editable',
table: {
type: { summary: 'boolean' },
defaultValue: { summary: 'false' }
}
},
displayEmpty: {
control: 'boolean',
description: 'Defines if it should display CardView item when data is empty',
table: {
type: { summary: 'boolean' },
defaultValue: { summary: 'true' }
}
},
displayNoneOption: {
control: 'boolean',
description: 'Shows None option inside select element',
table: {
type: { summary: 'boolean' },
defaultValue: { summary: 'true' }
}
},
displayClearAction: {
control: 'boolean',
description: 'Defines if it should display clear input action (only with SingleValued components)',
table: {
type: { summary: 'boolean' },
defaultValue: { summary: 'true' }
}
},
copyToClipboardAction: {
control: 'boolean',
description: 'Copy to clipboard action - default template in editable mode',
table: {
type: { summary: 'boolean' },
defaultValue: { summary: 'true' }
}
},
useChipsForMultiValueProperty: {
control: 'boolean',
description: 'Split text for chips using defined separator',
table: {
type: { summary: 'boolean' },
defaultValue: { summary: 'true' }
}
},
multiValueSeparator: {
control: 'text',
description: 'Separator used for text splitting',
table: {
type: { summary: 'string' },
defaultValue: { summary: ', ' }
}
},
displayLabelForChips: {
control: 'boolean',
description: 'Display label for chips property',
table: {
type: { summary: 'boolean' },
defaultValue: { summary: 'false' }
}
}
};
/**
* Common default args shared across Card View component stories.
*/
export const cardViewDefaultArgs: Record<string, unknown> = {
editable: true,
displayEmpty: true,
displayNoneOption: true,
displayClearAction: true,
copyToClipboardAction: true,
useChipsForMultiValueProperty: true,
multiValueSeparator: ', ',
displayLabelForChips: false
};
/**
* Shared metadata object that can be spread into component-specific meta configurations.
* Contains decorators, argTypes, and args commonly used across Card View stories.
*/
export const cardViewSharedMeta: {
decorators: Decorator[];
argTypes: ArgTypes;
args: Record<string, unknown>;
} = {
decorators: cardViewDecorators,
argTypes: cardViewArgTypes,
args: cardViewDefaultArgs
};
@@ -0,0 +1,82 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { applicationConfig, Meta, StoryObj, moduleMetadata } from '@storybook/angular';
import { CommentListComponent } from './comment-list.component';
import { commentsTaskData, commentsNodeData } from '../mocks/comments.stories.mock';
import { CommentsServiceStoriesMock } from '../mocks/comments.service.stories.mock';
import { ADF_COMMENTS_SERVICE } from '../interfaces/comments.token';
import { provideStoryCore } from '../../stories/core-story.providers';
const meta: Meta<CommentListComponent> = {
component: CommentListComponent,
title: 'Core/Comments/Comment List',
decorators: [
moduleMetadata({
imports: [CommentListComponent],
providers: [{ provide: ADF_COMMENTS_SERVICE, useClass: CommentsServiceStoriesMock }]
}),
applicationConfig({
providers: [...provideStoryCore()]
})
],
parameters: {
docs: {
description: {
component: 'Displays a list of comments from users involved in a specified task or node'
}
}
},
argTypes: {
comments: {
control: 'object',
description: 'CommentModel array',
table: {
type: { summary: 'CommentModel[]' }
}
},
clickRow: {
action: 'clickRow',
description: 'Emitted when the user clicks on one of the comment rows',
table: {
category: 'Actions',
type: { summary: 'EventEmitter <CommentModel>' }
}
}
}
};
export default meta;
type Story = StoryObj<CommentListComponent>;
export const TaskBased: Story = {
render: (args) => ({
props: args
}),
args: {
comments: commentsTaskData
}
};
export const NodeBased: Story = {
render: (args) => ({
props: args
}),
args: {
comments: commentsNodeData
}
};
@@ -0,0 +1,122 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { applicationConfig, Meta, StoryObj, moduleMetadata } from '@storybook/angular';
import { CommentsComponent } from './comments.component';
import { ADF_COMMENTS_SERVICE } from './interfaces/comments.token';
import { commentsStoriesData } from './mocks/comments.stories.mock';
import { CommentsServiceStoriesMock } from './mocks/comments.service.stories.mock';
import { provideStoryCore } from '../stories/core-story.providers';
const meta: Meta<CommentsComponent> = {
component: CommentsComponent,
title: 'Core/Comments/Comment',
decorators: [
moduleMetadata({
imports: [CommentsComponent],
providers: [
{ provide: CommentsServiceStoriesMock, useValue: { getUserProfileImage: () => '../assets/images/logo.png' } },
{ provide: ADF_COMMENTS_SERVICE, useClass: CommentsServiceStoriesMock }
]
}),
applicationConfig({
providers: [...provideStoryCore()]
})
],
parameters: {
docs: {
description: {
component: `Displays comments from users involved in a specified environment.
Allows an involved user to add a comment to a environment.`
}
}
},
argTypes: {
comments: {
control: 'object',
description: 'CommentModel array',
table: { type: { summary: 'CommentModel[]' } }
},
readOnly: {
control: 'boolean',
description: 'Displays input area to add new comment',
defaultValue: false,
table: {
type: { summary: 'boolean' },
defaultValue: { summary: 'false' }
}
},
id: {
control: 'text',
description: 'Necessary in order to add a new comment',
table: {
type: { summary: 'string' }
}
},
error: {
action: 'error',
description: 'Emitted when an error occurs while displaying/adding a comment',
table: {
category: 'Actions',
type: { summary: 'EventEmitter <any>' }
}
}
},
args: {
comments: commentsStoriesData,
id: '-fake-'
}
};
export default meta;
type Story = StoryObj<CommentsComponent>;
export const SingleCommentWithAvatar: Story = {
render: (args) => ({
props: args
}),
args: {
comments: [commentsStoriesData[0]],
readOnly: true
}
};
export const SingleCommentWithoutAvatar: Story = {
render: (args) => ({
props: args
}),
args: {
comments: [commentsStoriesData[1]],
readOnly: true
}
};
export const NoComments: Story = {
render: (args) => ({
props: args
}),
args: {
comments: [],
readOnly: true
}
};
export const Comments: Story = {
render: (args) => ({
props: args
})
};
@@ -0,0 +1,79 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { CommentModel, User } from '../../models';
import { Observable, of } from 'rxjs';
import { CommentsService } from '../interfaces/comments-service.interface';
import { testUser } from './comments.stories.mock';
export class CommentsServiceStoriesMock implements Partial<CommentsService> {
get(_id: string): Observable<CommentModel[]> {
return commentsResponseMock.getComments();
}
add(_id: string, message = 'test comment'): Observable<CommentModel> {
return commentsResponseMock.addComment(message);
}
getUserImage(_userId: string): string {
return '../assets/images/logo.png';
}
}
const commentUser = new User({
enabled: true,
firstName: 'hruser',
displayName: 'hruser',
id: 'hruser',
email: 'test'
});
export const commentsResponseMock = {
getComments: () =>
of([
new CommentModel({
id: 1,
message: 'Test Comment',
created: new Date(),
createdBy: commentUser,
isSelected: false
}),
new CommentModel({
id: 2,
message: 'Test Comment',
created: new Date(),
createdBy: commentUser,
isSelected: false
}),
new CommentModel({
id: 3,
message: 'Test Comment',
created: new Date(),
createdBy: commentUser,
isSelected: false
})
]),
addComment: (message: string) =>
of(
new CommentModel({
id: 1,
message,
created: new Date(),
createdBy: testUser,
isSelected: false
})
)
};
@@ -0,0 +1,166 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { CommentModel } from '../../models';
const fakeCompany: any = {
organization: '',
address1: '',
address2: '',
address3: '',
postcode: '',
telephone: '',
fax: '',
email: ''
};
export const getDateXMinutesAgo = (minutes: number) => new Date(Date.now() - minutes * 60000);
const johnDoe: any = {
id: '1',
email: 'john.doe@alfresco.com',
firstName: 'John',
lastName: 'Doe',
company: fakeCompany,
enabled: true,
isAdmin: undefined,
avatarId: '001'
};
const janeEod: any = {
id: '2',
email: 'jane.eod@alfresco.com',
firstName: 'Jane',
lastName: 'Eod',
company: fakeCompany,
enabled: true,
isAdmin: undefined
};
const robertSmith: any = {
id: '3',
email: 'robert.smith@alfresco.com',
firstName: 'Robert',
lastName: 'Smith',
company: fakeCompany,
enabled: true,
isAdmin: undefined
};
export const testUser: any = {
id: '44',
email: 'test.user@hyland.com',
firstName: 'Test',
lastName: 'User',
company: fakeCompany,
enabled: true,
isAdmin: undefined,
avatarId: '044'
};
export const commentsStoriesData: CommentModel[] = [
new CommentModel({
id: 1,
message: `I've done this task, what's next?`,
created: getDateXMinutesAgo(30),
createdBy: johnDoe,
isSelected: false
}),
new CommentModel({
id: 2,
message: `I've assigned you another one 🤠`,
created: getDateXMinutesAgo(15),
createdBy: janeEod,
isSelected: false
}),
new CommentModel({
id: 3,
message: '+1',
created: getDateXMinutesAgo(12),
createdBy: robertSmith,
isSelected: false
}),
new CommentModel({
id: 4,
message: 'Cheers',
created: new Date(),
createdBy: johnDoe,
isSelected: false
})
];
export const commentsNodeData: CommentModel[] = [
new CommentModel({
id: 1,
message: `I've done this component, is it cool?`,
created: getDateXMinutesAgo(30),
createdBy: johnDoe,
isSelected: false
}),
new CommentModel({
id: 2,
message: 'Yeah',
created: getDateXMinutesAgo(15),
createdBy: janeEod,
isSelected: false
}),
new CommentModel({
id: 3,
message: '+1',
created: getDateXMinutesAgo(12),
createdBy: robertSmith,
isSelected: false
}),
new CommentModel({
id: 4,
message: 'ty',
created: new Date(),
createdBy: johnDoe,
isSelected: false
})
];
export const commentsTaskData: CommentModel[] = [
new CommentModel({
id: 1,
message: `I've done this task, what's next?`,
created: getDateXMinutesAgo(30),
createdBy: johnDoe,
isSelected: false
}),
new CommentModel({
id: 2,
message: `I've assigned you another one 🤠`,
created: getDateXMinutesAgo(15),
createdBy: janeEod,
isSelected: false
}),
new CommentModel({
id: 3,
message: '+1',
created: getDateXMinutesAgo(12),
createdBy: robertSmith,
isSelected: false
}),
new CommentModel({
id: 4,
message: 'Cheers',
created: new Date(),
createdBy: johnDoe,
isSelected: false
})
];
@@ -0,0 +1,514 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { applicationConfig, Meta, StoryObj, moduleMetadata } from '@storybook/angular';
import { DataTableComponent, ShowHeaderMode } from './datatable.component';
import { ColumnsSelectorComponent } from '../columns-selector/columns-selector.component';
import { EmptyListBodyDirective, EmptyListComponent, EmptyListFooterDirective, EmptyListHeaderDirective } from '../empty-list/empty-list.component';
import { LoadingContentTemplateDirective } from '../../directives/loading-template.directive';
import { NoContentTemplateDirective } from '../../directives/no-content-template.directive';
import { NoPermissionTemplateDirective } from '../../directives/no-permission-template.directive';
import { MainMenuDataTableTemplateDirective } from '../../directives/main-data-table-action-template.directive';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { mockPathInfos } from '../mocks/datatable.mock';
import { provideStoryCore } from '../../../stories/core-story.providers';
const meta: Meta<DataTableComponent> = {
component: DataTableComponent,
title: 'Core/Datatable/Datatable',
decorators: [
moduleMetadata({
imports: [
ColumnsSelectorComponent,
EmptyListComponent,
EmptyListHeaderDirective,
EmptyListBodyDirective,
EmptyListFooterDirective,
NoContentTemplateDirective,
NoPermissionTemplateDirective,
LoadingContentTemplateDirective,
MainMenuDataTableTemplateDirective,
DataTableComponent,
MatProgressSpinnerModule
]
}),
applicationConfig({
providers: [...provideStoryCore()]
})
],
argTypes: {
data: {
control: 'object',
description: 'Data source for the table',
table: {
category: 'Data',
type: { summary: 'DataTableAdapter' }
}
},
rows: {
control: 'object',
description: 'The rows that the datatable will show.',
table: {
category: 'Data',
type: { summary: 'any[]' },
defaultValue: { summary: '[]' }
}
},
sorting: {
control: 'object',
description: 'A string array.\n\n' + 'First element describes the key to sort by.\n\n' + 'Second element describes the sorting order.',
table: {
type: { summary: 'any[]' },
defaultValue: { summary: '[]' }
}
},
columns: {
control: 'object',
description: 'The columns that the datatable will show.',
table: {
category: 'Data',
type: { summary: 'any[]' },
defaultValue: { summary: '[]' }
}
},
selectionMode: {
control: 'inline-radio',
description: 'Row selection mode.',
options: ['none', 'single', 'multiple'],
table: {
category: 'Selection',
type: { summary: 'string' },
defaultValue: { summary: 'single' }
}
},
multiselect: {
control: 'boolean',
description: 'Toggles multiple row selection, which renders checkboxes at the beginning of each row.',
table: {
category: 'Selection',
type: { summary: 'boolean' },
defaultValue: { summary: 'false' }
}
},
mainTableAction: {
control: 'boolean',
description: 'Toggles main data table action column.',
table: {
category: 'Data Actions Column',
type: { summary: 'boolean' },
defaultValue: { summary: 'true' }
}
},
actions: {
control: 'boolean',
description: 'Toggles the data actions column.',
table: {
category: 'Data Actions Column',
type: { summary: 'boolean' },
defaultValue: { summary: 'false' }
}
},
showMainDatatableActions: {
control: 'boolean',
description: 'Toggles the main datatable action.',
table: {
category: 'Data Actions Column',
type: { summary: 'boolean' },
defaultValue: { summary: 'false' }
}
},
actionsPosition: {
control: 'inline-radio',
description: 'Position of the actions dropdown menu.',
options: ['right', 'left'],
table: {
category: 'Data Actions Column',
type: { summary: 'string' },
defaultValue: { summary: 'right' }
}
},
actionsVisibleOnHover: {
control: 'boolean',
description: 'Toggles whether the actions dropdown should only be visible if the row is hovered over or the dropdown menu is open.',
table: {
category: 'Data Actions Column',
type: { summary: 'boolean' },
defaultValue: { summary: 'false' }
}
},
fallbackThumbnail: {
control: 'text',
description: 'Fallback image for rows where the thumbnail is missing.',
table: {
type: { summary: 'string' }
}
},
contextMenu: {
control: 'boolean',
description: 'Toggles custom context menu for the component.',
table: {
type: { summary: 'boolean' },
defaultValue: { summary: 'false' }
}
},
rowStyle: {
control: 'object',
description:
'The inline style to apply to every row. See [NgStyle](https://angular.io/docs/ts/latest/api/common/index/NgStyle-directive.html) docs for more details and usage examples.',
table: {
category: 'Custom Row Styles',
type: { summary: '{ [key: string]: any }' }
}
},
rowStyleClass: {
control: 'text',
description: 'The CSS class to apply to every row.',
table: {
category: 'Custom Row Styles',
type: { summary: 'string' },
defaultValue: { summary: '' }
}
},
showHeader: {
control: 'inline-radio',
description: 'Toggles the header visibility mode.',
options: ['never', 'always', 'data'],
table: {
category: 'Header',
type: { summary: 'string' },
defaultValue: { summary: 'data' }
}
},
stickyHeader: {
control: 'boolean',
description: 'Toggles the sticky header mode.',
table: {
category: 'Header',
type: { summary: 'boolean' },
defaultValue: { summary: 'false' }
}
},
loading: {
control: 'boolean',
table: {
category: 'Table Template',
type: { summary: 'boolean' },
defaultValue: { summary: 'false' }
}
},
noPermission: {
control: 'boolean',
table: {
category: 'Table Template',
type: { summary: 'boolean' },
defaultValue: { summary: 'false' }
}
},
rowMenuCacheEnabled: {
control: 'boolean',
description: 'Should the items for the row actions menu be cached for reuse after they are loaded the first time?',
table: {
type: { summary: 'boolean' },
defaultValue: { summary: 'false' }
}
},
allowFiltering: {
control: 'boolean',
description: 'Flag that indicate if the datatable allow the use facet widget search for filtering.',
table: {
type: { summary: 'boolean' },
defaultValue: { summary: 'false' }
}
},
rowClick: {
action: 'rowClick',
description: 'Emitted when the user clicks a row.',
table: { category: 'Actions' }
},
rowDblClick: {
action: 'rowDblClick',
description: 'Emitted when the user double-clicks a row.',
table: { category: 'Actions' }
},
/* commented until [AAE-10239] fixed
showRowContextMenu: {
action: 'showRowContextMenu',
description: 'Emitted before the context menu is displayed for a row.',
table: { category: 'Actions' }
},
showRowActionsMenu: {
action: 'showRowActionsMenu',
description: 'Emitted before the actions menu is displayed for a row.',
table: { category: 'Actions' }
},
*/
executeRowAction: {
action: 'executeRowAction',
description: 'Emitted when the user executes a row action.',
table: { category: 'Actions' }
},
columnOrderChanged: {
action: 'columnOrderChanged',
description: 'Emitted when the order of columns changed.',
table: { category: 'Actions' }
}
},
args: {
rows: [
{
id: 1,
textCol: 'This is a very long text inside the text column to check if the hidden text will be displayed on hover.',
imageCol: 'material-icons://folder_open',
iconCol: 'folder_open',
dateCol: new Date(),
fileSizeCol: '536870912',
locationCol: mockPathInfos[0],
booleanCol: true,
amountCol: 100.55,
numberCol: 10000.31,
jsonCol: mockPathInfos[0]
},
{
id: 2,
textCol: 'Text 2',
imageCol: 'material-icons://cloud_outline',
iconCol: 'cloud_outline',
dateCol: new Date().setDate(new Date().getDate() - 1),
fileSizeCol: '524288',
locationCol: mockPathInfos[1],
booleanCol: false,
amountCol: 1020.123,
numberCol: 240.3,
jsonCol: mockPathInfos[1]
},
{
id: 3,
textCol: 'Text 3',
imageCol: 'material-icons://save',
iconCol: 'save',
dateCol: new Date().setDate(new Date().getDate() - 5),
fileSizeCol: '10737418240B',
locationCol: mockPathInfos[1],
booleanCol: 'true',
amountCol: -2020,
numberCol: 120,
jsonCol: mockPathInfos[1]
},
{
id: 4,
textCol: 'Text 4',
imageCol: 'material-icons://delete',
iconCol: 'delete',
dateCol: new Date().setDate(new Date().getDate() - 6),
fileSizeCol: '512B',
locationCol: mockPathInfos[2],
booleanCol: 'false',
amountCol: 230.76,
numberCol: 3.032,
jsonCol: mockPathInfos[2]
},
{
id: 5,
textCol: 'Text 5',
imageCol: 'material-icons://person_outline',
iconCol: 'person_outline',
dateCol: new Date().setDate(new Date().getDate() - 7),
fileSizeCol: '1073741824B',
locationCol: mockPathInfos[0],
booleanCol: 'false',
amountCol: 0.444,
numberCol: 2000,
jsonCol: mockPathInfos[0]
}
],
sorting: ['id', 'asc'],
columns: [
{ type: 'text', key: 'id', title: 'Id', sortable: true },
{
type: 'text',
key: 'textCol',
title: 'Text Column',
sortable: true,
draggable: true,
cssClass: 'adf-ellipsis-cell',
copyContent: true
},
{ type: 'image', key: 'imageCol', title: 'Image Column', draggable: true, cssClass: 'adf-ellipsis-cell' },
{ type: 'icon', key: 'iconCol', title: 'Icon Column', draggable: true, cssClass: 'adf-ellipsis-cell' },
{ type: 'date', key: 'dateCol', title: 'Date Column', sortable: true, draggable: true, cssClass: 'adf-ellipsis-cell' },
{
type: 'date',
key: 'dateCol',
title: 'Date Time Ago Column',
sortable: true,
draggable: true,
cssClass: 'adf-ellipsis-cell',
dateConfig: { format: 'timeAgo' }
},
{ type: 'fileSize', key: 'fileSizeCol', title: 'File Size Column', sortable: true, draggable: true, cssClass: 'adf-ellipsis-cell' },
{ type: 'location', format: '/files', key: 'locationCol', title: 'Location Column', draggable: true, cssClass: 'adf-ellipsis-cell' },
{ type: 'boolean', key: 'booleanCol', title: 'Boolean Column', draggable: true, cssClass: 'adf-ellipsis-cell' },
{ type: 'amount', key: 'amountCol', title: 'Amount Column', draggable: true, cssClass: 'adf-ellipsis-cell' },
{ type: 'number', key: 'numberCol', title: 'Number Column', draggable: true, cssClass: 'adf-ellipsis-cell' },
{ type: 'json', key: 'jsonCol', title: 'JSON Column', draggable: true, cssClass: 'adf-ellipsis-cell' }
],
selectionMode: 'single',
multiselect: false,
mainTableAction: true,
actions: false,
showMainDatatableActions: false,
actionsPosition: 'right',
actionsVisibleOnHover: false,
contextMenu: false,
rowStyleClass: '',
showHeader: ShowHeaderMode.Data,
stickyHeader: false,
loading: false,
noPermission: false,
rowMenuCacheEnabled: false,
allowFiltering: false
}
};
export default meta;
type Story = StoryObj<DataTableComponent>;
const insertContentToTemplate = (content: string): string =>
`<adf-datatable
[rows]=rows
[sorting]=sorting
[columns]=columns
[selectionMode]=selectionMode
[multiselect]=multiselect
[mainTableAction]=mainTableAction
[actions]=actions
[showMainDatatableActions]=showMainDatatableActions
[actionsPosition]=actionsPosition
[actionsVisibleOnHover]=actionsVisibleOnHover
[contextMenu]=contextMenu
[rowStyleClass]=rowStyleClass
[showHeader]=showHeader
[stickyHeader]=stickyHeader
[loading]=loading
[noPermission]=noPermission
[rowMenuCacheEnabled]=rowMenuCacheEnabled
[allowFiltering]=allowFiltering
(rowClick)=rowClick($event)
(rowDblClick)=rowDblClick($event)
(executeRowAction)=executeRowAction($event)
(columnOrderChanged)=columnOrderChanged($event)
>
${content}
</adf-datatable>`;
export const DefaultDatatable: Story = {
render: (args) => ({
props: args,
template: insertContentToTemplate('')
})
};
export const EmptyWithList: Story = {
render: (args) => ({
props: {
...args,
rows: []
},
template: insertContentToTemplate(`
<adf-empty-list>
<div adf-empty-list-header>Empty List Header</div>
<div adf-empty-list-body>Empty List Body</div>
<div adf-empty-list-footer>Empty List Footer</div>
</adf-empty-list>
`)
})
};
export const EmptyWithTemplate: Story = {
render: (args) => ({
props: {
...args,
rows: []
},
template: insertContentToTemplate(`
<adf-no-content-template>
<ng-template>Sorry, no content</ng-template>
</adf-no-content-template>
`)
})
};
export const LoadingWithTemplate: Story = {
render: (args) => ({
props: {
...args,
loading: true
},
template: insertContentToTemplate(`
<adf-loading-content-template>
<ng-template>
<mat-progress-spinner [mode]='indeterminate'>
</mat-progress-spinner>
</ng-template>
</adf-loading-content-template>
`)
})
};
export const NoPermissionWithTemplate: Story = {
render: (args) => ({
props: {
...args,
noPermission: true
},
template: insertContentToTemplate(`
<adf-no-permission-template>
<ng-template>
<div style=color:red;>You don't have permission to this content.</div>
</ng-template>
</adf-no-permission-template>
`)
})
};
export const MainMenuWithTemplate: Story = {
render: (args) => ({
props: {
...args,
mainTableAction: true,
showMainDatatableActions: true
},
template: insertContentToTemplate(`
<adf-main-menu-datatable-template>
<ng-template let-mainMenuTrigger>
<adf-datatable-column-selector [columns]=columns [mainMenuTrigger]=mainMenuTrigger>
</adf-datatable-column-selector>
</ng-template>
</adf-main-menu-datatable-template>
`)
})
};
export const StickyHeader: Story = {
render: (args) => ({
props: {
...args,
stickyHeader: true
},
template: '<div style="overflow:scroll;display:block;height:230px;">' + insertContentToTemplate(``) + '</div>'
})
};
@@ -15,13 +15,14 @@
* limitations under the License.
*/
import { PathInfo } from '../../../models/path.model';
import { DataColumn } from '../../data/data-column.model';
export const mockCarsData: any = [
{
car_id: 1,
car_name: 'Fiat 126p (Process)',
car_price: 599.0,
car_price: 599,
fuel_consumption: 5.25789,
is_available: 'false',
production_start: '1972-04-23',
@@ -131,3 +132,29 @@ export const mockCarsSchemaDefinition: DataColumn[] = [
draggable: true
}
];
export const mockPathInfos: PathInfo[] = [
{
elements: [
{ id: '1', name: 'User files', nodeType: 'folder' },
{ id: '2', name: 'Favorite', nodeType: 'folder' },
{ id: '3', name: 'Movies', nodeType: 'folder' }
],
name: '/User files/Favorite/Movies'
},
{
elements: [
{ id: '1', name: 'User files', nodeType: 'folder' },
{ id: '4', name: 'Photos', nodeType: 'folder' }
],
name: '/User files/Photos'
},
{
elements: [
{ id: '1', name: 'User files', nodeType: 'folder' },
{ id: '2', name: 'Favorite', nodeType: 'folder' },
{ id: '5', name: 'Series', nodeType: 'folder' }
],
name: '/User files/Favorite/Series'
}
];
@@ -0,0 +1,531 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { applicationConfig, Meta, StoryObj, moduleMetadata } from '@storybook/angular';
import { DataColumnComponent } from './data-column.component';
import { DataTableComponent } from '../components/datatable/datatable.component';
import { DataColumnListComponent, DateColumnHeaderComponent } from '../data-column';
import * as mockData from '../../mock/data-column.mock';
import { DataRow } from '../index';
import { provideStoryCore } from '../../stories/core-story.providers';
const meta: Meta<DataColumnComponent & { rows: any[] }> = {
component: DataColumnComponent,
title: 'Core/Data Column/Data Column',
decorators: [
moduleMetadata({
imports: [DataColumnComponent, DataColumnListComponent, DateColumnHeaderComponent, DataTableComponent]
}),
applicationConfig({
providers: [...provideStoryCore()]
})
],
argTypes: {
copyContent: {
description: 'Enables/disables a Clipboard directive to allow copying of cell contents.',
control: { type: 'boolean' },
table: {
category: 'Component Inputs',
type: {
summary: 'boolean'
}
}
},
cssClass: {
description: 'Additional CSS class to be applied to column (header and cells).',
control: { type: 'text' },
table: {
category: 'Component Inputs',
type: {
summary: 'string'
}
}
},
customData: {
description: 'You can specify any custom data which can be used by any specific feature',
control: { disable: true },
table: {
category: 'Component Inputs',
type: {
summary: 'any'
}
}
},
draggable: {
description: 'Toggles drag and drop for header column.',
control: { type: 'boolean' },
table: {
category: 'Component Inputs',
type: {
summary: 'boolean'
},
defaultValue: {
summary: 'false'
}
}
},
resizable: {
description: 'Toggles resize for column.',
control: { type: 'boolean' },
table: {
category: 'Component Inputs',
type: {
summary: 'boolean'
},
defaultValue: {
summary: 'true'
}
}
},
editable: {
description: 'Toggles the editing support of the column data.',
control: { type: 'boolean', disable: true },
table: {
category: 'Component Inputs',
type: {
summary: 'boolean'
},
defaultValue: {
summary: 'false'
}
}
},
focus: {
description: 'Enable or disable cell focus',
control: { disable: true },
table: {
category: 'Component Inputs',
type: {
summary: 'boolean'
},
defaultValue: {
summary: 'true'
}
}
},
format: {
description: 'Used for location type. Setups root path for router navigation.',
control: { type: 'text', disable: true },
table: {
category: 'Component Inputs',
type: {
summary: 'string'
}
}
},
formatTooltip: {
description: 'Custom tooltip formatter function.',
control: { disable: true },
table: {
category: 'Component Inputs',
type: {
summary: 'Function'
}
}
},
id: {
description: 'Column identifier.',
control: { disable: true },
table: {
category: 'Component Inputs',
type: {
summary: 'string'
},
defaultValue: {
summary: ''
}
}
},
isHidden: {
description: 'Hides columns',
control: { type: 'boolean' },
table: {
category: 'Component Inputs',
type: {
summary: 'boolean'
},
defaultValue: {
summary: 'false'
}
}
},
key: {
description: 'Data source key. Can be either a column/property key like title or a property path like `createdBy.name`.',
control: { type: 'text', disable: false },
table: {
category: 'Component Inputs',
type: {
summary: 'string'
}
}
},
sortable: {
description: 'Toggles ability to sort by this column, for example by clicking the column header.',
control: { type: 'boolean' },
table: {
category: 'Component Inputs',
type: {
summary: 'boolean'
},
defaultValue: {
summary: 'true'
}
}
},
sortingKey: {
description: 'When using server side sorting the column used by the api call where the sorting will be performed',
control: { disable: true },
table: {
category: 'Component Inputs',
type: {
summary: 'string'
}
}
},
srTitle: {
description: 'Title to be used for screen readers.',
control: { type: 'text' },
table: {
category: 'Component Inputs',
type: {
summary: 'string'
},
defaultValue: {
summary: ''
}
}
},
title: {
description:
'Display title of the column, typically used for column headers. You can use the i18n resource key to get it translated automatically.',
control: { type: 'text' },
table: {
category: 'Component Inputs',
type: {
summary: 'string'
},
defaultValue: {
summary: ''
}
}
},
type: {
description:
'Value type for the column. Possible settings are: `text`, `icon`, `image`, `date`, `fileSize`, `location`, `boolean`, `amount`, `number` and `json`.',
control: { type: 'select', disable: false },
options: ['text', 'icon', 'image', 'date', 'fileSize', 'location', 'boolean', 'amount', 'number', 'json'],
table: {
category: 'Component Inputs',
type: {
summary: 'DataColumnType'
},
defaultValue: {
summary: 'text'
}
}
},
currencyConfig: {
description: `The currencyConfig input allows you to customize the formatting and display of currency values within the component.`,
control: { type: 'object', disable: true },
table: {
category: 'Component Inputs',
type: {
summary: 'CurrencyConfig'
},
defaultValue: {
summary: `{ code: 'USD', display: 'symbol' }`
}
}
},
decimalConfig: {
description: `The decimalConfig input allows you to customize the formatting and display of decimal values within the component.`,
control: { type: 'object', disable: true },
table: {
category: 'Component Inputs',
type: {
summary: 'DecimalConfig'
},
defaultValue: {
summary: `{}`
}
}
},
dateConfig: {
description: `The dateConfig input allows you to configure date formatting and localization for a component.`,
control: { type: 'object', disable: true },
table: {
category: 'Component Inputs',
type: {
summary: 'DateConfig'
},
defaultValue: {
summary: `{ format: 'medium', tooltipFormat: 'medium' }`
}
}
},
rows: {
description: 'Provides rows for DataTable component',
control: { disable: false },
table: {
category: 'Component data',
type: {
summary: 'array'
}
}
}
},
args: {
copyContent: false,
cssClass: '',
customData: {},
draggable: false,
editable: false,
focus: true,
format: '',
formatTooltip: null,
id: '',
isHidden: false,
key: '',
sortable: true,
sortingKey: '',
srTitle: '',
title: '',
type: 'text',
currencyConfig: {
code: 'USD',
display: 'symbol',
digitsInfo: undefined,
locale: undefined
},
decimalConfig: {
digitsInfo: '2.4-5',
locale: undefined
},
dateConfig: {
format: 'medium',
tooltipFormat: 'medium',
locale: undefined
}
}
};
export default meta;
type Story = StoryObj<DataColumnComponent & { rows: any[] }>;
const formatCustomTooltip = (row: DataRow): string => (row ? 'This is ' + row.getValue('firstname') : null);
const render = (args: DataColumnComponent & { rows: DataRow[] }) => ({
props: args,
template: `
<adf-datatable [rows]="rows">
<data-columns>
<data-column
[key]="key"
[type]="type"
[title]="title"
[editable]="editable"
[sortable]="sortable"
[draggable]="draggable"
[copyContent]="copyContent"
[format]="format"
[isHidden]="isHidden"
[class]="cssClass"
[sr-title]="srTitle"
[currencyConfig]="currencyConfig"
[decimalConfig]="decimalConfig"
[dateConfig]="dateConfig"
[formatTooltip]="formatTooltip">
</data-column>
</data-columns>
</adf-datatable>
`
});
// Text Column
export const TextColumn: Story = {
render: render,
args: {
rows: mockData.textColumnRows,
key: 'firstname',
type: 'text',
title: 'Text Column'
}
};
// Text Column With Custom Tooltip
export const TextColumnWithCustomTooltip: Story = {
render: render,
argTypes: {
formatTooltip: { control: { disable: false } }
},
args: {
rows: mockData.textColumnRows,
key: 'firstname',
type: 'text',
title: 'Custom Tooltip Column',
formatTooltip: formatCustomTooltip
}
};
// Icon Column
export const IconColumn: Story = {
render: render,
argTypes: {
copyContent: { control: { disable: true } }
},
args: {
rows: mockData.iconColumnRows,
key: 'icon',
type: 'icon',
title: 'Icon Column'
}
};
// Image Column
export const ImageColumn: Story = {
render: render,
argTypes: {
copyContent: { control: { disable: true } }
},
args: {
rows: mockData.imageColumnRows,
key: 'image',
type: 'image',
title: 'Image Column'
}
};
// Date Column
export const DateColumn: Story = {
render: render,
argTypes: {
copyContent: { control: { disable: true } },
dateConfig: { control: { disable: false } }
},
args: {
rows: mockData.dateColumnRows,
key: 'createdOn',
type: 'date',
title: 'Date Column'
}
};
// Date Column Time Ago
export const DateColumnTimeAgo: Story = {
render: render,
argTypes: {
copyContent: { control: { disable: true } },
dateConfig: { control: { disable: false } }
},
args: {
rows: mockData.dateColumnTimeAgoRows,
key: 'modifiedOn',
type: 'date',
title: 'Date Column Time Ago',
dateConfig: { format: 'timeAgo' }
}
};
// File Size Column
export const FileSizeColumn: Story = {
render: render,
argTypes: {
copyContent: { control: { disable: true } }
},
args: {
rows: mockData.fileSizeColumnRows,
key: 'size',
type: 'fileSize',
title: 'File Size Column'
}
};
// Location Column
export const LocationColumn: Story = {
render: render,
argTypes: {
copyContent: { control: { disable: true } },
format: { control: { disable: false } },
sortable: { control: { disable: true } }
},
args: {
rows: mockData.locationColumnRows,
format: '/files',
key: 'path',
type: 'location',
title: 'Location Column'
}
};
// Boolean Column
export const BooleanColumn: Story = {
render: render,
argTypes: {
copyContent: { control: { disable: true } }
},
args: {
rows: mockData.booleanColumnRows,
key: 'bool',
type: 'boolean',
title: 'Boolean Column'
}
};
// Json Column
export const JsonColumn: Story = {
render: render,
argTypes: {
editable: { control: { disable: false } },
copyContent: { control: { disable: true } }
},
args: {
rows: mockData.jsonColumnRows,
key: 'rowInfo',
type: 'json',
title: 'JSON Column'
}
};
// Amount Column
export const AmountColumn: Story = {
render: render,
argTypes: {
copyContent: { control: { disable: true } },
currencyConfig: { control: { disable: false } }
},
args: {
rows: mockData.amountColumnRows,
key: 'price',
type: 'amount',
title: 'Amount Column'
}
};
// Number Column
export const NumberColumn: Story = {
render: render,
argTypes: {
decimalConfig: { control: { disable: false } },
copyContent: { control: { disable: true } }
},
args: {
rows: mockData.amountColumnRows,
key: 'price',
type: 'number',
title: 'Number Column'
}
};
@@ -0,0 +1,79 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, inject, OnInit, OnChanges, Input } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { EditJsonDialogComponent, EditJsonDialogSettings } from './edit-json.dialog';
import { MatButtonModule } from '@angular/material/button';
@Component({
selector: 'adf-edit-json-dialog-storybook',
template: `<button mat-raised-button (click)="openDialog()">Open dialog</button>`,
imports: [MatButtonModule]
})
export class EditJsonDialogStorybookComponent implements OnInit, OnChanges {
@Input()
title: string;
@Input()
editable: boolean;
@Input()
value: string;
private _settings: EditJsonDialogSettings;
set settings(newSettings: EditJsonDialogSettings) {
this._settings = {
title: newSettings.title,
editable: newSettings.editable,
value: JSON.stringify(newSettings.value, null, ' ')
};
}
private readonly dialog = inject(MatDialog);
ngOnInit() {
this.assignSettings();
}
ngOnChanges() {
this.assignSettings();
}
openDialog() {
this.dialog
.open(EditJsonDialogComponent, {
data: this._settings,
minWidth: `50%`
})
.afterClosed()
.subscribe((value: string) => {
if (value) {
this._settings.value = JSON.stringify(JSON.parse(value), null, ' ');
}
});
}
private assignSettings() {
this.settings = {
title: this.title,
editable: this.editable,
value: this.value
};
}
}
@@ -0,0 +1,100 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { applicationConfig, Meta, StoryObj, moduleMetadata } from '@storybook/angular';
import { EditJsonDialogStorybookComponent } from './edit-json.dialog.stories.component';
import { MatButtonModule } from '@angular/material/button';
import { EditJsonDialogComponent } from './edit-json.dialog';
import { provideStoryCore } from '../../stories/core-story.providers';
const jsonData = {
maxValue: 50,
minValue: 10,
values: [10, 15, 14, 27, 35, 23, 49, 38],
measurementId: 'm_10001',
researcherId: 's_10002'
};
const meta: Meta<EditJsonDialogStorybookComponent> = {
component: EditJsonDialogStorybookComponent,
title: 'Core/Dialog/Edit JSON Dialog',
decorators: [
moduleMetadata({
imports: [EditJsonDialogComponent, MatButtonModule]
}),
applicationConfig({
providers: [...provideStoryCore()]
})
],
argTypes: {
value: {
description: 'Displayed text',
control: {
type: 'object'
},
table: {
category: 'Provider settings',
type: {
summary: 'object'
}
}
},
editable: {
description: 'Defines if component is editable',
control: {
type: 'boolean'
},
table: {
category: 'Provider settings',
type: {
summary: 'boolean'
},
defaultValue: {
summary: 'false'
}
}
},
title: {
control: {
type: 'text'
},
table: {
category: 'Provider settings',
type: {
summary: 'string'
},
defaultValue: {
summary: 'JSON'
}
}
}
},
args: {
value: jsonData as unknown as string,
editable: false,
title: 'JSON Dialog Title'
}
};
export default meta;
type Story = StoryObj<EditJsonDialogStorybookComponent>;
export const EditJSONDialog: Story = {
render: (args) => ({
props: args
})
};
@@ -42,15 +42,15 @@
display: grid;
&-column-view {
@include flex.layout-bp(lt-md) {
display: flow;
}
display: flex;
margin-right: -1%;
width: 100%;
gap: 8px;
@include flex.layout-bp(lt-md) {
display: flow;
}
&-item {
width: 100%;
box-sizing: border-box;
@@ -0,0 +1,80 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { applicationConfig, Meta, moduleMetadata, StoryObj } from '@storybook/angular';
import { IconComponent } from './icon.component';
import { provideStoryCore } from '../stories/core-story.providers';
const meta: Meta<IconComponent> = {
component: IconComponent,
title: 'Core/Icon/Icon',
decorators: [
moduleMetadata({
imports: [IconComponent]
}),
applicationConfig({
providers: [...provideStoryCore()]
})
],
parameters: {
docs: {
description: {
component: `Provides a universal way of rendering registered and named icons.`
}
}
},
argTypes: {
color: {
control: 'radio',
options: ['primary', 'accent', 'warn', undefined],
description: 'icon color',
defaultValue: undefined,
table: {
type: { summary: 'ThemePalette' },
defaultValue: { summary: 'undefined' }
}
},
value: {
description: 'icon name',
table: {
type: { summary: 'string' },
defaultValue: { summary: 'settings' }
}
}
}
};
export default meta;
type Story = StoryObj<IconComponent>;
export const DefaultIcon: Story = {
render: (args) => ({
props: args
}),
args: {
value: ''
}
};
export const CustomIcon: Story = {
render: (args) => ({
props: args
}),
args: {
value: 'cloud_download'
}
};
@@ -0,0 +1,124 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { applicationConfig, Meta, StoryObj, moduleMetadata } from '@storybook/angular';
import { IdentityUserInfoComponent } from './identity-user-info.component';
import { provideStoryCore } from '../stories/core-story.providers';
const fakeIdentityUser = {
familyName: 'Identity',
givenName: 'John',
email: 'john.identity@gmail.com',
username: 'johnyIdentity99'
};
const meta: Meta<IdentityUserInfoComponent> = {
component: IdentityUserInfoComponent,
title: 'Core/Identity User Info/Identity User Info',
decorators: [
moduleMetadata({
imports: [IdentityUserInfoComponent]
}),
applicationConfig({
providers: [...provideStoryCore()]
})
],
argTypes: {
isLoggedIn: {
description: 'Determines if user is logged in',
control: 'boolean',
table: {
type: { summary: 'boolean' },
defaultValue: { summary: 'true' }
}
},
identityUser: {
description: 'Identity User Info',
control: 'object',
table: {
type: { summary: 'IdentityUserModel' }
}
},
menuPositionX: {
description: 'Material Angular menu horizontal position in regard to User Info',
control: 'radio',
options: ['before', 'after'],
table: {
type: { summary: 'MenuPositionX' },
defaultValue: { summary: 'after' }
}
},
menuPositionY: {
description: 'Material Angular menu vertical position in regard to User Info',
control: 'radio',
options: ['above', 'below'],
table: {
type: { summary: 'MenuPositionY' },
defaultValue: { summary: 'below' }
}
},
showName: {
description: 'Determines if name should be shown next to user avatar',
control: 'boolean',
table: {
type: { summary: 'boolean' },
defaultValue: { summary: 'true' }
}
},
namePosition: {
description: 'User name position in regard to avatar',
control: 'radio',
options: ['left', 'right'],
table: {
type: { summary: 'string' },
defaultValue: { summary: 'right' }
}
},
bpmBackgroundImage: {
description: 'Menu background banner image for APS users',
control: {
disable: true
},
table: {
type: {
summary: 'string'
},
defaultValue: {
summary: './assets/images/bpm-background.png'
}
}
}
},
args: {
identityUser: fakeIdentityUser,
isLoggedIn: true,
menuPositionX: 'after',
menuPositionY: 'below',
showName: true,
namePosition: 'right',
bpmBackgroundImage: './assets/images/bpm-background.png'
}
};
export default meta;
type Story = StoryObj<IdentityUserInfoComponent>;
export const LoginWithSSO: Story = {
render: (args) => ({
props: args
})
};
@@ -0,0 +1,334 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { applicationConfig, Meta, StoryObj, moduleMetadata } from '@storybook/angular';
import { InfoDrawerComponent, InfoDrawerTabComponent } from './info-drawer.component';
import { InfoDrawerButtonsDirective, InfoDrawerContentDirective, InfoDrawerTitleDirective } from './info-drawer-layout.component';
import { mockTabText, mockCardText } from './mock/info-drawer.mock';
import { provideStoryCore } from '../stories/core-story.providers';
import { MatIconModule } from '@angular/material/icon';
type InfoDrawerStoryArgs = InfoDrawerComponent & {
showSecondTab?: boolean;
showThirdTab?: boolean;
label1?: string;
label2?: string;
label3?: string;
icon1?: string;
icon2?: string;
icon3?: string;
tab1Text?: string;
tab2Text?: string;
tab3Text?: string;
cardText?: string;
};
const meta: Meta<InfoDrawerStoryArgs> = {
component: InfoDrawerComponent,
title: 'Core/Info Drawer/Info Drawer',
decorators: [
moduleMetadata({
imports: [
InfoDrawerTabComponent,
InfoDrawerComponent,
InfoDrawerTitleDirective,
InfoDrawerButtonsDirective,
InfoDrawerContentDirective,
MatIconModule
]
}),
applicationConfig({
providers: [...provideStoryCore()]
})
],
parameters: {
docs: {
description: {
component: `Displays a sidebar-style information panel in single layout or using tabs.`
}
}
},
argTypes: {
selectedIndex: {
control: 'select',
options: [0, 1, 2],
defaultValue: 0,
description: 'The selected index tab (Tab Layout only)',
table: {
type: { summary: 'number' },
defaultValue: { summary: '0' }
}
},
title: {
control: 'text',
description: 'The title of the info drawer',
defaultValue: null,
table: {
type: { summary: 'string' },
defaultValue: { summary: 'null' }
},
if: { arg: 'showHeader', truthy: true }
},
showHeader: {
control: 'boolean',
description: 'Visibility of the header',
defaultValue: true,
table: {
type: { summary: 'boolean' },
defaultValue: { summary: 'true' }
}
},
showSecondTab: {
control: 'boolean',
description: 'Visibility of the second tab (Tab Layout only)',
defaultValue: true,
table: {
type: { summary: 'boolean' },
defaultValue: { summary: 'true' }
}
},
showThirdTab: {
control: 'boolean',
description: 'Visibility of the third tab (Tab Layout only)',
defaultValue: true,
table: {
type: { summary: 'boolean' },
defaultValue: { summary: 'true' }
}
},
label1: {
control: 'text',
description: 'Label of the first tab (Tab Layout only)',
defaultValue: undefined,
table: {
type: { summary: 'string' },
defaultValue: { summary: 'undefined' },
category: 'Labels'
}
},
label2: {
control: 'text',
description: 'Label of the second tab (Tab Layout only)',
defaultValue: undefined,
table: {
type: { summary: 'string' },
defaultValue: { summary: 'undefined' },
category: 'Labels'
},
if: { arg: 'showSecondTab', truthy: true }
},
label3: {
control: 'text',
description: 'Label of the third tab (Tab Layout only)',
defaultValue: undefined,
table: {
type: { summary: 'string' },
defaultValue: { summary: 'undefined' },
category: 'Labels'
},
if: { arg: 'showThirdTab', truthy: true }
},
icon1: {
control: 'text',
description: 'Icon of the first tab (Tab Layout only)',
defaultValue: undefined,
table: {
type: { summary: 'string' },
defaultValue: { summary: 'undefined' },
category: 'Icons'
}
},
icon2: {
control: 'text',
description: 'Icon of the second tab (Tab Layout only)',
defaultValue: undefined,
table: {
type: { summary: 'string' },
defaultValue: { summary: 'undefined' },
category: 'Icons'
},
if: { arg: 'showSecondTab', truthy: true }
},
icon3: {
control: 'text',
description: 'Icon of the third tab (Tab Layout only)',
defaultValue: undefined,
table: {
type: { summary: 'string' },
defaultValue: { summary: 'undefined' },
category: 'Icons'
},
if: { arg: 'showThirdTab', truthy: true }
},
tab1Text: {
control: 'text',
description: 'Text content of the first tab (Tab Layout only)',
defaultValue: undefined,
table: {
type: { summary: 'string' },
defaultValue: { summary: 'undefined' },
category: 'Tab Content'
}
},
tab2Text: {
control: 'text',
description: 'Text content of the second tab (Tab Layout only)',
defaultValue: undefined,
table: {
type: { summary: 'string' },
defaultValue: { summary: 'undefined' },
category: 'Tab Content'
},
if: { arg: 'showSecondTab', truthy: true }
},
tab3Text: {
control: 'text',
description: 'Text content of the third tab (Tab Layout only)',
defaultValue: undefined,
table: {
type: { summary: 'string' },
defaultValue: { summary: 'undefined' },
category: 'Tab Content'
},
if: { arg: 'showThirdTab', truthy: true }
},
cardText: {
control: 'text',
description: 'The content of the single card (Single Layout only)',
defaultValue: undefined,
table: {
type: { summary: 'string' },
defaultValue: { summary: 'undefined' }
}
},
currentTab: {
action: 'currentTab',
description: 'Emitted when the currently active tab changes',
table: {
type: { summary: 'EventEmitter <number>' },
category: 'Actions'
}
}
},
args: {
selectedIndex: 0,
showHeader: true,
showSecondTab: true,
showThirdTab: true
}
};
export default meta;
type Story = StoryObj<InfoDrawerStoryArgs>;
export const TabLayoutWithTextLabels: Story = {
render: (args) => ({
props: args,
template: `<adf-info-drawer title="{{ title }}" [showHeader]="showHeader" (currentTab)="currentTab($event)" selectedIndex="{{ selectedIndex }}">
<div info-drawer-buttons>
<mat-icon>clear</mat-icon>
</div>
<adf-info-drawer-tab [label]="label1" [icon]="[icon1]">
<div class="info-drawer-tab-text">{{ tab1Text }}</div>
</adf-info-drawer-tab>
<adf-info-drawer-tab [label]="label2" [icon]="[icon2]" *ngIf="showSecondTab">
<div class="info-drawer-tab-text">{{ tab2Text }}</div>
</adf-info-drawer-tab>
<adf-info-drawer-tab [label]="label3" [icon]="[icon3]" *ngIf="showThirdTab">
<div class="info-drawer-tab-text">{{ tab3Text }}</div>
</adf-info-drawer-tab>
</adf-info-drawer>`
}),
args: {
title: 'Activities',
label1: 'Activity',
label2: 'Details',
label3: 'More Info',
tab1Text: `This is a variant of the Info Drawer Layout component that displays information in tabs. ${mockTabText}`,
tab2Text: mockTabText,
tab3Text: mockTabText
},
parameters: {
controls: { exclude: ['cardText'] }
}
};
export const TabLayoutWithIconLabels: Story = {
render: (args) => ({
props: args,
template: `<adf-info-drawer title="{{ title }}" [showHeader]="showHeader" (currentTab)="currentTab($event)" selectedIndex="{{ selectedIndex }}">
<div info-drawer-buttons>
<mat-icon>clear</mat-icon>
</div>
<adf-info-drawer-tab [label]="label1" [icon]="[icon1]">
<div class="info-drawer-tab-text">{{ tab1Text }}</div>
</adf-info-drawer-tab>
<adf-info-drawer-tab [label]="label2" [icon]="[icon2]" *ngIf="showSecondTab">
<div class="info-drawer-tab-text">{{ tab2Text }}</div>
</adf-info-drawer-tab>
<adf-info-drawer-tab [label]="label3" [icon]="[icon3]" *ngIf="showThirdTab">
<div class="info-drawer-tab-text">{{ tab3Text }}</div>
</adf-info-drawer-tab>
</adf-info-drawer>`
}),
args: {
title: 'Activities',
icon1: 'people',
icon2: 'android',
icon3: 'comment',
tab1Text: `This is a variant of the Info Drawer Layout component that displays information in tabs. ${mockTabText}`,
tab2Text: mockTabText,
tab3Text: mockTabText
},
parameters: {
controls: { exclude: ['cardText'] }
}
};
export const SingleLayout: Story = {
render: (args) => ({
props: args,
template: `<adf-info-drawer title="{{ title }}" [showHeader]="showHeader">
<div info-drawer-title>File info</div>
<div info-drawer-buttons>
<mat-icon>clear</mat-icon>
</div>
<div info-drawer-content>
<mat-card>
{{ cardText }}
</mat-card>
</div>
</adf-info-drawer>`
}),
args: {
title: 'Single Activities',
cardText: mockCardText,
showHeader: true,
showSecondTab: false,
showThirdTab: false
}
};
@@ -0,0 +1,34 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* eslint-disable @cspell/spellchecker */
export const mockTabText = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam urna odio, sagittis vel nulla vel, condimentum egestas dolor.
Interdum et malesuada fames ac ante ipsum primis in faucibus. Mauris eu hendrerit lectus. Aliquam et ex imperdiet, sodales tellus finibus, malesuada eros.
Vestibulum aliquet eros sed diam euismod tincidunt.
Pellentesque euismod, augue at blandit dapibus, ex nunc viverra nisl, non laoreet nibh odio in libero.
Quisque facilisis, dui luctus fringilla lacinia, dui enim accumsan diam, a vehicula mi nulla quis dolor.
Maecenas non neque sed nulla tincidunt vehicula.`;
export const mockCardText = `Suspendisse euismod egestas nisi, non ullamcorper orci scelerisque id. Vestibulum mollis ex imperdiet nisl viverra egestas.
Nunc commodo, mi elementum auctor bibendum, neque tortor justo, eget gravida eros.
Vestibulum nec dui ac ipsum posuere ullamcorper. Nullam ultrices eget tellus ut gravida. Aliquam ullamcorper tellus ac dui vehicula venenatis.
Maecenas ante ipsum, vestibulum sit amet fringilla a, fringilla quis leo.
Sed nisl nisi, lacinia ac ullamcorper non, tincidunt at massa. Sed at metus fermentum augue eleifend porta. Sed nec dui ut quam facilisis cursus at et eros.
Nulla quis diam vitae odio faucibus faucibus ac ac erat. Sed vehicula est eu congue pretium.
Donec quis nisi ligula. Donec pellentesque nibh nec scelerisque placerat. Nulla facilisi. Sed egestas nisi at risus iaculis faucibus. Nulla facilisi.
Aliquam ac tincidunt justo, sit amet aliquet libero.`;
@@ -0,0 +1,107 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { applicationConfig, componentWrapperDecorator, Meta, StoryObj, moduleMetadata } from '@storybook/angular';
import { LanguageMenuComponent } from './language-menu.component';
import { LanguageService } from './service/language.service';
import { LanguageServiceMock } from '../mock/language.service.mock';
import { provideStoryCore } from '../stories/core-story.providers';
import { MatMenuModule } from '@angular/material/menu';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
const meta: Meta<LanguageMenuComponent> = {
component: LanguageMenuComponent,
title: 'Core/Language Menu/Language Menu',
decorators: [
moduleMetadata({
imports: [LanguageMenuComponent, MatMenuModule, MatButtonModule, MatIconModule],
providers: [{ provide: LanguageService, useClass: LanguageServiceMock }]
}),
applicationConfig({
providers: [...provideStoryCore()]
})
],
parameters: {
docs: {
description: {
component: `Displays all the languages that are present in "app.config.json" and the default (EN).`
}
}
},
argTypes: {
changedLanguage: {
action: 'changedLanguage',
description: 'Emitted when the user clicks on one of the language buttons.',
table: {
category: 'Actions',
type: { summary: 'EventEmitter <LanguageItem>' }
}
}
}
};
export default meta;
type Story = StoryObj<LanguageMenuComponent>;
export const AsMainMenu: Story = {
render: (args) => ({
props: args
}),
decorators: [
componentWrapperDecorator(
(story) => `
<button mat-icon-button [matMenuTriggerFor]="langMenu">
<mat-icon>
language
</mat-icon>
</button>
<mat-menu #langMenu="matMenu">
${story}
</mat-menu>
`
)
]
};
export const AsNestedMenu: Story = {
render: (args) => ({
props: args
}),
decorators: [
componentWrapperDecorator(
(story) => `
<button mat-icon-button [matMenuTriggerFor]="profileMenu">
<mat-icon>
more_vert
</mat-icon>
</button>
<mat-menu #profileMenu="matMenu">
<button mat-menu-item [matMenuTriggerFor]="langMenu">
<mat-icon>
language
</mat-icon>
Language
</button>
</mat-menu>
<mat-menu #langMenu="matMenu">
${story}
</mat-menu>
`
)
]
};
@@ -0,0 +1,76 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { applicationConfig, componentWrapperDecorator, Meta, StoryObj, moduleMetadata } from '@storybook/angular';
import { LanguagePickerComponent } from './language-picker.component';
import { LanguageService } from './service/language.service';
import { LanguageServiceMock } from '../mock/language.service.mock';
import { provideStoryCore } from '../stories/core-story.providers';
import { MatMenuModule } from '@angular/material/menu';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
const meta: Meta<LanguagePickerComponent> = {
component: LanguagePickerComponent,
title: 'Core/Language Menu/Language Picker',
decorators: [
moduleMetadata({
imports: [LanguagePickerComponent, MatMenuModule, MatButtonModule, MatIconModule],
providers: [{ provide: LanguageService, useClass: LanguageServiceMock }]
}),
applicationConfig({
providers: [...provideStoryCore()]
})
],
argTypes: {
changedLanguage: {
action: 'changedLanguage',
description: 'Emitted when the user clicks on one of the language buttons.',
table: {
category: 'Actions',
type: { summary: 'EventEmitter <LanguageItem>' }
}
}
}
};
export default meta;
type Story = StoryObj<LanguagePickerComponent>;
export const Primary: Story = {
render: (args) => ({
props: args
})
};
export const AsNestedMenu: Story = {
render: (args) => ({
props: args
}),
decorators: [
componentWrapperDecorator(
(story) => `
<button mat-icon-button [matMenuTriggerFor]="menu">
<mat-icon>more_vert</mat-icon>
</button>
<mat-menu #menu="matMenu">
${story}
</mat-menu>
`
)
]
};
@@ -0,0 +1,142 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { applicationConfig, Meta, StoryObj, moduleMetadata } from '@storybook/angular';
import { HeaderLayoutComponent } from './header.component';
import { provideStoryCore } from '../../../stories/core-story.providers';
const meta: Meta<HeaderLayoutComponent> = {
component: HeaderLayoutComponent,
title: 'Core/Layout/Header',
decorators: [
moduleMetadata({
imports: [HeaderLayoutComponent]
}),
applicationConfig({
providers: [...provideStoryCore()]
})
],
parameters: {
docs: {
description: {
component: `This component displays a customizable header for Alfresco applications that can be reused.
Use the input properties to configure the left side (title, button) and the primary color of the header.
The right part of the header can contain other components which are transcluded in the header component.`
}
}
},
argTypes: {
color: {
control: 'radio',
options: ['primary', 'accent', 'warn', '#42f57e', undefined],
description: `Background color for the header.
It can be any hex color code or one of the Material theme colors: 'primary', 'accent' or 'warn'`,
table: {
type: { summary: 'ThemePalette' },
defaultValue: { summary: 'undefined' }
}
},
expandedSidenav: {
control: 'boolean',
description: 'Toggles the expanded state of the component',
defaultValue: true,
table: {
type: { summary: 'boolean' },
defaultValue: { summary: 'true' }
}
},
showSidenavToggle: {
control: 'boolean',
description: 'Toggles whether the sidenav button will be displayed in the header or not',
defaultValue: true,
table: {
type: { summary: 'boolean' },
defaultValue: { summary: 'true' }
}
},
logo: {
control: 'text',
description: 'Path to an image file for the application logo',
table: {
type: { summary: 'string' },
defaultValue: { summary: 'undefined' }
}
},
title: {
control: 'text',
description: 'Title of the application',
table: {
type: { summary: 'string' },
defaultValue: { summary: 'undefined' }
}
},
tooltip: {
control: 'text',
description: 'The tooltip text for the application logo',
table: {
type: { summary: 'string' },
defaultValue: { summary: 'undefined' }
}
},
position: {
control: 'radio',
options: ['start', 'end'],
description: `The side of the page that the drawer is attached to (can be 'start' or 'end')`,
defaultValue: 'start',
table: {
type: { summary: 'string' },
defaultValue: { summary: 'start' }
}
},
redirectUrl: {
control: 'text',
description: 'The router link for the application logo, when clicked',
defaultValue: '/',
table: {
type: { summary: 'string | any[]' },
defaultValue: { summary: '/' }
}
},
clicked: {
action: 'clicked',
description: 'Emitted when the sidenav button is clicked',
table: {
type: { summary: 'EventEmitter <boolean>' },
category: 'Actions'
}
}
},
args: {
expandedSidenav: true,
showSidenavToggle: true,
position: 'start',
redirectUrl: '/'
}
};
export default meta;
type Story = StoryObj<HeaderLayoutComponent>;
export const Header: Story = {
render: (args) => ({
props: args
}),
args: {
title: 'Hello from Header!',
tooltip: 'Default Tooltip text'
}
};
@@ -0,0 +1,82 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { applicationConfig, Meta, StoryObj, moduleMetadata } from '@storybook/angular';
import { SidebarActionMenuComponent } from './sidebar-action-menu.component';
import { provideStoryCore } from '../../../stories/core-story.providers';
const meta: Meta<SidebarActionMenuComponent> = {
component: SidebarActionMenuComponent,
title: 'Core/Layout/Sidebar Action Menu',
decorators: [
moduleMetadata({
imports: [SidebarActionMenuComponent]
}),
applicationConfig({
providers: [...provideStoryCore()]
})
],
parameters: {
docs: {
description: {
component: `Displays a sidebar-action menu information panel.`
}
}
},
argTypes: {
expanded: {
control: 'boolean',
description: 'Toggle the sidebar action menu on expand',
table: {
type: { summary: 'boolean' },
defaultValue: { summary: 'true' }
}
},
title: {
control: 'text',
description: 'The title of the sidebar action',
table: {
type: { summary: 'string' },
defaultValue: { summary: 'undefined' }
}
},
width: {
control: 'number',
description: 'Width in pixels for sidebar action menu options',
table: {
type: { summary: 'number' },
defaultValue: { summary: '272' }
}
}
},
args: {
expanded: true,
width: 272
}
};
export default meta;
type Story = StoryObj<SidebarActionMenuComponent>;
export const SidebarActionMenu: Story = {
render: (args) => ({
props: args
}),
args: {
title: 'Hello from Sidebar Action Menu!'
}
};
@@ -3,6 +3,10 @@
@use '../../../styles/mat-selectors' as ms;
.adf-sidenav-layout {
@include mixins.flex-column;
width: 100%;
&-full-space {
display: flex;
flex-direction: column;
@@ -13,10 +17,6 @@
width: 100%;
}
@include mixins.flex-column;
width: 100%;
.adf-layout__content {
flex: 1 1 auto;
}
@@ -0,0 +1,243 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { applicationConfig, Meta, StoryObj, moduleMetadata } from '@storybook/angular';
import { SidenavLayoutComponent } from './sidenav-layout.component';
import { MatListModule } from '@angular/material/list';
import { MatIconModule } from '@angular/material/icon';
import { provideStoryCore } from '../../../stories/core-story.providers';
type SidenavLayoutStoryArgs = SidenavLayoutComponent & {
title?: string;
color?: 'primary' | 'accent' | 'warn';
clicked?: any;
};
const meta: Meta<SidenavLayoutStoryArgs> = {
component: SidenavLayoutComponent,
title: 'Core/Layout/Sidenav Layout',
decorators: [
moduleMetadata({
imports: [SidenavLayoutComponent, MatIconModule, MatListModule]
}),
applicationConfig({
providers: [...provideStoryCore()]
})
],
parameters: {
docs: {
description: {
component: `Displays the standard three-region ADF application layout.`
}
}
},
argTypes: {
expandedSidenav: {
control: 'boolean',
description: 'Toggles the expand of navigation region',
defaultValue: true,
table: {
type: { summary: 'boolean' },
defaultValue: { summary: 'true' },
category: 'Navigation'
}
},
hideSidenav: {
control: 'boolean',
description: 'Toggles showing/hiding the navigation region',
defaultValue: false,
table: {
type: { summary: 'boolean' },
defaultValue: { summary: 'false' },
category: 'Navigation'
}
},
position: {
control: 'radio',
options: ['start', 'end'],
description: `The side of the page that the drawer is attached to (can be 'start' or 'end')`,
defaultValue: 'start',
table: {
type: { summary: 'string' },
defaultValue: { summary: 'start' },
category: 'Navigation'
}
},
sidenavMax: {
control: 'number',
description: 'Maximum size of the navigation region',
table: {
type: { summary: 'number' },
defaultValue: { summary: 'undefined' },
category: 'Navigation'
}
},
sidenavMin: {
control: 'number',
description: 'Minimum size of the navigation region',
table: {
type: { summary: 'number' },
defaultValue: { summary: 'undefined' },
category: 'Navigation'
}
},
stepOver: {
control: 'number',
description: 'Screen size at which display switches from small screen to large screen configuration',
table: {
type: { summary: 'number' },
defaultValue: { summary: 'undefined' }
}
},
title: {
control: 'text',
description: 'Title of the application',
table: {
type: { summary: 'string' },
defaultValue: { summary: 'undefined' },
category: 'Header'
}
},
color: {
control: 'radio',
options: ['primary', 'accent', 'warn', undefined],
description: `Background color for the header.
It can be any hex color code or one of the Material theme colors: 'primary', 'accent' or 'warn'`,
table: {
type: { summary: 'ThemePalette' },
defaultValue: { summary: 'undefined' },
category: 'Header'
}
},
clicked: {
action: 'expanded',
description: 'Emitted when the menu toggle and the collapsed/expanded state of the sideNav changes',
table: {
type: { summary: 'EventEmitter <boolean>' },
category: 'Actions'
}
}
},
args: {
expandedSidenav: true,
hideSidenav: false,
position: 'start'
}
};
export default meta;
type Story = StoryObj<SidenavLayoutStoryArgs>;
export const SidenavLayout: Story = {
render: (args) => ({
props: args,
template: `
<adf-sidenav-layout
[sidenavMin]="sidenavMin"
[sidenavMax]="sidenavMax"
[stepOver]="stepOver"
[position]="position"
[hideSidenav]="hideSidenav"
[expandedSidenav]="expandedSidenav"
>
<div class="adf-sidenav-layout-full-space">
<adf-sidenav-layout-header>
<ng-template>
<adf-layout-header [title]="title" [color]="color"></adf-layout-header>
</ng-template>
</adf-sidenav-layout-header>
<adf-sidenav-layout-navigation>
<ng-template>
<mat-nav-list class="app-sidenav-linklist">
<mat-list-item class="app-sidenav-link">
<mat-icon matListItemIcon>home</mat-icon>
<span matLine>Home</span>
</mat-list-item>
<mat-list-item class="app-sidenav-link">
<mat-icon matListItemIcon>device_hub</mat-icon>
<span matLine>Content Processes</span>
</mat-list-item>
<mat-list-item class="app-sidenav-link">
<mat-icon matListItemIcon>folder_open</mat-icon>
<span matLine>Files</span>
</mat-list-item>
<mat-list-item class="app-sidenav-link">
<mat-icon matListItemIcon>rowing</mat-icon>
<span matLine>Quick Search</span>
</mat-list-item>
<mat-list-item class="app-sidenav-link">
<mat-icon matListItemIcon>cloud</mat-icon>
<span matLine>Cloud</span>
</mat-list-item>
<mat-list-item class="app-sidenav-link">
<mat-icon matListItemIcon>settings</mat-icon>
<span matLine>Settings</span>
</mat-list-item>
<mat-list-item adf-logout class="app-sidenav-link" data-automation-id="Logout">
<mat-icon matListItemIcon>exit_to_app</mat-icon>
<span matLine>Logout</span>
</mat-list-item>
</mat-nav-list>
</ng-template>
</adf-sidenav-layout-navigation>
<adf-sidenav-layout-content>
<ng-template>
<div class="fake-router-outlet">
Thanks to transclusion you can put anything you want inside header, sidenav and this (content) sections.
<a href="https://github.com/Alfresco/alfresco-ng2-components/blob/develop/docs/core/components/header.component.md"
target="_blank">ADF Layout Header component</a> is located in header section. In navigation, there is
<a href="https://material.angular.io/components/list/overview#navigation-lists"
target="_blank">Angular Material Navigation list</a> where items can contain routes to ADF components which then they will be rendered here, in content section.
<br/><br/>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.
<br/><br/>
Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt.
Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit.
Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur?
Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?
At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident,
similique sunt in culpa qui officia deserunt mollitia animi, id est laborum et dolorum fuga.
Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus id quod maxime placeat facere possimus,
omnis voluptas assumenda est, omnis dolor repellendus.
Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates repudiandae sint et molestiae non recusandae.
Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat.
</div>
</ng-template>
</adf-sidenav-layout-content>
</div>
</adf-sidenav-layout>`
}),
args: {
sidenavMin: 85,
sidenavMax: 250,
stepOver: 600,
position: 'start',
title: 'Hello from Sidenav Layout!'
}
};
@@ -0,0 +1,183 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { applicationConfig, Meta, StoryObj, moduleMetadata } from '@storybook/angular';
import { LoginComponent } from './login.component';
import { provideStoryCore } from '../../../stories/core-story.providers';
import { NoopAuthModule } from '@alfresco/adf-core';
type LoginStoryArgs = LoginComponent & {
correct?: any;
corsError?: any;
csrfError?: any;
ecmAccessError?: any;
};
const meta: Meta<LoginStoryArgs> = {
component: LoginComponent,
title: 'Core/Login/Login',
decorators: [
moduleMetadata({
imports: [LoginComponent, NoopAuthModule]
}),
applicationConfig({
providers: [...provideStoryCore()]
})
],
parameters: {
docs: {
description: {
component: `Authenticates to Alfresco Content Services and Alfresco Process Services.`
}
}
},
argTypes: {
correct: {
name: 'To test correct functionality:',
description: 'Use `fake-username` and `fake-password`.',
table: { category: 'Storybook Info' }
},
corsError: {
name: 'To test CORS error:',
description: 'Use `fake-username-CORS-error` and `fake-password`.',
table: { category: 'Storybook Info' }
},
csrfError: {
name: 'To test CSRF error:',
description: 'Use `fake-username-CSRF-error` and `fake-password`.',
table: { category: 'Storybook Info' }
},
ecmAccessError: {
name: 'To test ECM access error:',
description: 'Use `fake-username-ECM-access-error` and `fake-password`.',
table: { category: 'Storybook Info' }
},
showRememberMe: {
control: 'boolean',
description:
'Should the `Remember me` checkbox be shown? When selected, this option will remember the logged-in user after the browser is closed to avoid logging in repeatedly.',
table: {
type: { summary: 'boolean' },
defaultValue: { summary: 'true' }
}
},
showLoginActions: {
control: 'boolean',
description: 'Should the extra actions (`Need Help`, `Register`, etc) be shown?',
defaultValue: true,
table: {
type: { summary: 'boolean' },
defaultValue: { summary: 'true' }
}
},
needHelpLink: {
control: 'text',
description: 'Sets the URL of the NEED HELP link in the footer.',
defaultValue: '/?path=/story/core-login-login--login',
table: {
type: { summary: 'string' },
defaultValue: { summary: '/?path=/story/core-login-login--login' }
}
},
registerLink: {
control: 'text',
description: 'Sets the URL of the REGISTER link in the footer.',
defaultValue: '/?path=/story/core-login-login--login',
table: {
type: { summary: 'string' },
defaultValue: { summary: '/?path=/story/core-login-login--login' }
}
},
logoImageUrl: {
control: 'text',
description: 'Path to a custom logo image.',
defaultValue: './assets/images/alfresco-logo.svg',
table: {
type: { summary: 'string' },
defaultValue: { summary: './assets/images/alfresco-logo.svg' }
}
},
backgroundImageUrl: {
control: 'text',
description: 'Path to a custom background image.',
defaultValue: './assets/images/background.svg',
table: {
type: { summary: 'string' },
defaultValue: { summary: './assets/images/background.svg' }
}
},
copyrightText: {
control: 'text',
description: 'The copyright text below the login box.',
defaultValue: '\u00A9 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.',
table: {
type: { summary: 'string' },
defaultValue: { summary: '\u00A9 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.' }
}
},
fieldsValidation: {
control: 'object',
description: 'Custom validation rules for the login form.',
table: {
type: { summary: 'any' },
defaultValue: { summary: 'undefined' }
}
},
successRoute: {
control: 'text',
description: 'Route to redirect to on successful login.',
defaultValue: '.',
table: {
type: { summary: 'string' },
defaultValue: { summary: '.' }
}
},
success: {
action: 'success',
description: 'Emitted when the login is successful.',
table: {
type: { summary: 'EventEmitter <LoginSuccessEvent>' },
category: 'Actions'
}
},
error: {
action: 'error',
description: 'Emitted when the login fails.',
table: {
type: { summary: 'EventEmitter <LoginErrorEvent>' },
category: 'Actions'
}
},
executeSubmit: {
action: 'executeSubmit',
description: 'Emitted when the login form is submitted.',
table: {
type: { summary: 'EventEmitter <LoginSubmitEvent>' },
category: 'Actions'
}
}
}
};
export default meta;
type Story = StoryObj<LoginStoryArgs>;
export const Login: Story = {
render: (args) => ({
props: args
})
};
+63
View File
@@ -0,0 +1,63 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { mockPathInfos } from '../datatable/components/mocks/datatable.mock';
export const textColumnRows = [{ firstname: 'John' }, { firstname: 'Henry' }, { firstname: 'David' }, { firstname: 'Thomas' }];
export const dateColumnRows = [
{ createdOn: new Date(2016, 6, 1, 11, 8, 4) },
{ createdOn: new Date(2018, 4, 3, 12, 8, 4) },
{ createdOn: new Date(2021, 2, 3, 9, 8, 4) }
];
const aMinuteInMilliseconds = 60 * 1000;
const anHourInMilliseconds = 60 * aMinuteInMilliseconds;
const aDayInMilliseconds = 24 * anHourInMilliseconds;
export const dateColumnTimeAgoRows = [
{ modifiedOn: new Date() },
{ modifiedOn: new Date(Date.now() - 44 * aMinuteInMilliseconds) },
{ modifiedOn: new Date(Date.now() - 45 * aMinuteInMilliseconds) },
{ modifiedOn: new Date(Date.now() - 23 * anHourInMilliseconds) },
{ modifiedOn: new Date(Date.now() - 7 * aDayInMilliseconds) },
{ modifiedOn: new Date(Date.now() - 8 * aDayInMilliseconds) }
];
export const locationColumnRows = [
{
path: mockPathInfos[0]
},
{
path: mockPathInfos[1]
},
{
path: mockPathInfos[2]
}
];
export const booleanColumnRows = [{ bool: 'true' }, { bool: 'false' }, { bool: true }, { bool: false }];
export const iconColumnRows = [{ icon: 'alarm' }, { icon: 'folder_open' }, { icon: 'accessibility' }];
export const imageColumnRows = [{ image: 'material-icons://image' }, { image: 'material-icons://image' }, { image: 'material-icons://image' }];
export const fileSizeColumnRows = [{ size: 12313 }, { size: 23 }, { size: 42421412421 }];
export const amountColumnRows = [{ price: 1230 }, { price: 422.55 }, { price: 50000.7855332 }, { price: 0.123 }, { price: -2022.3321 }];
export const jsonColumnRows = [{ rowInfo: { id: 1, name: 'row1' } }, { rowInfo: { id: 2, name: 'row2' } }, { rowInfo: { id: 3, name: 'row3' } }];
@@ -0,0 +1,54 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { LanguageServiceInterface } from '../language-menu/service/language.service.interface';
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { LanguageItem } from '../common/services/language-item.interface';
@Injectable()
export class LanguageServiceMock implements LanguageServiceInterface {
private languages = new BehaviorSubject<LanguageItem[]>([
{ key: 'de', label: 'Deutsch' },
{ key: 'en', label: 'English' },
{ key: 'es', label: 'Español' },
{ key: 'fr', label: 'Français' },
{ key: 'it', label: 'Italiano' },
{ key: 'ja', label: '日本語' },
{ key: 'nb', label: 'Bokmål' },
{ key: 'nl', label: 'Nederlands' },
{ key: 'pt-BR', label: 'Português (Brasil)' },
{ key: 'ru', label: 'Русский' },
{ key: 'zh-CN', label: '中文简体' },
{ key: 'cs', label: 'Čeština' },
{ key: 'da', label: 'Dansk' },
{ key: 'fi', label: 'Suomi' },
{ key: 'pl', label: 'Polski' },
{ key: 'sv', label: 'Svenska' },
{ key: 'ar', label: 'العربية', direction: 'rtl' }
]);
languages$ = this.languages.asObservable();
changeLanguage(_language: LanguageItem): void {}
setLanguages(items: LanguageItem[]): void {
if (items?.length > 0) {
this.languages.next(items);
}
}
}
@@ -0,0 +1,36 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component } from '@angular/core';
import { NotificationService } from '../services/notification.service';
import { MatButtonModule } from '@angular/material/button';
@Component({
selector: 'adf-add-notification-storybook',
imports: [MatButtonModule],
template: ` <button mat-raised-button (click)="showInfo()">Add Notification</button>`
})
export class AddNotificationStorybookComponent {
infoCounter: number = 1;
constructor(private notificationService: NotificationService) {}
showInfo() {
this.notificationService.showInfo(`Example notification ${this.infoCounter}`);
this.infoCounter++;
}
}
@@ -0,0 +1,93 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { applicationConfig, Meta, StoryObj, moduleMetadata } from '@storybook/angular';
import { NotificationHistoryComponent } from './notification-history.component';
import { AddNotificationStorybookComponent } from './add-notification-component.mock';
import { provideStoryCore } from '../../stories/core-story.providers';
const meta: Meta<NotificationHistoryComponent> = {
component: NotificationHistoryComponent,
title: 'Core/Notification History/Notification History',
decorators: [
moduleMetadata({
imports: [NotificationHistoryComponent, AddNotificationStorybookComponent]
}),
applicationConfig({
providers: [...provideStoryCore()]
})
],
parameters: {
docs: {
description: {
component: `Lists notifications received in the current session. The notifications disappear from the list after refresh.`
}
}
},
argTypes: {
menuPositionX: {
control: 'inline-radio',
options: ['before', 'after'],
description: 'Custom choice for opening the menu at the bottom.',
table: {
type: { summary: 'MenuPositionX' },
defaultValue: { summary: 'after' }
}
},
menuPositionY: {
control: 'inline-radio',
options: ['below', 'above'],
description: 'Custom choice for opening the menu at the bottom.',
table: {
type: { summary: 'MenuPositionY' },
defaultValue: { summary: 'below' }
}
},
maxNotifications: {
control: 'number',
description: 'Maximum number of notifications to display. The rest will remain hidden until load more is clicked.',
table: {
type: { summary: 'number' },
defaultValue: { summary: '5' }
}
}
},
args: {
menuPositionX: 'after',
menuPositionY: 'below',
maxNotifications: 5
}
};
export default meta;
type Story = StoryObj<NotificationHistoryComponent>;
export const NotificationHistory: Story = {
render: (args) => ({
props: args,
template: `
<div style="display:flex;flex-direction:column;align-items:center;">
<adf-notification-history
[menuPositionX]=menuPositionX
[menuPositionY]=menuPositionY
[maxNotifications]=maxNotifications>
</adf-notification-history>
<adf-add-notification-storybook>
</adf-add-notification-storybook>
</div>`
})
};
@@ -0,0 +1,99 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { applicationConfig, Meta, StoryObj, moduleMetadata } from '@storybook/angular';
import { PaginationComponent } from './pagination.component';
import { provideStoryCore } from '../stories/core-story.providers';
const meta: Meta<PaginationComponent> = {
component: PaginationComponent,
title: 'Core/Pagination/Pagination',
decorators: [
moduleMetadata({
imports: [PaginationComponent]
}),
applicationConfig({
providers: [...provideStoryCore()]
})
],
argTypes: {
target: {
control: 'object',
description: 'Component that provides custom pagination support.',
table: { type: { summary: 'PaginatedComponent' } }
},
supportedPageSizes: {
control: 'object',
description: 'An array of page sizes.',
table: { type: { summary: 'number[]' } }
},
pagination: {
control: 'object',
description: 'Pagination object.',
table: {
type: { summary: 'PaginationModel' },
defaultValue: {
summary: 'PaginationModel',
detail:
'{\n skipCount: 0 /* How many entities exist in the collection before those included in this list? */,' +
'\n maxItems: 25 /* The value of the maxItems parameter used to generate this list. The default value is 100. */,' +
'\n totalItems: 0 /* An integer describing the total number of entities in the collection. */,' +
'\n count: 0, /* The number of objects in the entries array. */' +
'\n hasMoreItems: false /* Are there more entities in the collection beyond those in this response? */\n}'
}
}
},
change: {
action: 'change',
description: 'Emitted when pagination changes in any way.',
table: { category: 'Actions' }
},
changePageNumber: {
action: 'changePageNumber',
description: 'Emitted when the page number changes.',
table: { category: 'Actions' }
},
changePageSize: {
action: 'changePageSize',
description: 'Emitted when the page size changes.',
table: { category: 'Actions' }
},
nextPage: {
action: 'nextPage',
description: 'Emitted when the next page is requested.',
table: { category: 'Actions' }
},
prevPage: {
action: 'prevPage',
description: 'Emitted when the previous page is requested.',
table: { category: 'Actions' }
}
},
args: {
supportedPageSizes: [5, 10, 15, 20],
pagination: { skipCount: 0, maxItems: 25, totalItems: 100, count: 100, hasMoreItems: false }
}
};
export default meta;
type Story = StoryObj<PaginationComponent>;
export const Pagination: Story = {
render: (args) => ({
props: args
})
};
@@ -0,0 +1,108 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { applicationConfig, Meta, StoryObj, moduleMetadata } from '@storybook/angular';
import { SortingPickerComponent } from './sorting-picker.component';
import { provideStoryCore } from '../stories/core-story.providers';
const initialSortingTypes: Array<{ key: string; label: string }> = [
{ key: 'sortByFirstName', label: 'First Name' },
{ key: 'sortByLastName', label: 'Last Name' },
{ key: 'sortByBirthDate', label: 'Birth Date' }
];
const initialOptionKeys = [...initialSortingTypes.map((type) => type.key.toString())];
const meta: Meta<SortingPickerComponent> = {
component: SortingPickerComponent,
title: 'Core/Sorting Picker/Sorting Picker',
decorators: [
moduleMetadata({
imports: [SortingPickerComponent]
}),
applicationConfig({
providers: [...provideStoryCore()]
})
],
parameters: {
docs: {
description: {
component: `The picker shows the user a menu of sorting options (which could be data columns to sort on alphabetical vs numerical search, etc)
and the choice of ascending vs descending sort order.
Note that picker only implements the menu, so you are responsible for implementing the sorting options yourself.`
}
}
},
argTypes: {
selected: {
control: 'select',
options: initialOptionKeys,
description: 'Currently selected option key',
table: {
type: { summary: 'string' },
defaultValue: { summary: 'undefined' }
}
},
ascending: {
control: 'boolean',
description: 'Current sorting direction',
table: {
type: { summary: 'boolean' },
defaultValue: { summary: 'true' }
}
},
options: {
description: 'Available sorting options',
table: {
type: { summary: 'Array<{key: string; label: string}>' },
defaultValue: { summary: '[]' }
}
},
valueChange: {
action: 'valueChange',
description: 'Raised each time sorting key gets changed',
table: {
type: { summary: 'EventEmitter <string>' },
category: 'Actions'
}
},
sortingChange: {
action: 'sortingChange',
description: 'Raised each time direction gets changed',
table: {
type: { summary: 'EventEmitter <boolean>' },
category: 'Actions'
}
}
},
args: {
ascending: true,
options: []
}
};
export default meta;
type Story = StoryObj<SortingPickerComponent>;
export const SortingPicker: Story = {
render: (args) => ({
props: args
}),
args: {
options: initialSortingTypes
}
};
@@ -0,0 +1,49 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Provider, EnvironmentProviders, provideAppInitializer, inject } from '@angular/core';
import { provideAnimations } from '@angular/platform-browser/animations';
import { provideCoreAuthTesting } from '../testing/noop-auth.module';
import { provideAppConfig } from '../app-config/provide-app-config';
import { AppConfigService } from '../app-config/app-config.service';
import { provideI18N } from '../translation';
import { provideRouter, withHashLocation } from '@angular/router';
/**
* Provides the core providers for the storybook.
*
* @returns An array of providers for the core module.
*/
export function provideStoryCore(): (Provider | EnvironmentProviders)[] {
return [
provideAppConfig(),
provideI18N({
assets: [
['adf-core', 'assets/adf-core'],
['adf-process-services', 'assets/adf-process-services'],
['adf-process-services-cloud', 'assets/adf-process-services-cloud']
]
}),
provideAnimations(),
provideCoreAuthTesting(),
provideAppInitializer(() => {
const appConfig = inject(AppConfigService);
appConfig.config = { ...appConfig.config, locale: 'en' };
}),
provideRouter([], withHashLocation())
];
}
+18
View File
@@ -0,0 +1,18 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from './core-story.providers';
-3
View File
@@ -64,19 +64,16 @@ $overlapping-lt: (
@if map.has-key($breakpoints, $bp) {
$min: map.get(map.get($breakpoints, $bp), begin);
$max: map.get(map.get($breakpoints, $bp), end);
@media (min-width: $min) and (max-width: $max) {
@content;
}
} @else if map.has-key($overlapping-gt, $bp) {
$min: map.get($overlapping-gt, $bp);
@media (min-width: $min) {
@content;
}
} @else if map.has-key($overlapping-lt, $bp) {
$max: map.get($overlapping-lt, $bp);
@media (max-width: $max) {
@content;
}
@@ -0,0 +1,104 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { applicationConfig, Meta, StoryObj, moduleMetadata } from '@storybook/angular';
import { EmptyContentComponent } from './empty-content.component';
import { provideStoryCore } from '../../stories/core-story.providers';
type EmptyContentStoryArgs = EmptyContentComponent & {
anyContentProjection?: boolean;
};
const meta: Meta<EmptyContentStoryArgs> = {
component: EmptyContentComponent,
title: 'Core/Template/Empty Content',
decorators: [
moduleMetadata({
imports: [EmptyContentComponent]
}),
applicationConfig({
providers: [...provideStoryCore()]
})
],
parameters: {
docs: {
description: {
component: `Provides a generic "Empty Content" placeholder for components.`
}
}
},
argTypes: {
icon: {
control: 'text',
description: 'Material Icon to use.',
table: {
type: { summary: 'string' },
defaultValue: { summary: 'cake' }
}
},
title: {
control: 'text',
description: 'String or Resource Key for the title.',
table: {
type: { summary: 'string' },
defaultValue: { summary: 'title' }
}
},
subtitle: {
control: 'text',
description: 'String or Resource Key for the subtitle.',
table: {
type: { summary: 'string' },
defaultValue: { summary: 'subtitle' }
}
},
anyContentProjection: {
name: 'with any component / selector',
control: 'boolean',
description: 'Showcase content projection with any component / selector',
table: {
category: 'Content Projection',
type: {
summary: 'code',
detail: '<div style="color:red">\n projected content\n</div>'
},
defaultValue: { summary: 'false' }
}
}
},
args: {
icon: 'cake',
title: 'title',
subtitle: 'subtitle',
anyContentProjection: false
}
};
export default meta;
type Story = StoryObj<EmptyContentStoryArgs>;
export const EmptyContent: Story = {
render: (args: EmptyContentComponent & { anyContentProjection: boolean }) => ({
props: args,
template: `
<adf-empty-content icon="${args.icon}" title="${args.title}" subtitle="${args.subtitle}">
<div *ngIf="${args.anyContentProjection}" style="color:red">
projected content
</div>
</adf-empty-content>`
})
};
@@ -0,0 +1,89 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { applicationConfig, Meta, StoryObj, moduleMetadata } from '@storybook/angular';
import { ErrorContentComponent } from './error-content.component';
import { ActivatedRoute } from '@angular/router';
import { of } from 'rxjs';
import { provideStoryCore } from '../../stories/core-story.providers';
type ErrorContentStoryArgs = ErrorContentComponent & {
errorContentActions?: boolean;
};
const meta: Meta<ErrorContentStoryArgs> = {
component: ErrorContentComponent,
title: 'Core/Template/Error Content',
decorators: [
moduleMetadata({
imports: [ErrorContentComponent],
providers: [{ provide: ActivatedRoute, useValue: { params: of({}) } }]
}),
applicationConfig({
providers: [...provideStoryCore()]
})
],
parameters: {
docs: {
description: {
component: `Displays information about a specific error.`
}
}
},
argTypes: {
errorCode: {
control: 'text',
description: 'Error code associated with this error.',
table: {
type: { summary: 'string' },
defaultValue: { summary: 'UNKNOWN' }
}
},
errorContentActions: {
name: 'with adf-error-content-actions selector',
control: 'boolean',
description: 'Showcase content projection with <span style="color:red">adf-error-content-actions</span> selector',
table: {
category: 'Content Projection',
type: {
summary: 'code',
detail: '<div adf-error-content-actions>\n <button>MyAction</button>\n</div>'
},
defaultValue: { summary: 'false' }
}
}
},
args: {
errorCode: 'UNKNOWN',
errorContentActions: false
}
};
export default meta;
type Story = StoryObj<ErrorContentStoryArgs>;
export const ErrorContent: Story = {
render: (args: ErrorContentComponent & { errorContentActions: boolean }) => ({
props: args,
template: `
<adf-error-content errorCode="${args.errorCode}">
<div adf-error-content-actions *ngIf="${args.errorContentActions}">
<button mat-raised-button type="button">MyAction</button>
</div>
</adf-error-content>`
})
};
@@ -0,0 +1,122 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { applicationConfig, Meta, StoryObj, moduleMetadata } from '@storybook/angular';
import { ToolbarComponent } from './toolbar.component';
import { ToolbarDividerComponent } from './toolbar-divider.component';
import { ToolbarTitleComponent } from './toolbar-title.component';
import { provideStoryCore } from '../stories/core-story.providers';
type ToolbarStoryArgs = ToolbarComponent & {
toolbarTitle?: boolean;
toolbarDivider?: boolean;
anyContentProjection?: boolean;
};
const meta: Meta<ToolbarStoryArgs> = {
component: ToolbarComponent,
title: 'Core/Toolbar/Toolbar',
decorators: [
moduleMetadata({
imports: [ToolbarComponent, ToolbarTitleComponent, ToolbarDividerComponent]
}),
applicationConfig({
providers: [...provideStoryCore()]
})
],
argTypes: {
color: {
control: 'radio',
options: ['primary', 'accent', 'warn', undefined],
description: 'Toolbar color.',
table: {
type: { summary: 'ThemePalette' },
defaultValue: { summary: 'undefined' }
}
},
title: {
control: 'text',
description: 'Toolbar title.',
table: {
type: { summary: 'string' },
defaultValue: { summary: '' }
}
},
toolbarTitle: {
name: 'with adf-toolbar-title component',
control: 'boolean',
description: 'Showcase content projection with <span style="color:red">adf-toolbar-title</span> component',
table: {
category: 'Content Projection',
type: {
summary: 'code',
detail: '<adf-toolbar-title>Projected Title</adf-toolbar-title>'
},
defaultValue: { summary: 'false' }
}
},
toolbarDivider: {
name: 'with adf-toolbar-divider component',
control: 'boolean',
description: 'Showcase content projection with <span style="color:red">adf-toolbar-divider</span> component',
table: {
category: 'Content Projection',
type: {
summary: 'code',
detail: 'left<adf-toolbar-divider></adf-toolbar-divider>right'
},
defaultValue: { summary: 'false' }
}
},
anyContentProjection: {
name: 'with any component / selector',
control: 'boolean',
description: 'Showcase content projection with any component / selector',
table: {
category: 'Content Projection',
type: {
summary: 'code',
detail: '<span style="color:red">projected content</span>'
},
defaultValue: { summary: 'false' }
}
}
},
args: {
title: '',
toolbarTitle: false,
toolbarDivider: false,
anyContentProjection: false
}
};
export default meta;
type Story = StoryObj<ToolbarStoryArgs>;
export const Toolbar: Story = {
render: (args: ToolbarComponent & { anyContentProjection: boolean } & { toolbarDivider: boolean } & { toolbarTitle: boolean }) => ({
props: args,
template: `
<adf-toolbar color="${args.color}" title="${args.title}">
<ng-container *ngIf="${args.toolbarTitle}"><adf-toolbar-title>Projected Title</adf-toolbar-title></ng-container>
<ng-container *ngIf="${args.anyContentProjection}">
<span style="color:red">projected content</span>
</ng-container>
<ng-container *ngIf="${args.toolbarDivider}">left<adf-toolbar-divider></adf-toolbar-divider>right</ng-container>
</adf-toolbar>`
})
};
+1
View File
@@ -52,6 +52,7 @@ export * from './lib/models/index';
export * from './lib/events/index';
export * from './lib/mock/index';
export * from './lib/testing';
export * from './lib/stories/index';
export * from './lib/auth';
export * from './lib/common';