Compare commits

...
Author SHA1 Message Date
Denys Vuika a1dd297439 chore: add React and ReactDOM dependencies to package.json and package-lock.json
- Included React version 19.2.4 and ReactDOM version 19.2.4 to enhance UI capabilities.
- Updated package-lock.json to reflect the new dependencies and their integrity checks.
2026-02-26 13:51:09 +00:00
Denys Vuika 431e921031 chore: update webpack and acorn dependencies in package.json and package-lock.json
- Upgraded webpack from 5.104.1 to 5.105.3 for improved performance and features.
- Updated acorn from 8.15.0 to 8.16.0 to incorporate the latest fixes.
- Adjusted enhanced-resolve and watchpack versions for compatibility.
2026-02-26 13:46:53 +00:00
Denys Vuika 8f2c812205 chore: update Node.js version in .nvmrc to 24.14.0 2026-02-26 13:41:53 +00:00
Denys Vuika c9c18180dd chore: clean up package-lock.json by removing redundant workspace dependencies
- Removed duplicate entries for workspace dependencies under @nx/cypress and @nx/storybook.
- Streamlined the structure of the package-lock.json for better clarity and maintainability.
2026-02-26 13:38:29 +00:00
Denys Vuika 6bce4272d3 Revert "chore: update Node.js version in .nvmrc to 24.13.0"
This reverts commit b80309fa94.
2026-02-26 13:37:12 +00:00
Denys Vuika b80309fa94 chore: update Node.js version in .nvmrc to 24.13.0 2026-02-26 13:34:35 +00:00
Denys Vuika 3a8a15af82 refactor: enhance file-uploading-dialog component for improved focus handling
- Added a template reference variable to the file-uploading-dialog component for better access to the dialog element.
- Updated focus handling in ngOnInit to use the new reference, simplifying the code and improving clarity.
- Streamlined property initializations for consistency.
2026-02-26 13:10:57 +00:00
Denys Vuika 3fb2d6e5dc refactor: update stylelint configuration in VSCode settings for improved path resolution
- Changed the stylelint.configFile path to use the workspace folder variable for better compatibility across different environments.
- Added stylelint.configBasedir to ensure correct base directory resolution for stylelint configuration.
2026-02-26 12:58:42 +00:00
Denys Vuika 86524ed15c refactor: improve template syntax in file upload components for clarity
- Reorganized conditional rendering in file-uploading-dialog.component.html to enhance readability by using if-else statements.
- Updated upload-button.component.html to streamline the label rendering logic, ensuring consistent display of static titles and translated text.
2026-02-26 12:49:48 +00:00
Denys Vuika beb00b22ec refactor: simplify breadcrumb component template syntax for clarity
- Replaced switch-case syntax with if-else statements in breadcrumb.component.html to enhance readability and maintainability of the template logic.
- Streamlined the rendering of breadcrumb items while preserving functionality.
2026-02-26 11:05:49 +00:00
Denys Vuika bdb197b520 refactor: fix template syntax in breadcrumb component for improved rendering
- Corrected the closing tag syntax in breadcrumb.component.html to ensure proper rendering of the breadcrumb items.
2026-02-26 10:52:19 +00:00
Denys Vuika 06c1e6c594 refactor: update file-uploading-list template to track file IDs
- Modified the file-uploading-list.component.html to track file IDs instead of file objects for improved performance and clarity in rendering.
2026-02-26 10:51:43 +00:00
Denys Vuika 620580a851 refactor: improve dropdown-breadcrumb and file-uploading-list-row templates for clarity
- Updated dropdown-breadcrumb.component.html to track node IDs instead of nodes for better performance.
- Refactored file-uploading-list-row.component.html to enhance readability by restructuring conditional rendering and simplifying the template logic.
2026-02-26 10:51:11 +00:00
Denys Vuika d170a41894 docs: update Decimal Number Pipe documentation for clarity
- Revised the Decimal Number Pipe documentation to improve clarity by removing unnecessary links and formatting.
- Enhanced the description of the `digitsInfo` parameter and updated the Result section for better readability.
2026-02-26 10:49:10 +00:00
Denys Vuika af82c9c4fc refactor: remove ecm-user.service.mock and update references
- Deleted the ecm-user.service.mock file to eliminate unused mock data, enhancing code clarity.
- Updated public API exports to remove the reference to the deleted mock file.
- Introduced the fakeEcmUser object directly in the people-content.service.spec.ts for improved test isolation and maintainability.
2026-02-26 10:08:26 +00:00
Denys Vuika 94fc14b40a refactor: remove BreadcrumbModule export from public API
- Deleted the export statement for BreadcrumbModule in public-api.ts to reflect its removal from the codebase, enhancing clarity and maintainability.
2026-02-26 09:59:15 +00:00
Denys Vuika 9f3bd8ff53 refactor: remove BreadcrumbModule and its references from ContentModule
- Deleted the BreadcrumbModule and its associated directives to streamline the ContentModule.
- Updated ContentModule imports and exports to reflect the removal, enhancing maintainability.
2026-02-26 09:57:51 +00:00
Denys Vuika 40862091bd refactor: improve breadcrumb component structure and remove unused imports
- Enhanced the breadcrumb.component.html for better readability by restructuring the template and using structural directives.
- Streamlined the rendering logic for previous and last nodes in the breadcrumb.
- Removed the unused CommonModule import from breadcrumb.component.ts to simplify dependencies.
2026-02-26 09:55:44 +00:00
Denys Vuika 6ce65aeb4e refactor: enhance breadcrumb component templates for improved readability and functionality
- Updated breadcrumb.component.html and dropdown-breadcrumb.component.html to use structural directives for better readability and maintainability.
- Simplified the rendering of previous nodes and last nodes in the breadcrumb, ensuring proper handling of optional values.
- Removed unnecessary imports in dropdown-breadcrumb.component.ts to streamline the component's dependencies.
2026-02-26 09:47:42 +00:00
Denys Vuika 710fa104bd refactor: clean up ContentModule by removing unused components
- Removed several unused components from the ContentModule, including TreeViewComponent, ContentTypeDialogComponent, and others, to streamline the module.
- Updated the module's exports and imports accordingly to reflect these changes, enhancing maintainability.
2026-02-26 09:25:36 +00:00
Denys Vuika cc3716200d refactor: remove DecimalNumberModel and update related references
- Deleted the DecimalNumberModel class from the models directory to streamline the codebase.
- Updated public API exports to remove references to DecimalNumberModel.
- Replaced the class with an interface in the decimal-number.pipe.ts file to maintain functionality while simplifying the implementation.
2026-02-26 09:15:46 +00:00
Denys Vuika 1a3ac072ac docs: remove references to File Upload Error Pipe from documentation
- Deleted the File Upload Error Pipe documentation and its references from various components and the version index.
- Updated README and component documentation to reflect the removal of the pipe, ensuring consistency across the documentation.
2026-02-26 08:56:23 +00:00
Denys Vuika 31ac216bf9 test: add unit tests for getFileUploadErrorKey method
- Introduced unit tests for the getFileUploadErrorKey method in the file uploading list row component.
- Added tests for various error codes (500, 404, 403) and scenarios for null, undefined, and zero values to ensure correct key format retrieval. [ci:force]
2026-02-26 08:38:18 +00:00
Denys Vuika de21706f7d fix: handle optional error code in file upload error key retrieval
- Updated the getFileUploadErrorKey method to accept an optional errorCode parameter, allowing for better handling of undefined or null values.
- This change improves the robustness of error message generation in the file uploading list row component.
2026-02-25 17:43:52 +00:00
Denys Vuika d2c2b07676 fix: update aria-label translation for upload error status
- Modified the aria-label attribute in the file uploading list row component to improve translation handling for upload error messages.
- Removed unnecessary translation parameter to streamline the error message retrieval process.
2026-02-25 17:42:51 +00:00
Denys Vuika 06801292b6 test: add toggle icon state tests for mouse events and focus handling
- Introduced unit tests for the toggle icon state in the file uploading list row component.
- Added tests for mouse enter, mouse leave, focus, and blur events to ensure correct state management.
- Verified independent state handling for toggleIconState and toggleIconCancelState. [ci:force]
2026-02-25 17:25:02 +00:00
Denys Vuika 5f26e6dd53 chore: remove Toggle Icon directive and related references
- Deleted the Toggle Icon directive implementation and its associated tests.
- Updated documentation to reflect the removal of the directive.
- Adjusted components to eliminate dependencies on the Toggle Icon directive.
2026-02-25 15:57:14 +00:00
Denys Vuika 7f19c6661c refactor: clean up upload error handling and improve UI translations
- Removed the FileUploadErrorPipe and its associated tests to streamline error handling.
- Updated the upload button and file uploading list row components to enhance translation handling for error messages.
- Adjusted HTML templates to ensure consistent spacing in static titles.
- Minor refactoring in the file uploading list row component to introduce a new method for error key retrieval.
2026-02-25 15:42:14 +00:00
Denys Vuika 5194777451 migrate upload dialog to control flow 2026-02-25 15:19:35 +00:00
Denys Vuika a77c268904 update lock file 2026-02-25 15:19:35 +00:00
43 changed files with 875 additions and 1746 deletions
+1 -1
View File
@@ -1 +1 @@
24.11.1
24.14.0
+2 -4
View File
@@ -20,10 +20,8 @@
"stylelint.enable": true,
"css.validate": false,
"scss.validate": false,
"stylelint.config": {
"extends": "./.stylelintrc.json"
},
"stylelint.configFile": ".stylelintrc.json",
"stylelint.configFile": "${workspaceFolder}/.stylelintrc.json",
"stylelint.configBasedir": "${workspaceFolder}",
"stylelint.validate": [
"css",
"less",
-7
View File
@@ -323,7 +323,6 @@ for more information about installing and using the source code.
| [Node Favorite directive](content-services/directives/node-favorite.directive.md) | Selectively toggles nodes as favorites. | [Source](../lib/content-services/src/lib/directives/node-favorite.directive.ts) |
| [Node Lock directive](content-services/directives/node-lock.directive.md) | Locks or unlocks a node. | [Source](../lib/content-services/src/lib/directives/node-lock.directive.ts) |
| [Node Restore directive](content-services/directives/node-restore.directive.md) | Restores deleted nodes to their original location. | [Source](../lib/content-services/src/lib/directives/node-restore.directive.ts) |
| [Toggle Icon directive](content-services/directives/toggle-icon.directive.md) | Toggle icon on mouse or keyboard event for a selectable element. | [Source](../lib/content-services/src/lib/upload/directives/toggle-icon.directive.ts) |
| [Version Compatibility Directive](content-services/directives/version-compatibility.directive.md) | Enables/disables components based on ACS version in use. | [Source](../lib/content-services/src/lib/version-compatibility/version-compatibility.directive.ts) |
### Dialogs
@@ -348,12 +347,6 @@ for more information about installing and using the source code.
| [Permission Style model](content-services/models/permissions-style.model.md) | Sets custom CSS styles for rows of a Document List according to the item's permissions. | [Source](../lib/content-services/src/lib/document-list/models/permissions-style.model.ts) |
| [Row Filter Model](content-services/models/row-filter.model.md) | Defines the Row Filter function used by the Document List Component. | [Source](../lib/content-services/document-list/data/row-filter.model.ts) |
### Pipes
| Name | Description | Source link |
| ---- | ----------- | ----------- |
| [File upload error pipe](content-services/pipes/file-upload-error.pipe.md) | Converts an upload error code to an error message. | [Source](../lib/content-services/src/lib/upload/pipes/file-upload-error.pipe.ts) |
### Services
| Name | Description | Source link |
@@ -40,4 +40,3 @@ This component should be used in combination with the
- [Upload button component](upload-button.component.md)
- [Upload drag area component](upload-drag-area.component.md)
- [File upload error pipe](../pipes/file-upload-error.pipe.md)
@@ -86,4 +86,3 @@ export class MyComponent {
## See also
- [Upload Version Button component](upload-version-button.component.md)
- [File upload error pipe](../pipes/file-upload-error.pipe.md)
@@ -141,4 +141,3 @@ This could be either a selection of 2 or more files, or a folder with multiple e
- [File uploading dialog component](file-uploading-dialog.component.md)
- [Upload button component](upload-button.component.md)
- [File upload error pipe](../pipes/file-upload-error.pipe.md)
@@ -1,27 +0,0 @@
---
Title: Toggle Icon directive
Added: v2.0.0
Status: Active
Last reviewed: 2019-04-09
---
# [Toggle Icon directive](../../../lib/content-services/src/lib/upload/directives/toggle-icon.directive.ts "Defined in toggle-icon.directive.ts")
Toggle icon on mouse or keyboard event for a selectable element.
## Example Usage
```html
<button mat-icon-button adf-toggle-icon #toggle="toggleIcon">
<mat-icon *ngIf="!toggle.isToggled">
check_circle
</mat-icon>
<mat-icon *ngIf="toggle.isToggled">
remove_circle
</mat-icon>
<button></button>
</button>
```
## Class members
@@ -1,34 +0,0 @@
---
Title: File upload error pipe
Added: v3.0.0
Status: Active
Last reviewed: 2019-01-17
---
# [File upload error pipe](../../../lib/content-services/src/lib/upload/pipes/file-upload-error.pipe.ts "Defined in file-upload-error.pipe.ts")
Converts an upload error code to an error message.
## Basic Usage
<!-- {% raw %} -->
```HTML
<div>
Upload failed: {{ errorCode | adfFileUploadError }}
</div>
```
<!-- {% endraw %} -->
## Details
The pipe takes an upload error code (eg, from the `error` event of one of the
upload components) and converts it to a human-readable message. The message is
automatically translated to the user's chosen language.
## See also
- [File uploading dialog component](../components/file-uploading-dialog.component.md)
- [Upload drag area component](../components/upload-drag-area.component.md)
- [Upload button component](../components/upload-button.component.md)
+4 -4
View File
@@ -5,7 +5,7 @@ Status: Active
Last reviewed: 2019-08-05
---
# [Decimal Number Pipe](../../../lib/core/src/lib/pipes/decimal-number.pipe.ts "Defined in decimal-number.pipe.ts")
# Decimal Number Pipe
Transforms a number to have a certain amount of digits in its integer part and also in its decimal part.
@@ -15,7 +15,7 @@ Transforms a number to have a certain amount of digits in its integer part and a
| Name | Type | Default value | Description |
| ---- | ---- | ------------- | ----------- |
| digitsInfo | [`DecimalNumberModel`](../../../lib/core/src/lib/models/decimal-number.model.ts) | | A format to apply to the date value. [Date Pipe Formats.](https://angular.io/api/common/DatePipe#custom-format-options) |
| digitsInfo | `DecimalNumberModel` | | A format to apply to the date value. [Date Pipe Formats.](https://angular.io/api/common/DatePipe#custom-format-options) |
| locale | string | 'en-US' | A locale id for the locale format rules to use. |
## Details
@@ -30,11 +30,11 @@ This pipe transforms a given number so it follows the set configuration for the
}
```
You can also overwrite this config by passing a [`DecimalNumberModel`](../../../lib/core/src/lib/models/decimal-number.model.ts) as an argument for this pipe.
You can also overwrite this config by passing a `DecimalNumberModel` as an argument for this pipe.
The number can be also localized so it applies commas and dots in the right place depending on the locale id in use.
#### Result
### Result
```ts
decimalNumberPipe.transform(1234.567);
-2
View File
@@ -354,7 +354,6 @@ backend services have been tested with each released version of ADF.
- [Edit process filter cloud component](process-services-cloud/components/edit-process-filter-cloud.component.md)
- [Edit task filter cloud component](process-services-cloud/components/edit-task-filter-cloud.component.md)
- [Extension service](extensions/services/extension.service.md)
- [File upload error pipe](content-services/pipes/file-upload-error.pipe.md)
- [Format space pipe](core/pipes/format-space.pipe.md)
- [Full name pipe](core/pipes/full-name.pipe.md)
- [Group cloud component](process-services-cloud/components/group-cloud.component.md)
@@ -663,7 +662,6 @@ backend services have been tested with each released version of ADF.
- [Text mask component](core/components/text-mask.component.md)
- [Thumbnail service](core/services/thumbnail.service.md)
- [Time ago pipe](core/pipes/time-ago.pipe.md)
- [Toggle icon directive](content-services/directives/toggle-icon.directive.md)
- [Toolbar divider component](core/components/toolbar-divider.component.md)
- [Toolbar title component](core/components/toolbar-title.component.md)
- [Toolbar component](core/components/toolbar.component.md)
@@ -1,74 +1,79 @@
<nav
*ngIf="folderNode"
data-automation-id="breadcrumb"
class="adf-breadcrumb-container"
role="navigation"
[attr.aria-label]="'BREADCRUMB.ARIA-LABEL.BREADCRUMB' | translate"
>
<button
*ngIf="hasPreviousNodes()"
tabindex="0"
class="adf-breadcrumb-dropdown-trigger"
(click)="open()"
[attr.aria-label]="'BREADCRUMB.ARIA-LABEL.DROPDOWN' | translate"
@if (folderNode) {
<nav
data-automation-id="breadcrumb"
class="adf-breadcrumb-container"
role="navigation"
[attr.aria-label]="'BREADCRUMB.ARIA-LABEL.BREADCRUMB' | translate"
>
<div class="adf-breadcrumb-dropdown-trigger-icon">
<mat-icon [class.adf-isRoot]="!hasPreviousNodes()" adf-icon="folder" />
<mat-icon [class.adf-isRoot]="!hasPreviousNodes()" class="adf-breadcrumb-dropdown-trigger-arrow" adf-icon="arrow_drop_down" />
</div>
</button>
@if (hasPreviousNodes()) {
<button
tabindex="0"
class="adf-breadcrumb-dropdown-trigger"
(click)="open()"
[attr.aria-label]="'BREADCRUMB.ARIA-LABEL.DROPDOWN' | translate"
>
<div class="adf-breadcrumb-dropdown-trigger-icon">
<mat-icon [class.adf-isRoot]="!hasPreviousNodes()" adf-icon="folder" />
<mat-icon [class.adf-isRoot]="!hasPreviousNodes()" class="adf-breadcrumb-dropdown-trigger-arrow" adf-icon="arrow_drop_down" />
</div>
</button>
<mat-select
#dropdown
class="adf-breadcrumb-dropdown-path"
tabindex="-1"
role="button"
>
@for (node of previousNodes; track node.id) {
<mat-option
(click)="onRoutePathClick(node, $event)"
(onSelectionChange)="onRouteClick(node)"
class="adf-breadcrumb-path-option"
[disabled]="readOnly"
>
{{ node.name ?? '' | translate }}
</mat-option>
}
</mat-select>
}
<mat-select
#dropdown
*ngIf="hasPreviousNodes()"
class="adf-breadcrumb-dropdown-path"
tabindex="-1"
role="button"
>
<mat-option
*ngFor="let node of previousNodes"
(click)="onRoutePathClick(node, $event)"
(onSelectionChange)="onRouteClick(node)"
class="adf-breadcrumb-path-option"
[disabled]="readOnly"
@for (item of lastNodes; track item.id; let last = $last) {
<div
[class.adf-active]="last"
title="{{ item.name ?? '' | translate }}"
class="adf-breadcrumb-item">
@if (breadcrumbItemIsAnchor(last)) {
<a
href="#"
[attr.data-automation-id]="'breadcrumb_' + item.name"
class="adf-breadcrumb-item-anchor"
(click)="onRoutePathClick(item, $event)"
>
{{ item.name ?? '' | translate }}
</a>
} @else {
<div class="adf-breadcrumb-item-current" aria-current="location">
{{ (selectedRowItemsCount < 1 ? item.name ?? '' : 'BREADCRUMB.HEADER.SELECTED') | translate: { count: selectedRowItemsCount } }}
</div>
}
@if (!last) {
<mat-icon class="adf-breadcrumb-item-chevron" adf-icon="chevron_right" />
}
</div>
}
</nav>
@if (!folderNode && hasRoot) {
<nav
data-automation-id="breadcrumb"
role="navigation"
[attr.aria-label]="'BREADCRUMB.ARIA-LABEL.BREADCRUMB' | translate"
>
{{ node.name | translate }}
</mat-option>
</mat-select>
<div
*ngFor="let item of lastNodes; let last = last"
[class.adf-active]="last"
[ngSwitch]="breadcrumbItemIsAnchor(last)"
title="{{ item.name | translate }}"
class="adf-breadcrumb-item">
<a
*ngSwitchCase="true"
href="#"
[attr.data-automation-id]="'breadcrumb_' + item.name"
class="adf-breadcrumb-item-anchor"
(click)="onRoutePathClick(item, $event)"
>
{{ item.name | translate }}
</a>
<div *ngSwitchDefault class="adf-breadcrumb-item-current" aria-current="location">
{{ (selectedRowItemsCount < 1 ? item.name : 'BREADCRUMB.HEADER.SELECTED') | translate: { count: selectedRowItemsCount } }}
</div>
<mat-icon class="adf-breadcrumb-item-chevron" *ngIf="!last" adf-icon="chevron_right" />
</div>
</nav>
<nav
*ngIf="!folderNode && hasRoot"
data-automation-id="breadcrumb"
role="navigation"
[attr.aria-label]="'BREADCRUMB.ARIA-LABEL.BREADCRUMB' | translate"
>
<div class="adf-breadcrumb-item adf-active" role="group">
<div class="adf-breadcrumb-item-current">
{{ root | translate }}
</div>
</div>
</nav>
<div class="adf-breadcrumb-item adf-active" role="group">
<div class="adf-breadcrumb-item-current">
{{ root ?? '' | translate }}
</div>
</div>
</nav>
}
}
@@ -31,7 +31,6 @@ import {
import { MatSelect, MatSelectModule } from '@angular/material/select';
import { Node, PathElement } from '@alfresco/js-api';
import { DocumentListComponent } from '../document-list/components/document-list.component';
import { CommonModule } from '@angular/common';
import { TranslatePipe, TranslateService } from '@ngx-translate/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { IconModule } from '@alfresco/adf-core';
@@ -39,7 +38,7 @@ import { LiveAnnouncer } from '@angular/cdk/a11y';
@Component({
selector: 'adf-breadcrumb',
imports: [CommonModule, IconModule, TranslatePipe, MatSelectModule],
imports: [IconModule, TranslatePipe, MatSelectModule],
templateUrl: './breadcrumb.component.html',
styleUrls: ['./breadcrumb.component.scss'],
encapsulation: ViewEncapsulation.None,
@@ -1,29 +0,0 @@
/*!
* @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 { NgModule } from '@angular/core';
import { BreadcrumbComponent } from './breadcrumb.component';
import { DropdownBreadcrumbComponent } from './dropdown-breadcrumb.component';
export const BREADCRUMB_DIRECTIVES = [BreadcrumbComponent, DropdownBreadcrumbComponent] as const;
/** @deprecated use `...BREADCRUMB_DIRECTIVES` instead */
@NgModule({
imports: [...BREADCRUMB_DIRECTIVES],
exports: [...BREADCRUMB_DIRECTIVES]
})
export class BreadcrumbModule {}
@@ -1,48 +1,47 @@
<nav *ngIf="route.length > 0" class="adf-dropdown-breadcrumb-container"
role="navigation"
[attr.aria-label]="'BREADCRUMB.ARIA-LABEL.BREADCRUMB' | translate">
@if (route.length > 0) {
<nav class="adf-dropdown-breadcrumb-container"
role="navigation"
[attr.aria-label]="'BREADCRUMB.ARIA-LABEL.BREADCRUMB' | translate">
<button
id="dropdown-breadcrumb-button"
[tabindex]="hasPreviousNodes() ? 0 : -1"
class="adf-dropdown-breadcrumb-trigger"
(click)="open()"
[attr.aria-label]="'BREADCRUMB.ARIA-LABEL.DROPDOWN' | translate:{ folderName: currentNode?.name }"
[attr.aria-haspopup]="hasPreviousNodes() ? 'true' : null"
[attr.aria-expanded]="dropdown?.panelOpen || false"
data-automation-id="dropdown-breadcrumb-trigger">
<mat-icon class="adf-dropdown-breadcrumb-icon" [class.adf-isRoot]="!hasPreviousNodes()" adf-icon="folder" />
id="dropdown-breadcrumb-button"
[tabindex]="hasPreviousNodes() ? 0 : -1"
class="adf-dropdown-breadcrumb-trigger"
(click)="open()"
[attr.aria-label]="'BREADCRUMB.ARIA-LABEL.DROPDOWN' | translate:{ folderName: currentNode?.name }"
[attr.aria-haspopup]="hasPreviousNodes() ? 'true' : null"
[attr.aria-expanded]="dropdown?.panelOpen || false"
data-automation-id="dropdown-breadcrumb-trigger">
<mat-icon class="adf-dropdown-breadcrumb-icon" [class.adf-isRoot]="!hasPreviousNodes()" adf-icon="folder" />
</button>
<mat-icon class="adf-dropdown-breadcrumb-icon adf-dropdown-breadcrumb-item-chevron" adf-icon="chevron_right" />
<div class="adf-dropdown-breadcrumb-path">
@if (hasPreviousNodes()) {
<mat-select
#dropdown
*ngIf="hasPreviousNodes()"
panelClass="adf-breadcrumb-dropdown-panel"
class="adf-dropdown-breadcrumb-path-select"
tabindex="-1"
role="button"
data-automation-id="dropdown-breadcrumb-path"
aria-labelledby="dropdown-breadcrumb-button">
#dropdown
panelClass="adf-breadcrumb-dropdown-panel"
class="adf-dropdown-breadcrumb-path-select"
tabindex="-1"
role="button"
data-automation-id="dropdown-breadcrumb-path"
aria-labelledby="dropdown-breadcrumb-button">
@for (node of previousNodes; track node.id) {
<mat-option
*ngFor="let node of previousNodes;"
(click)="onRoutePathClick(node, $event)"
(onSelectionChange)="onRouteClick(node)"
class="adf-dropdown-breadcrumb-path-option adf-dropdown-breadcrumb-path-option-node"
data-automation-class="dropdown-breadcrumb-path-option"
[disabled]="readOnly">
{{ node.name | translate }}
(click)="onRoutePathClick(node, $event)"
(onSelectionChange)="onRouteClick(node)"
class="adf-dropdown-breadcrumb-path-option adf-dropdown-breadcrumb-path-option-node"
data-automation-class="dropdown-breadcrumb-path-option"
[disabled]="readOnly">
{{ node.name ?? '' | translate }}
</mat-option>
}
</mat-select>
}
</div>
<span
class="adf-current-folder"
[class.adf-isRoot]="!hasPreviousNodes()"
data-automation-id="current-folder"
aria-current="location">{{ currentNode.name }}
class="adf-current-folder"
[class.adf-isRoot]="!hasPreviousNodes()"
data-automation-id="current-folder"
aria-current="location">{{ currentNode.name }}
</span>
</nav>
</nav>
}
@@ -19,13 +19,13 @@ import { Component, OnChanges, ViewChild, ViewEncapsulation } from '@angular/cor
import { MatSelect, MatSelectModule } from '@angular/material/select';
import { PathElement, Node } from '@alfresco/js-api';
import { BreadcrumbComponent } from './breadcrumb.component';
import { CommonModule } from '@angular/common';
import { TranslatePipe } from '@ngx-translate/core';
import { IconModule } from '@alfresco/adf-core';
@Component({
selector: 'adf-dropdown-breadcrumb',
imports: [CommonModule, IconModule, MatSelectModule, TranslatePipe],
imports: [IconModule, MatSelectModule, TranslatePipe],
templateUrl: './dropdown-breadcrumb.component.html',
styleUrls: ['./dropdown-breadcrumb.component.scss'],
encapsulation: ViewEncapsulation.None,
@@ -17,5 +17,3 @@
export * from './breadcrumb.component';
export * from './dropdown-breadcrumb.component';
export * from './breadcrumb.module';
@@ -1,39 +0,0 @@
/*!
* @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 { Person } from '@alfresco/js-api';
export const fakeEcmUser: Person = {
id: 'fake-id',
firstName: 'fake-ecm-first-name',
lastName: 'fake-ecm-last-name',
description: 'i am a fake user for test',
avatarId: 'fake-avatar-id',
email: 'fakeEcm@ecmUser.com',
skypeId: 'fake-skype-id',
googleId: 'fake-googleId-id',
instantMessageId: 'fake-instantMessageId-id',
company: null,
jobTitle: 'job-ecm-test',
location: 'fake location',
mobile: '000000000',
telephone: '11111111',
statusUpdatedAt: new Date(),
userStatus: 'active',
enabled: true,
emailNotificationsEnabled: true
};
@@ -41,4 +41,3 @@ export * from './models/allowable-operations.enum';
export * from './interfaces/search-configuration.interface';
export * from './interfaces/saved-search.interface';
export * from './interfaces/saved-searches-strategy.interface';
export * from './mocks/ecm-user.service.mock';
@@ -15,17 +15,37 @@
* limitations under the License.
*/
import { fakeEcmUser } from '../mocks/ecm-user.service.mock';
import { RedirectAuthService } from '@alfresco/adf-core';
import { PeopleContentQueryRequestModel, PeopleContentService } from './people-content.service';
import { TestBed } from '@angular/core/testing';
import { PersonPaging } from '@alfresco/js-api';
import { Person, PersonPaging } from '@alfresco/js-api';
import { provideHttpClient } from '@angular/common/http';
import { provideHttpClientTesting } from '@angular/common/http/testing';
import { EMPTY, firstValueFrom, of } from 'rxjs';
import { AlfrescoApiService } from '../../services';
import { AlfrescoApiServiceMock } from '../../mock';
const fakeEcmUser: Person = {
id: 'fake-id',
firstName: 'fake-ecm-first-name',
lastName: 'fake-ecm-last-name',
description: 'i am a fake user for test',
avatarId: 'fake-avatar-id',
email: 'fakeEcm@ecmUser.com',
skypeId: 'fake-skype-id',
googleId: 'fake-googleId-id',
instantMessageId: 'fake-instantMessageId-id',
company: null,
jobTitle: 'job-ecm-test',
location: 'fake location',
mobile: '000000000',
telephone: '11111111',
statusUpdatedAt: new Date(),
userStatus: 'active',
enabled: true,
emailNotificationsEnabled: true
};
export const fakeEcmUser2 = {
id: 'another-fake-id',
firstName: 'another-fake-first-name',
@@ -76,7 +76,7 @@
<div>
<ng-container *ngIf="isUploadEnabled()">
<adf-upload-button
[staticTitle]="'FORM.FIELD.UPLOAD' | translate "
[staticTitle]="'FORM.FIELD.UPLOAD' | translate"
[multipleFiles]="isMultipleSelection()"
[rootFolderId]="currentDirectoryId"
[disabled]="isNotAllowedToUpload()"
+2 -32
View File
@@ -21,7 +21,6 @@ import { MatDatetimepickerModule, MatNativeDatetimeModule } from '@mat-datetimep
import { CONTENT_TAG_DIRECTIVES } from './tag/tag.module';
import { DOCUMENT_LIST_DIRECTIVES } from './document-list/document-list.module';
import { CONTENT_SEARCH_DIRECTIVES } from './search/search.module';
import { BREADCRUMB_DIRECTIVES } from './breadcrumb/breadcrumb.module';
import { CONTENT_VERSION_DIRECTIVES } from './version-manager/version-manager.module';
import { CONTENT_NODE_SELECTOR_DIRECTIVES } from './content-node-selector/content-node-selector.module';
import { CONTENT_NODE_SHARE_DIRECTIVES } from './content-node-share/content-node-share.module';
@@ -34,17 +33,8 @@ import { versionCompatibilityFactory } from './version-compatibility/version-com
import { VersionCompatibilityService } from './version-compatibility/version-compatibility.service';
import { contentAuthLoaderFactory } from './auth-loader/content-auth-loader-factory';
import { ContentAuthLoaderService } from './auth-loader/content-auth-loader.service';
import { CategoriesManagementComponent } from './category';
import { TreeComponent } from './tree';
import { NewVersionUploaderDialogComponent } from './new-version-uploader';
import { VersionCompatibilityDirective } from './version-compatibility';
import { CONTENT_UPLOAD_DIRECTIVES } from './upload';
import { TreeViewComponent } from './tree-view';
import { NodeCommentsComponent } from './node-comments';
import { AlfrescoViewerComponent } from './viewer';
import { ContentTypeDialogComponent } from './content-type';
import { MaterialModule } from './material.module';
import { AlfrescoIconComponent } from './alfresco-icon/alfresco-icon.component';
import { AlfrescoApiService } from './services/alfresco-api.service';
import { AlfrescoApiNoAuthService } from './api-factories/alfresco-api-no-auth.service';
import { AlfrescoApiLoaderService, createAlfrescoApiInstance } from './api-factories/alfresco-api-v2-loader.service';
@@ -59,23 +49,13 @@ import { AlfrescoApiLoaderService, createAlfrescoApiInstance } from './api-facto
...CONTENT_SEARCH_DIRECTIVES,
...DOCUMENT_LIST_DIRECTIVES,
...CONTENT_UPLOAD_DIRECTIVES,
...BREADCRUMB_DIRECTIVES,
...CONTENT_NODE_SELECTOR_DIRECTIVES,
...CONTENT_NODE_SHARE_DIRECTIVES,
...CONTENT_METADATA_DIRECTIVES,
...CONTENT_DIRECTIVES,
...CONTENT_PERMISSION_MANAGER_DIRECTIVES,
...CONTENT_VERSION_DIRECTIVES,
TreeViewComponent,
ContentTypeDialogComponent,
...ASPECT_LIST_DIRECTIVES,
VersionCompatibilityDirective,
NodeCommentsComponent,
TreeComponent,
AlfrescoViewerComponent,
CategoriesManagementComponent,
NewVersionUploaderDialogComponent,
AlfrescoIconComponent
...ASPECT_LIST_DIRECTIVES
],
providers: [provideTranslations('adf-content-services', 'assets/adf-content-services')],
exports: [
@@ -84,7 +64,6 @@ import { AlfrescoApiLoaderService, createAlfrescoApiInstance } from './api-facto
...DOCUMENT_LIST_DIRECTIVES,
...CONTENT_UPLOAD_DIRECTIVES,
...CONTENT_SEARCH_DIRECTIVES,
...BREADCRUMB_DIRECTIVES,
...CONTENT_NODE_SELECTOR_DIRECTIVES,
...CONTENT_NODE_SHARE_DIRECTIVES,
...CONTENT_METADATA_DIRECTIVES,
@@ -92,16 +71,7 @@ import { AlfrescoApiLoaderService, createAlfrescoApiInstance } from './api-facto
...CONTENT_DIRECTIVES,
...CONTENT_PERMISSION_MANAGER_DIRECTIVES,
...CONTENT_VERSION_DIRECTIVES,
TreeViewComponent,
...ASPECT_LIST_DIRECTIVES,
ContentTypeDialogComponent,
VersionCompatibilityDirective,
NodeCommentsComponent,
TreeComponent,
AlfrescoViewerComponent,
CategoriesManagementComponent,
NewVersionUploaderDialogComponent,
AlfrescoIconComponent
...ASPECT_LIST_DIRECTIVES
]
})
export class ContentModule {
@@ -1,116 +1,117 @@
<div *ngIf="canShowDialog()"
role="dialog"
[attr.aria-label]="'ADF_FILE_UPLOAD.ARIA-LABEL.DIALOG'| translate"
tabindex="0"
class="adf-upload-dialog"
id="upload-dialog"
[class.adf-upload-dialog--minimized]="isDialogMinimized">
@if (canShowDialog()) {
<div
#uploadDialog
role="dialog"
[attr.aria-label]="'ADF_FILE_UPLOAD.ARIA-LABEL.DIALOG'| translate"
tabindex="0"
class="adf-upload-dialog"
id="upload-dialog"
[class.adf-upload-dialog--minimized]="isDialogMinimized">
<header class="adf-upload-dialog__header">
<button
mat-icon-button
class="adf-upload-dialog__header-button"
[attr.data-automation-id]="'adf-upload-dialog__toggle-minimize'"
[attr.aria-label]="(isDialogMinimized ?
'ADF_FILE_UPLOAD.ARIA-LABEL.DIALOG_MAXIMIZE':
'ADF_FILE_UPLOAD.ARIA-LABEL.DIALOG_MINIMIZE') | translate"
[disabled]="isConfirmation"
[attr.aria-expanded]="!isDialogMinimized"
(click)="toggleMinimized()">
<mat-icon class="adf-upload-dialog__header-button-icon" [adf-icon]="isDialogMinimized ? 'keyboard_arrow_up' : 'keyboard_arrow_down'" />
</button>
<button
mat-icon-button
class="adf-upload-dialog__header-button"
[attr.data-automation-id]="'adf-upload-dialog__toggle-minimize'"
[attr.aria-label]="(isDialogMinimized ?
'ADF_FILE_UPLOAD.ARIA-LABEL.DIALOG_MAXIMIZE':
'ADF_FILE_UPLOAD.ARIA-LABEL.DIALOG_MINIMIZE') | translate"
[disabled]="isConfirmation"
[attr.aria-expanded]="!isDialogMinimized"
(click)="toggleMinimized()">
<mat-icon class="adf-upload-dialog__header-button-icon" [adf-icon]="isDialogMinimized ? 'keyboard_arrow_up' : 'keyboard_arrow_down'" />
</button>
<span
class="adf-upload-dialog__title"
*ngIf="!uploadList.isUploadCancelled()">
@if (uploadList.isUploadCancelled()) {
<span class="adf-upload-dialog__title">
{{ 'FILE_UPLOAD.MESSAGES.UPLOAD_CANCELED' | translate }}
</span>
} @else {
<span class="adf-upload-dialog__title">
{{ 'FILE_UPLOAD.MESSAGES.UPLOAD_PROGRESS'
| translate: {
completed: totalCompleted,
total: filesUploadingList.length
}
}}
| translate: {
completed: totalCompleted,
total: filesUploadingList.length
}
}}
</span>
<span
class="adf-upload-dialog__title"
*ngIf="uploadList.isUploadCancelled()">
{{ 'FILE_UPLOAD.MESSAGES.UPLOAD_CANCELED' | translate }}
</span>
</header>
<section class="adf-upload-dialog__info"
*ngIf="totalErrors">
{{
(totalErrors > 1
? 'FILE_UPLOAD.MESSAGES.UPLOAD_ERRORS'
: 'FILE_UPLOAD.MESSAGES.UPLOAD_ERROR')
| translate: { total: totalErrors }
}}
}
</header>
@if (totalErrors) {
<section class="adf-upload-dialog__info">
{{
(totalErrors > 1
? 'FILE_UPLOAD.MESSAGES.UPLOAD_ERRORS'
: 'FILE_UPLOAD.MESSAGES.UPLOAD_ERROR')
| translate: { total: totalErrors }
}}
</section>
<section class="adf-upload-dialog__content"
[class.adf-upload-dialog--padding]="isConfirmation" aria-live="polite">
<adf-file-uploading-list
class="adf-file-uploading-list"
[class.adf-upload-dialog--hide]="isConfirmation"
#uploadList
[files]="filesUploadingList">
<ng-template let-file="$implicit">
<adf-file-uploading-list-row
[file]="file"
(cancel)="uploadList.cancelFile(file)" />
</ng-template>
</adf-file-uploading-list>
<div
aria-live="polite"
aria-labelledby="confirmationTitle"
aria-describedby="confirmationDescription"
class="adf-upload-dialog__confirmation"
[class.adf-upload-dialog--hide]="!isConfirmation">
<p role="heading" aria-level="2" id="confirmationTitle" class="adf-upload-dialog__confirmation--title">
{{ 'ADF_FILE_UPLOAD.CONFIRMATION.MESSAGE.TITLE' | translate }}
</p>
<p id="confirmationDescription" class="adf-upload-dialog__confirmation--text">
{{ 'ADF_FILE_UPLOAD.CONFIRMATION.MESSAGE.TEXT' | translate }}
</p>
</div>
</section>
<footer class="adf-upload-dialog__actions" *ngIf="!isConfirmation">
}
<section class="adf-upload-dialog__content"
[class.adf-upload-dialog--padding]="isConfirmation" aria-live="polite">
<adf-file-uploading-list
class="adf-file-uploading-list"
[class.adf-upload-dialog--hide]="isConfirmation"
#uploadList
[files]="filesUploadingList">
<ng-template let-file="$implicit">
<adf-file-uploading-list-row
[file]="file"
(cancel)="uploadList.cancelFile(file)" />
</ng-template>
</adf-file-uploading-list>
<div
aria-live="polite"
aria-labelledby="confirmationTitle"
aria-describedby="confirmationDescription"
class="adf-upload-dialog__confirmation"
[class.adf-upload-dialog--hide]="!isConfirmation">
<p role="heading" aria-level="2" id="confirmationTitle" class="adf-upload-dialog__confirmation--title">
{{ 'ADF_FILE_UPLOAD.CONFIRMATION.MESSAGE.TITLE' | translate }}
</p>
<p id="confirmationDescription" class="adf-upload-dialog__confirmation--text">
{{ 'ADF_FILE_UPLOAD.CONFIRMATION.MESSAGE.TEXT' | translate }}
</p>
</div>
</section>
@if (!isConfirmation) {
<footer class="adf-upload-dialog__actions">
@if (canShowCancelAll()) {
<button
id="adf-upload-dialog-cancel-all"
[attr.aria-label]="'ADF_FILE_UPLOAD.ARIA-LABEL.STOP_ALL' | translate"
color="primary"
mat-button
*ngIf="canShowCancelAll()"
(click)="toggleConfirmation()"
id="adf-upload-dialog-cancel-all"
[attr.aria-label]="'ADF_FILE_UPLOAD.ARIA-LABEL.STOP_ALL' | translate"
color="primary"
mat-button
(click)="toggleConfirmation()"
>{{ 'ADF_FILE_UPLOAD.BUTTON.STOP_ALL' | translate }}</button>
}
@if (canCloseDialog()) {
<button
id="adf-upload-dialog-close"
[attr.aria-label]="'ADF_FILE_UPLOAD.ARIA-LABEL.DIALOG_CLOSE' | translate"
*ngIf="canCloseDialog()"
mat-button
color="primary"
(click)="close()"
id="adf-upload-dialog-close"
[attr.aria-label]="'ADF_FILE_UPLOAD.ARIA-LABEL.DIALOG_CLOSE' | translate"
mat-button
color="primary"
(click)="close()"
>{{ 'ADF_FILE_UPLOAD.BUTTON.CLOSE' | translate }}</button>
}
</footer>
}
@if (isConfirmation) {
<footer class="adf-upload-dialog__actions"
*ngIf="isConfirmation" cdkTrapFocus cdkTrapFocusAutoCapture>
<button
id="adf-upload-dialog-cancel"
[attr.aria-label]="'ADF_FILE_UPLOAD.ARIA-LABEL.CONFIRMATION.CANCEL' | translate"
mat-button
(click)="cancelAllUploads()"
>{{ 'ADF_FILE_UPLOAD.CONFIRMATION.BUTTON.CANCEL' | translate }}</button>
<button
id="adf-upload-dialog-confirm"
[attr.aria-label]="'ADF_FILE_UPLOAD.ARIA-LABEL.CONFIRMATION.CONTINUE' | translate"
mat-button
color="primary"
(click)="toggleConfirmation()"
>{{ 'ADF_FILE_UPLOAD.CONFIRMATION.BUTTON.CONTINUE' | translate }}</button>
cdkTrapFocus cdkTrapFocusAutoCapture>
<button
id="adf-upload-dialog-cancel"
[attr.aria-label]="'ADF_FILE_UPLOAD.ARIA-LABEL.CONFIRMATION.CANCEL' | translate"
mat-button
(click)="cancelAllUploads()"
>{{ 'ADF_FILE_UPLOAD.CONFIRMATION.BUTTON.CANCEL' | translate }}</button>
<button
id="adf-upload-dialog-confirm"
[attr.aria-label]="'ADF_FILE_UPLOAD.ARIA-LABEL.CONFIRMATION.CONTINUE' | translate"
mat-button
color="primary"
(click)="toggleConfirmation()"
>{{ 'ADF_FILE_UPLOAD.CONFIRMATION.BUTTON.CONTINUE' | translate }}</button>
</footer>
}
</div>
}
@@ -38,7 +38,7 @@ import { delay } from 'rxjs/operators';
import { UploadService } from '../../common/services/upload.service';
import { FileModel, FileUploadStatus } from '../../common/models/file.model';
import { FileUploadCompleteEvent, FileUploadDeleteEvent } from '../../common/events/file.event';
import { CommonModule } from '@angular/common';
import { MatButtonModule } from '@angular/material/button';
import { TranslatePipe } from '@ngx-translate/core';
import { FileUploadingListRowComponent } from './file-uploading-list-row.component';
@@ -47,7 +47,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({
selector: 'adf-file-uploading-dialog',
imports: [CommonModule, MatButtonModule, TranslatePipe, IconModule, FileUploadingListComponent, FileUploadingListRowComponent, A11yModule],
imports: [MatButtonModule, TranslatePipe, IconModule, FileUploadingListComponent, FileUploadingListRowComponent, A11yModule],
templateUrl: './file-uploading-dialog.component.html',
styleUrls: ['./file-uploading-dialog.component.scss'],
encapsulation: ViewEncapsulation.None
@@ -56,7 +56,9 @@ export class FileUploadingDialogComponent implements OnInit, OnDestroy {
private readonly uploadService = inject(UploadService);
private readonly changeDetector = inject(ChangeDetectorRef);
private readonly userPreferencesService = inject(UserPreferencesService);
private readonly elementRef = inject(ElementRef);
@ViewChild('uploadDialog', { read: ElementRef })
private readonly uploadDialogRef: ElementRef<HTMLElement> | undefined;
/** Dialog direction. Can be 'ltr' or 'rtl. */
private direction: Direction = 'ltr';
@@ -70,11 +72,11 @@ export class FileUploadingDialogComponent implements OnInit, OnDestroy {
/** Makes the dialog always visible even when there are no uploads. */
@Input()
alwaysVisible: boolean = false;
alwaysVisible = false;
/** Emitted when a file in the list has an error. */
@Output()
error: EventEmitter<any> = new EventEmitter();
error = new EventEmitter();
@HostBinding('attr.adfUploadDialogRight')
public get isPositionRight(): boolean {
@@ -98,10 +100,7 @@ export class FileUploadingDialogComponent implements OnInit, OnDestroy {
ngOnInit() {
this.dialogActive.pipe(delay(100), takeUntilDestroyed(this.destroyRef)).subscribe(() => {
const element: any = this.elementRef.nativeElement.querySelector('#upload-dialog');
if (element) {
element.focus();
}
this.uploadDialogRef?.nativeElement?.focus();
});
this.uploadService.queueChanged.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((fileList) => {
@@ -1,92 +1,112 @@
<div class="adf-file-uploading-row" [attr.aria-label]="file.name + ' ' + file.status">
<mat-icon *ngIf="mimeType === 'default'" matListItemIcon class="adf-file-uploading-row__type" adf-icon="insert_drive_file" />
@if (mimeType === 'default') {
<mat-icon matListItemIcon class="adf-file-uploading-row__type" adf-icon="insert_drive_file" />
} @else {
<mat-icon svgIcon="adf:{{ mimeType }}" aria-hidden="true" />
}
<mat-icon *ngIf="mimeType !== 'default'" svgIcon="adf:{{ mimeType }}" aria-hidden="true" />
<span
class="adf-file-uploading-row__name"
title="{{ file.name }}">
{{ file.name }}
</span>
<span
class="adf-file-uploading-row__name"
title="{{ file.name }}">
{{ file.name }}
</span>
<span *ngIf="isUploadVersion()" class="adf-file-uploading-row__version" tabindex="0" >
<mat-chip color="primary"
[attr.aria-label]="'ADF_FILE_UPLOAD.ARIA-LABEL.VERSION' | translate: { version: versionNumber }"
[title]="'version' + versionNumber"
>{{ versionNumber }}</mat-chip>
@if (isUploadVersion()) {
<span class="adf-file-uploading-row__version" tabindex="0" >
<mat-chip color="primary"
[attr.aria-label]="'ADF_FILE_UPLOAD.ARIA-LABEL.VERSION' | translate: { version: versionNumber }"
[title]="'version' + versionNumber"
>{{ versionNumber }}</mat-chip>
</span>
}
@if (isUploading()) {
<div
tabindex="0"
role="button"
#toggleIcon="toggleIcon"
adf-toggle-icon
(keyup.enter)="onCancel(file)"
(click)="onCancel(file)"
data-automation-id="cancel-upload-progress"
*ngIf="isUploading()"
[attr.aria-label]="'ADF_FILE_UPLOAD.ARIA-LABEL.STOP_FILE_UPLOAD' | translate: { file: file.name }"
class="adf-file-uploading-row__group adf-file-uploading-row__group--toggle"
title="{{ 'ADF_FILE_UPLOAD.BUTTON.STOP_FILE' | translate }}">
tabindex="0"
role="button"
(mouseenter)="onToggleMouseEnter(toggleIconState)"
(mouseleave)="onToggleMouseLeave(toggleIconState)"
(focus)="onToggleFocus(toggleIconState)"
(blur)="onToggleBlur(toggleIconState)"
(keyup.enter)="onCancel(file)"
(click)="onCancel(file)"
data-automation-id="cancel-upload-progress"
[attr.aria-label]="'ADF_FILE_UPLOAD.ARIA-LABEL.STOP_FILE_UPLOAD' | translate: { file: file.name }"
class="adf-file-uploading-row__group adf-file-uploading-row__group--toggle"
title="{{ 'ADF_FILE_UPLOAD.BUTTON.STOP_FILE' | translate }}">
<span class="adf-file-uploading-row__status" *ngIf="!toggleIcon.isToggled">
@if (toggleIconState.isToggled) {
<mat-icon class="adf-file-uploading-row__action adf-file-uploading-row__action--cancel" adf-icon="clear" />
} @else {
<span class="adf-file-uploading-row__status">
{{ file.progress.loaded | adfFileSize }} / {{ file.progress.total | adfFileSize }}
</span>
<mat-icon *ngIf="toggleIcon.isToggled" class="adf-file-uploading-row__action adf-file-uploading-row__action--cancel" adf-icon="clear" />
}
</div>
}
@if (isUploadComplete()) {
<button mat-icon-button
adf-toggle-icon
#toggleIcon="toggleIcon"
*ngIf="isUploadComplete()"
class="adf-file-uploading-row__group"
[attr.aria-label]="'ADF_FILE_UPLOAD.ARIA-LABEL.UPLOAD_SUCCESSFUL' | translate: { file: file.name }"
title="{{ 'ADF_FILE_UPLOAD.BUTTON.UPLOAD_SUCCESSFUL' | translate }}">
<mat-icon class="adf-file-uploading-row__status adf-file-uploading-row__status--done" adf-icon="check_circle" />
(mouseenter)="onToggleMouseEnter(toggleIconState)"
(mouseleave)="onToggleMouseLeave(toggleIconState)"
(focus)="onToggleFocus(toggleIconState)"
(blur)="onToggleBlur(toggleIconState)"
class="adf-file-uploading-row__group"
[attr.aria-label]="'ADF_FILE_UPLOAD.ARIA-LABEL.UPLOAD_SUCCESSFUL' | translate: { file: file.name }"
title="{{ 'ADF_FILE_UPLOAD.BUTTON.UPLOAD_SUCCESSFUL' | translate }}">
<mat-icon class="adf-file-uploading-row__status adf-file-uploading-row__status--done" adf-icon="check_circle" />
</button>
}
@if (isUploadVersionComplete()) {
<div
*ngIf="isUploadVersionComplete()"
class="adf-file-uploading-row__file-version"
[attr.aria-label]="'ADF_FILE_UPLOAD.STATUS.FILE_DONE_STATUS' | translate"
>
<mat-icon class="adf-file-uploading-row__status--done" adf-icon="check_circle" matListItemIcon />
class="adf-file-uploading-row__file-version"
[attr.aria-label]="'ADF_FILE_UPLOAD.STATUS.FILE_DONE_STATUS' | translate"
>
<mat-icon class="adf-file-uploading-row__status--done" adf-icon="check_circle" matListItemIcon />
</div>
}
@if (canCancelUpload()) {
<button
adf-toggle-icon
#toggleIconCancel="toggleIcon"
mat-icon-button
*ngIf="canCancelUpload()"
(click)="onCancel(file)"
data-automation-id="cancel-upload-queue"
class="adf-file-uploading-row__group"
title="{{ 'ADF_FILE_UPLOAD.BUTTON.STOP_FILE' | translate }}"
[attr.aria-label]="'ADF_FILE_UPLOAD.ARIA-LABEL.STOP_FILE' | translate: { file: file.name }">
<mat-icon *ngIf="!toggleIconCancel.isToggled" class="adf-file-uploading-row__status adf-file-uploading-row__status--pending" adf-icon="schedule" />
(mouseenter)="onToggleMouseEnter(toggleIconCancelState)"
(mouseleave)="onToggleMouseLeave(toggleIconCancelState)"
(focus)="onToggleFocus(toggleIconCancelState)"
(blur)="onToggleBlur(toggleIconCancelState)"
mat-icon-button
(click)="onCancel(file)"
data-automation-id="cancel-upload-queue"
class="adf-file-uploading-row__group"
title="{{ 'ADF_FILE_UPLOAD.BUTTON.STOP_FILE' | translate }}"
[attr.aria-label]="'ADF_FILE_UPLOAD.ARIA-LABEL.STOP_FILE' | translate: { file: file.name }">
<mat-icon *ngIf="toggleIconCancel.isToggled" class="adf-file-uploading-row__action adf-file-uploading-row__action--remove" adf-icon="remove_circle" />
@if (toggleIconCancelState.isToggled) {
<mat-icon class="adf-file-uploading-row__action adf-file-uploading-row__action--remove" adf-icon="remove_circle" />
} @else {
<mat-icon class="adf-file-uploading-row__status adf-file-uploading-row__status--pending" adf-icon="schedule" />
}
</button>
}
@if (isUploadError()) {
<div
role="status"
*ngIf="isUploadError()"
class="adf-file-uploading-row__block adf-file-uploading-row__status--error">
<mat-icon
[attr.aria-label]="'ADF_FILE_UPLOAD.ARIA-LABEL.UPLOAD_FILE_ERROR' | translate: { error: file.errorCode | adfFileUploadError }"
[title]="file.errorCode | adfFileUploadError"
adf-icon="report_problem"
matListItemIcon
role="status"
class="adf-file-uploading-row__block adf-file-uploading-row__status--error">
<mat-icon
[attr.aria-label]="'ADF_FILE_UPLOAD.ARIA-LABEL.UPLOAD_FILE_ERROR' | translate: { error: getFileUploadErrorKey(file.errorCode) }"
[title]="getFileUploadErrorKey(file.errorCode) | translate"
adf-icon="report_problem"
matListItemIcon
/>
</div>
}
@if (showCancelledStatus()) {
<div
[attr.aria-label]="'ADF_FILE_UPLOAD.STATUS.FILE_CANCELED_STATUS' | translate"
role="status"
*ngIf="showCancelledStatus()"
class="adf-file-uploading-row__block adf-file-uploading-row__status--cancelled">
{{ 'ADF_FILE_UPLOAD.STATUS.FILE_CANCELED_STATUS' | translate }}
[attr.aria-label]="'ADF_FILE_UPLOAD.STATUS.FILE_CANCELED_STATUS' | translate"
role="status"
class="adf-file-uploading-row__block adf-file-uploading-row__status--cancelled">
{{ 'ADF_FILE_UPLOAD.STATUS.FILE_CANCELED_STATUS' | translate }}
</div>
}
</div>
@@ -106,4 +106,118 @@ describe('FileUploadingListRowComponent', () => {
const cancelButton = getCancelButton();
expect(cancelButton.title).toBe('ADF_FILE_UPLOAD.BUTTON.STOP_FILE');
});
describe('getFileUploadErrorKey', () => {
it('should return correct key format for error code 500', () => {
const result = component.getFileUploadErrorKey(500);
expect(result).toBe('FILE_UPLOAD.ERRORS.500');
});
it('should return correct key format for error code 404', () => {
const result = component.getFileUploadErrorKey(404);
expect(result).toBe('FILE_UPLOAD.ERRORS.404');
});
it('should return correct key format for error code 403', () => {
const result = component.getFileUploadErrorKey(403);
expect(result).toBe('FILE_UPLOAD.ERRORS.403');
});
it('should return GENERIC key when errorCode is null', () => {
const result = component.getFileUploadErrorKey(null);
expect(result).toBe('FILE_UPLOAD.ERRORS.GENERIC');
});
it('should return GENERIC key when errorCode is undefined', () => {
const result = component.getFileUploadErrorKey(undefined);
expect(result).toBe('FILE_UPLOAD.ERRORS.GENERIC');
});
it('should return GENERIC key when errorCode is 0', () => {
const result = component.getFileUploadErrorKey(0);
expect(result).toBe('FILE_UPLOAD.ERRORS.GENERIC');
});
});
describe('Toggle Icon State', () => {
describe('onToggleMouseEnter', () => {
it('should set isToggled to true when not focused', () => {
component.toggleIconState = { isFocused: false, isToggled: false };
component.onToggleMouseEnter(component.toggleIconState);
expect(component.toggleIconState.isToggled).toBe(true);
});
it('should not change isToggled when already focused', () => {
component.toggleIconState = { isFocused: true, isToggled: false };
component.onToggleMouseEnter(component.toggleIconState);
expect(component.toggleIconState.isToggled).toBe(false);
});
});
describe('onToggleMouseLeave', () => {
it('should set isToggled to false when not focused', () => {
component.toggleIconState = { isFocused: false, isToggled: true };
component.onToggleMouseLeave(component.toggleIconState);
expect(component.toggleIconState.isToggled).toBe(false);
});
it('should not change state when focused but not toggled', () => {
component.toggleIconState = { isFocused: true, isToggled: false };
component.onToggleMouseLeave(component.toggleIconState);
expect(component.toggleIconState.isFocused).toBe(true);
expect(component.toggleIconState.isToggled).toBe(false);
});
it('should reset both isFocused and isToggled when both are true', () => {
component.toggleIconState = { isFocused: true, isToggled: true };
component.onToggleMouseLeave(component.toggleIconState);
expect(component.toggleIconState.isFocused).toBe(false);
expect(component.toggleIconState.isToggled).toBe(false);
});
});
describe('onToggleFocus', () => {
it('should set both isFocused and isToggled to true', () => {
component.toggleIconState = { isFocused: false, isToggled: false };
component.onToggleFocus(component.toggleIconState);
expect(component.toggleIconState.isFocused).toBe(true);
expect(component.toggleIconState.isToggled).toBe(true);
});
});
describe('onToggleBlur', () => {
it('should set both isFocused and isToggled to false', () => {
component.toggleIconState = { isFocused: true, isToggled: true };
component.onToggleBlur(component.toggleIconState);
expect(component.toggleIconState.isFocused).toBe(false);
expect(component.toggleIconState.isToggled).toBe(false);
});
});
describe('Multiple toggle states', () => {
it('should maintain independent state for toggleIconState and toggleIconCancelState', () => {
component.onToggleFocus(component.toggleIconState);
component.onToggleMouseEnter(component.toggleIconCancelState);
expect(component.toggleIconState.isFocused).toBe(true);
expect(component.toggleIconState.isToggled).toBe(true);
expect(component.toggleIconCancelState.isFocused).toBe(false);
expect(component.toggleIconCancelState.isToggled).toBe(true);
});
});
});
});
@@ -17,28 +17,21 @@
import { FileModel, FileUploadStatus } from '../../common/models/file.model';
import { Component, EventEmitter, Input, Output, ViewEncapsulation } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MatListModule } from '@angular/material/list';
import { FileSizePipe, IconModule } from '@alfresco/adf-core';
import { MatChipsModule } from '@angular/material/chips';
import { TranslatePipe } from '@ngx-translate/core';
import { ToggleIconDirective } from '../directives/toggle-icon.directive';
import { MatButtonModule } from '@angular/material/button';
import { FileUploadErrorPipe } from '../pipes/file-upload-error.pipe';
interface ToggleIconState {
isFocused: boolean;
isToggled: boolean;
}
@Component({
selector: 'adf-file-uploading-list-row',
imports: [
CommonModule,
IconModule,
MatListModule,
MatChipsModule,
TranslatePipe,
ToggleIconDirective,
FileSizePipe,
MatButtonModule,
FileUploadErrorPipe
],
imports: [IconModule, MatListModule, MatChipsModule, TranslatePipe, FileSizePipe, MatButtonModule],
templateUrl: './file-uploading-list-row.component.html',
styleUrls: ['./file-uploading-list-row.component.scss'],
encapsulation: ViewEncapsulation.None
@@ -50,10 +43,40 @@ export class FileUploadingListRowComponent {
@Output()
cancel = new EventEmitter<FileModel>();
toggleIconState: ToggleIconState = { isFocused: false, isToggled: false };
toggleIconCancelState: ToggleIconState = { isFocused: false, isToggled: false };
onCancel(file: FileModel): void {
this.cancel.emit(file);
}
onToggleMouseEnter(state: ToggleIconState): void {
if (!state.isFocused) {
state.isToggled = true;
}
}
onToggleMouseLeave(state: ToggleIconState): void {
if (!state.isFocused) {
state.isToggled = false;
}
if (state.isFocused && state.isToggled) {
state.isFocused = false;
state.isToggled = false;
}
}
onToggleFocus(state: ToggleIconState): void {
state.isFocused = true;
state.isToggled = true;
}
onToggleBlur(state: ToggleIconState): void {
state.isFocused = false;
state.isToggled = false;
}
showCancelledStatus(): boolean {
return (
this.file.status === FileUploadStatus.Cancelled ||
@@ -93,4 +116,8 @@ export class FileUploadingListRowComponent {
isUploadVersionComplete(): boolean {
return this.file?.status === FileUploadStatus.Complete && this.isUploadVersion();
}
getFileUploadErrorKey(errorCode?: number | null): string {
return `FILE_UPLOAD.ERRORS.${errorCode || 'GENERIC'}`;
}
}
@@ -1,6 +1,7 @@
<div class="upload-list">
@for (file of files; track file.id) {
<ng-template
ngFor
[ngForOf]="files"
[ngForTemplate]="template" />
[ngTemplateOutlet]="template"
[ngTemplateOutletContext]="{ $implicit: file }" />
}
</div>
@@ -20,11 +20,11 @@ import { UploadService } from '../../common/services/upload.service';
import { FileModel, FileUploadStatus } from '../../common/models/file.model';
import { Component, ContentChild, Input, Output, TemplateRef, EventEmitter, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { NgTemplateOutlet } from '@angular/common';
@Component({
selector: 'adf-file-uploading-list',
imports: [CommonModule],
imports: [NgTemplateOutlet],
templateUrl: './file-uploading-list.component.html',
styleUrls: ['./file-uploading-list.component.scss']
})
@@ -1,70 +1,74 @@
<form class="adf-upload-button-file-container">
<!--Files Upload-->
<div *ngIf="!uploadFolders">
<!--Single Files Upload-->
<ng-container *ngIf="!multipleFiles">
<input
class="adf-upload-button-file-container-upload-single-file"
id="upload-single-file"
data-automation-id="upload-single-file"
[type]="file ? 'button' : 'file'"
name="uploadFiles"
accept="{{acceptedFilesType}}"
[attr.disabled]="isButtonDisabled()"
[title]="tooltip"
(change)="onFilesAdded($event)"
(click)="onClickUploadButton()"/>
<label tabindex="0" (keydown.enter)="onClickUploadButton()" for="upload-single-file" class="adf-upload-button-label">
<mat-icon class="adf-upload-button-icon" adf-icon="file_upload" />
<span id="upload-single-file-label" *ngIf="!staticTitle">
{{ 'FILE_UPLOAD.BUTTON.UPLOAD_FILE' | translate }}</span>
<span id="upload-single-file-label-static" *ngIf="staticTitle">
{{ staticTitle }}</span>
</label>
</ng-container>
<!--Multiple Files Upload-->
<ng-container *ngIf="multipleFiles">
<input
id="upload-multiple-files"
data-automation-id="upload-multiple-files"
type="file"
name="uploadFiles"
multiple="multiple"
accept="{{acceptedFilesType}}"
[attr.disabled]="isButtonDisabled()"
[title]="tooltip"
(change)="onFilesAdded($event)"/>
<label for="upload-multiple-files" class="adf-upload-button-label">
<mat-icon class="adf-upload-button-icon" adf-icon="file_upload" />
<span id="upload-multiple-file-label" *ngIf="!staticTitle">
{{ 'FILE_UPLOAD.BUTTON.UPLOAD_FILE' | translate }}</span>
<span id="upload-multiple-file-label-static" *ngIf="staticTitle">
{{ staticTitle }}</span>
</label>
</ng-container>
</div>
<!--Folders Upload-->
<ng-container *ngIf="uploadFolders">
<!--Files Upload-->
@if (!uploadFolders) {
<div>
<!--Single Files Upload-->
@if (!multipleFiles) {
<input
id="uploadFolder"
data-automation-id="uploadFolder"
type="file"
name="uploadFiles"
multiple="multiple"
accept="{{acceptedFilesType}}"
webkitdirectory directory
[attr.disabled]="isButtonDisabled()"
[title]="tooltip"
(change)="onDirectoryAdded($event)"/>
<label for="uploadFolder" data-automation-id="uploadFolder" class="adf-upload-button-label">
<mat-icon class="adf-upload-button-icon" adf-icon="file_upload" />
<span id="uploadFolder-label" *ngIf="!staticTitle">
{{ 'FILE_UPLOAD.BUTTON.UPLOAD_FOLDER' | translate }}</span>
<span id="uploadFolder-label-static" *ngIf="staticTitle">
{{ staticTitle }}</span>
class="adf-upload-button-file-container-upload-single-file"
id="upload-single-file"
data-automation-id="upload-single-file"
[type]="file ? 'button' : 'file'"
name="uploadFiles"
accept="{{acceptedFilesType}}"
[attr.disabled]="isButtonDisabled()"
[title]="tooltip"
(change)="onFilesAdded($event)"
(click)="onClickUploadButton()"/>
<label tabindex="0" (keydown.enter)="onClickUploadButton()" for="upload-single-file" class="adf-upload-button-label">
<mat-icon class="adf-upload-button-icon" adf-icon="file_upload" />
@if (staticTitle) {
<span id="upload-single-file-label-static"> {{ staticTitle }}</span>
} @else {
<span id="upload-single-file-label">{{ 'FILE_UPLOAD.BUTTON.UPLOAD_FILE' | translate }}</span>
}
</label>
</ng-container>
}
<!--Multiple Files Upload-->
@if (multipleFiles) {
<input
id="upload-multiple-files"
data-automation-id="upload-multiple-files"
type="file"
name="uploadFiles"
multiple="multiple"
accept="{{acceptedFilesType}}"
[attr.disabled]="isButtonDisabled()"
[title]="tooltip"
(change)="onFilesAdded($event)"/>
<label for="upload-multiple-files" class="adf-upload-button-label">
<mat-icon class="adf-upload-button-icon" adf-icon="file_upload" />
@if (staticTitle) {
<span id="upload-multiple-file-label-static"> {{ staticTitle }}</span>
} @else {
<span id="upload-multiple-file-label">{{ 'FILE_UPLOAD.BUTTON.UPLOAD_FILE' | translate }}</span>
}
</label>
}
</div>
}
<!--Folders Upload-->
@if (uploadFolders) {
<input
id="uploadFolder"
data-automation-id="uploadFolder"
type="file"
name="uploadFiles"
multiple="multiple"
accept="{{acceptedFilesType}}"
webkitdirectory directory
[attr.disabled]="isButtonDisabled()"
[title]="tooltip"
(change)="onDirectoryAdded($event)"/>
<label for="uploadFolder" data-automation-id="uploadFolder" class="adf-upload-button-label">
<mat-icon class="adf-upload-button-icon" adf-icon="file_upload" />
@if (staticTitle) {
<span id="uploadFolder-label-static"> {{ staticTitle }}</span>
} @else {
<span id="uploadFolder-label">{{ 'FILE_UPLOAD.BUTTON.UPLOAD_FOLDER' | translate }}</span>
}
</label>
}
</form>
@@ -24,13 +24,13 @@ import { Node } from '@alfresco/js-api';
import { Subject } from 'rxjs';
import { PermissionModel } from '../../document-list/models/permissions.model';
import { UploadBase } from './base-upload/upload-base';
import { CommonModule } from '@angular/common';
import { MatButtonModule } from '@angular/material/button';
import { TranslatePipe } from '@ngx-translate/core';
@Component({
selector: 'adf-upload-button',
imports: [CommonModule, MatButtonModule, TranslatePipe, IconModule],
imports: [MatButtonModule, TranslatePipe, IconModule],
templateUrl: './upload-button.component.html',
styleUrls: ['./upload-button.component.scss'],
encapsulation: ViewEncapsulation.None
@@ -1,86 +0,0 @@
/*!
* @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, ViewChild } from '@angular/core';
import { ToggleIconDirective } from './toggle-icon.directive';
import { ComponentFixture, TestBed } from '@angular/core/testing';
@Component({
selector: 'adf-test-component',
imports: [ToggleIconDirective],
template: ` <button id="testButton" adf-toggle-icon>test</button> `
})
class TestComponent {
@ViewChild(ToggleIconDirective)
directive: ToggleIconDirective;
}
describe('ToggleIconDirective', () => {
let fixture: ComponentFixture<TestComponent>;
let component: TestComponent;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [TestComponent]
});
fixture = TestBed.createComponent(TestComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should set toggle to true on mouseenter', () => {
const button: HTMLElement = fixture.nativeElement.querySelector('#testButton');
button.dispatchEvent(new MouseEvent('mouseenter'));
expect(component.directive.isToggled).toBe(true);
});
it('should set toggle to false on mouseleave if element is not focused', () => {
const button: HTMLElement = fixture.nativeElement.querySelector('#testButton');
button.dispatchEvent(new MouseEvent('mouseleave'));
expect(component.directive.isToggled).toBe(false);
});
it('should set toggle and focus to false on mouseleave when element is focused', () => {
const button: HTMLElement = fixture.nativeElement.querySelector('#testButton');
button.dispatchEvent(new Event('focus'));
expect(component.directive.isToggled).toBe(true);
button.dispatchEvent(new MouseEvent('mouseleave'));
expect(component.directive.isToggled).toBe(false);
expect(component.directive.isFocused).toBe(false);
});
it('should set toggle and focus to true when element is focused', () => {
const button: HTMLElement = fixture.nativeElement.querySelector('#testButton');
button.dispatchEvent(new Event('focus'));
expect(component.directive.isToggled).toBe(true);
expect(component.directive.isFocused).toBe(true);
});
it('should set toggle and focus to true when element blur event', () => {
const button: HTMLElement = fixture.nativeElement.querySelector('#testButton');
button.dispatchEvent(new Event('focus'));
button.dispatchEvent(new Event('blur'));
expect(component.directive.isToggled).toBe(false);
expect(component.directive.isFocused).toBe(false);
});
});
@@ -1,62 +0,0 @@
/*!
* @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 { Directive, HostListener } from '@angular/core';
@Directive({
selector: '[adf-toggle-icon]',
exportAs: 'toggleIcon'
})
export class ToggleIconDirective {
private isFocus: boolean = false;
private toggle: boolean = false;
@HostListener('mouseenter') onMouseEnter() {
if (!this.isFocus) {
this.toggle = true;
}
}
@HostListener('mouseleave') onMouseLeave() {
if (!this.isFocus) {
this.toggle = false;
}
if (this.isFocus && this.toggle) {
this.isFocus = false;
this.toggle = false;
}
}
@HostListener('focus') onFocus() {
this.isFocus = true;
this.toggle = true;
}
@HostListener('blur') onBlur() {
this.isFocus = false;
this.toggle = false;
}
get isToggled(): boolean {
return this.toggle;
}
get isFocused(): boolean {
return this.isFocus;
}
}
@@ -1,52 +0,0 @@
/*!
* @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 { NoopTranslateModule } from '@alfresco/adf-core';
import { FileUploadErrorPipe } from './file-upload-error.pipe';
import { TestBed } from '@angular/core/testing';
describe('FileUploadErrorPipe', () => {
let pipe: FileUploadErrorPipe;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [NoopTranslateModule],
providers: [FileUploadErrorPipe]
});
pipe = TestBed.inject(FileUploadErrorPipe);
});
it('should return generic message when error code is null', () => {
expect(pipe.transform(null)).toBe('FILE_UPLOAD.ERRORS.GENERIC');
});
it('should return 500 message', () => {
expect(pipe.transform(500)).toBe('FILE_UPLOAD.ERRORS.500');
});
it('should return 504 message', () => {
expect(pipe.transform(504)).toBe('FILE_UPLOAD.ERRORS.504');
});
it('should return 403 message', () => {
expect(pipe.transform(403)).toBe('FILE_UPLOAD.ERRORS.403');
});
it('should return 404 message', () => {
expect(pipe.transform(404)).toBe('FILE_UPLOAD.ERRORS.404');
});
});
@@ -1,31 +0,0 @@
/*!
* @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 { Pipe, PipeTransform, inject } from '@angular/core';
import { TranslationService } from '@alfresco/adf-core';
@Pipe({
name: 'adfFileUploadError',
pure: true
})
export class FileUploadErrorPipe implements PipeTransform {
private readonly translation = inject(TranslationService);
transform(errorCode: number): string {
return this.translation.instant(`FILE_UPLOAD.ERRORS.${errorCode || 'GENERIC'}`);
}
}
@@ -24,9 +24,6 @@ export * from './components/file-uploading-list-row.component';
export * from './components/upload-files.event';
export * from './directives/file-draggable.directive';
export * from './directives/toggle-icon.directive';
export * from './pipes/file-upload-error.pipe';
export * from './components/base-upload/upload-base';
@@ -22,14 +22,10 @@ import { FileUploadingListComponent } from './components/file-uploading-list.com
import { UploadButtonComponent } from './components/upload-button.component';
import { UploadVersionButtonComponent } from './components/upload-version-button.component';
import { UploadDragAreaComponent } from './components/upload-drag-area.component';
import { FileUploadErrorPipe } from './pipes/file-upload-error.pipe';
import { FileDraggableDirective } from './directives/file-draggable.directive';
import { ToggleIconDirective } from './directives/toggle-icon.directive';
export const CONTENT_UPLOAD_DIRECTIVES = [
FileUploadErrorPipe,
FileDraggableDirective,
ToggleIconDirective,
UploadDragAreaComponent,
UploadButtonComponent,
UploadVersionButtonComponent,
@@ -48,6 +48,7 @@ import { AlfrescoViewerComponent } from './alfresco-viewer.component';
import { RenditionService } from '../../common/services/rendition.service';
import { NodeActionsService } from '../../document-list/services/node-actions.service';
import { provideApiTesting } from '../../testing/providers';
import { MatIconTestingModule } from '@angular/material/icon/testing';
@Component({
selector: 'adf-viewer-container-toolbar',
@@ -179,7 +180,7 @@ describe('AlfrescoViewerComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [NoopAuthModule, NoopTranslateModule, MatDialogModule, AlfrescoViewerComponent],
imports: [NoopAuthModule, MatIconTestingModule, NoopTranslateModule, MatDialogModule, AlfrescoViewerComponent],
providers: [
provideApiTesting(),
{
@@ -1,30 +0,0 @@
/*!
* @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 class DecimalNumberModel {
minIntegerDigits: number;
minFractionDigits: number;
maxFractionDigits: number;
constructor(obj?: any) {
if (obj) {
this.minIntegerDigits = obj.minIntegerDigits;
this.minFractionDigits = obj.minFractionDigits;
this.maxFractionDigits = obj.maxFractionDigits;
}
}
}
-1
View File
@@ -18,6 +18,5 @@
export * from './comment.model';
export * from './pagination.model';
export * from './request-pagination.model';
export * from './decimal-number.model';
export * from './general-user.model';
export * from './path.model';
@@ -19,7 +19,12 @@ import { DecimalPipe } from '@angular/common';
import { Pipe, PipeTransform, inject } from '@angular/core';
import { AppConfigService } from '../app-config/app-config.service';
import { UserPreferencesService } from '../common/services/user-preferences.service';
import { DecimalNumberModel } from '../models/decimal-number.model';
export interface DecimalNumberModel {
minIntegerDigits?: number;
minFractionDigits?: number;
maxFractionDigits?: number;
}
@Pipe({
name: 'adfDecimalNumber',
@@ -33,6 +33,7 @@ import { ViewerWithCustomToolbarActionsComponent } from './mock/adf-viewer-conta
import { ViewerWithCustomToolbarComponent } from './mock/adf-viewer-container-toolbar.component.mock';
import { ViewerComponent } from './viewer.component';
import { ThumbnailService } from '../../common/services/thumbnail.service';
import { MatIconTestingModule } from '@angular/material/icon/testing';
@Component({
selector: 'adf-dialog-dummy',
@@ -71,6 +72,7 @@ describe('ViewerComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
MatIconTestingModule,
ViewerWithCustomToolbarComponent,
ViewerWithCustomSidebarComponent,
ViewerWithCustomOpenWithComponent,
@@ -106,8 +108,8 @@ describe('ViewerComponent', () => {
describe('Mime Type Test', () => {
it('should mimeType change when blobFile changes', () => {
const mockSimpleChanges: SimpleChanges = {
blobFile: new SimpleChange(null, { type: 'image/png' }, true)
const mockSimpleChanges: SimpleChanges = {
blobFile: new SimpleChange(null, { type: 'image/png' }, true)
};
component.ngOnChanges(mockSimpleChanges);
@@ -117,9 +119,9 @@ describe('ViewerComponent', () => {
it('should set mimeTypeIconUrl when mimeType changes and no nodeMimeType is provided', () => {
spyOn(thumbnailService, 'getMimeTypeIcon').and.returnValue('image/png');
const mockSimpleChanges: SimpleChanges = {
mimeType: new SimpleChange(null, 'image/png', true),
nodeMimeType: undefined
const mockSimpleChanges: SimpleChanges = {
mimeType: new SimpleChange(null, 'image/png', true),
nodeMimeType: undefined
};
component.ngOnChanges(mockSimpleChanges);
@@ -130,9 +132,9 @@ describe('ViewerComponent', () => {
it('should set mimeTypeIconUrl when nodeMimeType changes', () => {
spyOn(thumbnailService, 'getMimeTypeIcon').and.returnValue('application/pdf');
const mockSimpleChanges: SimpleChanges = {
mimeType: new SimpleChange(null, 'image/png', true),
nodeMimeType: new SimpleChange(null, 'application/pdf', true)
const mockSimpleChanges: SimpleChanges = {
mimeType: new SimpleChange(null, 'image/png', true),
nodeMimeType: new SimpleChange(null, 'application/pdf', true)
};
component.ngOnChanges(mockSimpleChanges);
@@ -531,8 +533,8 @@ describe('ViewerComponent', () => {
});
it('should file name be present if is overlay mode ', async () => {
const mockSimpleChanges: SimpleChanges = {
blobFile: new SimpleChange(null, { type: 'image/png' }, true)
const mockSimpleChanges: SimpleChanges = {
blobFile: new SimpleChange(null, { type: 'image/png' }, true)
};
component.ngOnChanges(mockSimpleChanges);
fixture.detectChanges();
+275 -900
View File
File diff suppressed because it is too large Load Diff
+4 -2
View File
@@ -147,6 +147,8 @@
"nock": "13.5.5",
"nx": "22.4.1",
"prettier": "3.6.2",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"rimraf": "^6.1.2",
"sass-loader": "16.0.7",
"spdx-license-list": "^6.11.0",
@@ -155,11 +157,11 @@
"stylelint-config-standard-scss": "^13.1.0",
"ts-node": "^10.9.2",
"typescript": "5.8.3",
"webpack": "5.104.1"
"webpack": "5.105.3"
},
"overrides": {
"@storybook/angular": {
"webpack": "5.101.3"
"webpack": "5.105.3"
}
},
"license": "Apache-2.0",