Compare commits

..

No commits in common. "develop" and "8.1.0-14908201181" have entirely different histories.

62 changed files with 6442 additions and 9252 deletions

View File

@ -267,9 +267,9 @@
} }
}, },
"node_modules/undici": { "node_modules/undici": {
"version": "5.29.0", "version": "5.28.5",
"resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.5.tgz",
"integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==", "integrity": "sha512-zICwjrDrcrUE0pyyJc1I2QzBkLM8FINsgOrt6WjA+BgajVq9Nxu2PbFFXUrAggLfDXlZGZBVZYw7WNV5KiBiBA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@fastify/busboy": "^2.0.0" "@fastify/busboy": "^2.0.0"
@ -495,9 +495,9 @@
"integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==" "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg=="
}, },
"undici": { "undici": {
"version": "5.29.0", "version": "5.28.5",
"resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.5.tgz",
"integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==", "integrity": "sha512-zICwjrDrcrUE0pyyJc1I2QzBkLM8FINsgOrt6WjA+BgajVq9Nxu2PbFFXUrAggLfDXlZGZBVZYw7WNV5KiBiBA==",
"requires": { "requires": {
"@fastify/busboy": "^2.0.0" "@fastify/busboy": "^2.0.0"
} }

View File

@ -26,7 +26,7 @@ runs:
cache-dependency-path: package-lock.json cache-dependency-path: package-lock.json
- name: get latest tag sha - name: get latest tag sha
id: tag-sha id: tag-sha
uses: Alfresco/alfresco-build-tools/.github/actions/git-latest-tag@8cd8c3798c79d10540e2876ad7cf2a5311cbd1ae # v8.20.0 uses: Alfresco/alfresco-build-tools/.github/actions/git-latest-tag@247f59bac145315d38078f8954c763e0db57d5f1 # v8.18.3
# CACHE # CACHE
- name: Node Modules cache - name: Node Modules cache
id: node-modules-cache id: node-modules-cache

View File

@ -30,7 +30,7 @@ jobs:
# Initializes the CodeQL tools for scanning. # Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17 uses: github/codeql-action/init@28deaeda66b76a05916b6923827895f2b14ab387 # v3.28.16
# Override language selection by uncommenting this and choosing your languages # Override language selection by uncommenting this and choosing your languages
with: with:
languages: javascript languages: javascript
@ -39,7 +39,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below) # If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild - name: Autobuild
uses: github/codeql-action/autobuild@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17 uses: github/codeql-action/autobuild@28deaeda66b76a05916b6923827895f2b14ab387 # v3.28.16
# Command-line programs to run using the OS shell. # Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl # 📚 https://git.io/JvXDl
@ -53,4 +53,4 @@ jobs:
# make release # make release
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17 uses: github/codeql-action/analyze@28deaeda66b76a05916b6923827895f2b14ab387 # v3.28.16

View File

@ -64,7 +64,7 @@ jobs:
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Ensure SHA pinned actions - name: Ensure SHA pinned actions
uses: zgosalvez/github-actions-ensure-sha-pinned-actions@2d6823da4039243036c86d76f503c84e2ded2517 # v3.0.24 uses: zgosalvez/github-actions-ensure-sha-pinned-actions@4830be28ce81da52ec70d65c552a7403821d98d4 # v3.0.23
- name: Check package-lock.json version - name: Check package-lock.json version
run: | run: |
@ -84,10 +84,10 @@ jobs:
fetch-depth: 0 fetch-depth: 0
- name: Get branch name - name: Get branch name
uses: Alfresco/alfresco-build-tools/.github/actions/get-branch-name@8cd8c3798c79d10540e2876ad7cf2a5311cbd1ae # v8.20.0 uses: Alfresco/alfresco-build-tools/.github/actions/get-branch-name@09293790e3d482b6376a602f607e009ef1025698 # v8.19.0
- name: Save commit message - name: Save commit message
uses: Alfresco/alfresco-build-tools/.github/actions/get-commit-message@8cd8c3798c79d10540e2876ad7cf2a5311cbd1ae # v8.20.0 uses: Alfresco/alfresco-build-tools/.github/actions/get-commit-message@09293790e3d482b6376a602f607e009ef1025698 # v8.19.0
- name: ci:force flag parser - name: ci:force flag parser
shell: bash shell: bash
@ -277,7 +277,7 @@ jobs:
uses: ./.github/actions/slack-group-area uses: ./.github/actions/slack-group-area
with: with:
affected: ${{ steps.e2e-result.outputs.result }} affected: ${{ steps.e2e-result.outputs.result }}
- uses: slackapi/slack-github-action@b0fa283ad8fea605de13dc3f449259339835fc52 # v2.1.0 - uses: slackapi/slack-github-action@485a9d42d3a73031f12ec201c457e2162c45d02d # v2.0.0
name: Nofify QA failure name: Nofify QA failure
if: ${{ github.event_name == 'schedule' && contains(needs.*.result, 'failure') }} if: ${{ github.event_name == 'schedule' && contains(needs.*.result, 'failure') }}
env: env:

View File

@ -229,7 +229,7 @@ jobs:
needs: [release-storybook, release-npm, npm-check-bundle] needs: [release-storybook, release-npm, npm-check-bundle]
steps: steps:
- uses: slackapi/slack-github-action@b0fa283ad8fea605de13dc3f449259339835fc52 # v2.1.0 - uses: slackapi/slack-github-action@485a9d42d3a73031f12ec201c457e2162c45d02d # v2.0.0
name: Nofify FE eng-guild-front-end workflow failed name: Nofify FE eng-guild-front-end workflow failed
if: ${{ contains(toJson(needs.*.result), 'failure') }} if: ${{ contains(toJson(needs.*.result), 'failure') }}
env: env:

2
.nvmrc
View File

@ -1 +1 @@
22.14.0 20.18.1

View File

@ -8,6 +8,6 @@
"source": "/**/**/i18n/en.json", "source": "/**/**/i18n/en.json",
"translation": "/%original_path%/%two_letters_code%.%file_extension%", "translation": "/%original_path%/%two_letters_code%.%file_extension%",
"export_only_approved": "true", "export_only_approved": "true",
"update_option": "update_without_changes" "update_option": "update_as_unapproved"
} }
] ]

View File

@ -4,18 +4,16 @@ Added: v4.1.0
--- ---
## Form Extensibility for APA Form Widget ## Form Extensibility for APA Form Widget
This page describes how you can customize ADF forms to your own specification. This page describes how you can customize ADF forms to your own specification.
## Contents ## Contents
There are two ways to customize the form There are two ways to customize the form
- [Replace default form widgets with custom components](#replace-default-form-widgets-with-apa-form-widgets) - [Replace default form widgets with custom components](#replace-default-form-widgets-with-apa-form-widgets)
- [Replace custom form widget with custom components](#replace-custom-form-widgets-with-custom-components) - [Replace custom form widget with custom components](#replace-custom-form-widgets-with-custom-components)
## Replace default form widgets with APA form widgets ## Replace default form widgets with APA form widgets
This is an example of replacing the standard `Text` with a custom component for all APA forms rendered within the `<adf-form>` component. This is an example of replacing the standard `Text` [widget](../../lib/testing/src/lib/core/pages/form/widgets/widget.ts) with a custom component for all APA forms rendered within the `<adf-form>` component.
1. Create a simple form with some `Text` widgets: 1. Create a simple form with some `Text` widgets:
@ -26,10 +24,8 @@ This is an example of replacing the standard `Text` with a custom component for
```ts ```ts
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { WidgetComponent } from '@alfresco/adf-core'; import { WidgetComponent } from '@alfresco/adf-core';
@Component({ @Component({
selector: 'custom-editor', selector: 'custom-editor',
standalone: true,
template: ` template: `
<div style="color: red">Look, I'm a APA custom editor!</div> <div style="color: red">Look, I'm a APA custom editor!</div>
` `
@ -37,14 +33,40 @@ This is an example of replacing the standard `Text` with a custom component for
export class CustomEditorComponent extends WidgetComponent {} export class CustomEditorComponent extends WidgetComponent {}
``` ```
2. Import the [`FormRenderingService`](../core/services/form-rendering.service.md) in the feature module, or application module (recommended: `ProcessServicesExtensionModule`), and override the default mapping: 2. Add it to the application module or any custom module that is imported into the application one:
```ts ```ts
import { NgModule } from '@angular/core';
import { CustomEditorComponent } from './custom-editor.component'; import { CustomEditorComponent } from './custom-editor.component';
import { FormRenderingService } from '@alfresco/adf-core'; @NgModule({
declarations: [ CustomEditorComponent ],
@NgModule({...}) exports: [ CustomEditorComponent ]
export class ProcessServicesExtensionModule { })
export class CustomEditorsModule {}
```
3. Every custom widget component should be added into the the collections `declarations` and `exports`. If you decided to store custom widgets in a separate dedicated module (and optionally as a separate re-distributable library) don't forget to import it into the main application:
```ts
@NgModule({
imports: [
// ...
CustomEditorsModule
// ...
],
providers: [],
bootstrap: [ AppComponent ]
})
export class AppModule {}
```
4. Import the [`FormRenderingService`](../core/services/form-rendering.service.md) into any of your Views and override the default mapping, for example:
```ts
import { Component } from '@angular/core';
import { CustomEditorComponent } from './custom-editor.component';
@Component({...})
export class MyView {
constructor(formRenderingService: FormRenderingService) { constructor(formRenderingService: FormRenderingService) {
this.formRenderingService.register({ this.formRenderingService.register({
'text': () => CustomEditorComponent 'text': () => CustomEditorComponent
@ -53,12 +75,10 @@ This is an example of replacing the standard `Text` with a custom component for
} }
``` ```
> [!IMPORTANT] 5. At runtime the form should look similar to the following:
> The widget should be registered outside the custom widget component, otherwise the widget will not be registered correctly.
At runtime the form should look similar to the following: ![custom text widget](../docassets/images/apa-simple-override-form.png)
![custom text widget](../docassets/images/apa-simple-override-form.png)
## Replace custom form widgets with custom components ## Replace custom form widgets with custom components
@ -85,30 +105,54 @@ When displayed in a task, the field will look similar to the following:
To render the missing content: To render the missing content:
1. Create a standalone Angular component: 1. Create an Angular component:
```ts ```ts
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { WidgetComponent } from '@alfresco/adf-core'; import { WidgetComponent } from '@alfresco/adf-core';
@Component({ @Component({
selector: 'app-demo-widget', selector: 'app-demo-widget',
standalone: true,
template: `<div style="color: green">ADF version of custom form widget</div>` template: `<div style="color: green">ADF version of custom form widget</div>`
}) })
export class DemoWidgetComponent extends WidgetComponent {} export class DemoWidgetComponent extends WidgetComponent {}
``` ```
2. Import the [`FormRenderingService`](../core/services/form-rendering.service.md) in the feature module, or application module (recommended: `ProcessServicesExtensionModule`), and override the default mapping: 2. Place it inside the custom module:
```ts ```ts
import { NgModule } from '@angular/core';
import { DemoWidgetComponent } from './demo-widget.component'; import { DemoWidgetComponent } from './demo-widget.component';
import { FormRenderingService } from '@alfresco/adf-core'; @NgModule({
declarations: [ DemoWidgetComponent ],
@NgModule({/*...*/}) exports: [ DemoWidgetComponent ]
export class ProcessServicesExtensionModule { })
export class CustomWidgetsModule {}
```
3. Import it into your Application Module:
```ts
@NgModule({
imports: [
// ...
CustomWidgetsModule
// ...
],
providers: [],
bootstrap: [ AppComponent ]
})
export class AppModule {}
```
4. Import the [`FormRenderingService`](../core/services/form-rendering.service.md) in any of your Views and provide the new mapping:
```ts
import { Component } from '@angular/core';
import { DemoWidgetComponent } from './demo-widget.component';
@Component({...})
export class MyView {
constructor(formRenderingService: FormRenderingService) { constructor(formRenderingService: FormRenderingService) {
formRenderingService.register({ this.formRenderingService.register({
'custom-editor': () => DemoWidgetComponent 'custom-editor': () => DemoWidgetComponent
}); });
} }
@ -119,9 +163,6 @@ At runtime you should now see your custom Angular component rendered in place of
![adf form widget runtime](../docassets/images/apa-resolved-widget.png) ![adf form widget runtime](../docassets/images/apa-resolved-widget.png)
> [!IMPORTANT]
> The widget should be registered outside the custom widget component, otherwise the widget will not be registered correctly.
## See Also ## See Also
- [Extensibility](./extensibility.md) - [Extensibility](./extensibility.md)

View File

@ -9,7 +9,7 @@
"version": "8.0.0", "version": "8.0.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@alfresco/js-api": ">=8.0.0-alpha.7", "@alfresco/js-api": ">=8.0.0-alpha.7-0",
"commander": "^6.2.1", "commander": "^6.2.1",
"ejs": "^3.1.9", "ejs": "^3.1.9",
"license-checker": "^25.0.1", "license-checker": "^25.0.1",
@ -39,27 +39,6 @@
"tslib": "^2.6.1" "tslib": "^2.6.1"
} }
}, },
"node_modules/@noble/hashes": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz",
"integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==",
"license": "MIT",
"engines": {
"node": "^14.21.3 || >=16"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@paralleldrive/cuid2": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.2.2.tgz",
"integrity": "sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==",
"license": "MIT",
"dependencies": {
"@noble/hashes": "^1.1.5"
}
},
"node_modules/@types/ejs": { "node_modules/@types/ejs": {
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.1.2.tgz", "resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.1.2.tgz",
@ -456,18 +435,14 @@
} }
}, },
"node_modules/formidable": { "node_modules/formidable": {
"version": "3.5.4", "version": "3.5.1",
"resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz", "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.1.tgz",
"integrity": "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==", "integrity": "sha512-WJWKelbRHN41m5dumb0/k8TeAx7Id/y3a+Z7QfhxP/htI9Js5zYaEDtG8uMgG0vM0lOlqnmjE99/kfpOYi/0Og==",
"license": "MIT",
"dependencies": { "dependencies": {
"@paralleldrive/cuid2": "^2.2.2",
"dezalgo": "^1.0.4", "dezalgo": "^1.0.4",
"hexoid": "^1.0.0",
"once": "^1.4.0" "once": "^1.4.0"
}, },
"engines": {
"node": ">=14.0.0"
},
"funding": { "funding": {
"url": "https://ko-fi.com/tunnckoCore/commissions" "url": "https://ko-fi.com/tunnckoCore/commissions"
} }
@ -601,6 +576,14 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/hexoid": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz",
"integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==",
"engines": {
"node": ">=8"
}
},
"node_modules/hosted-git-info": { "node_modules/hosted-git-info": {
"version": "2.8.9", "version": "2.8.9",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",

View File

@ -711,10 +711,10 @@
}, },
"NODE_FAVORITE_DIRECTIVE": { "NODE_FAVORITE_DIRECTIVE": {
"MESSAGES": { "MESSAGES": {
"NODE_ADDED": "Añadido {{ name }} a favoritos", "NODE_ADDED": "Added {{ name }} to favorites",
"NODES_ADDED": "Añadidos {{ number }} elementos a favoritos", "NODES_ADDED": "Added {{ number }} items to favorites",
"NODE_REMOVED": "Eliminado {{ name }} de favoritos", "NODE_REMOVED": "Removed {{ name }} from favorites",
"NODES_REMOVED": "Eliminado {{ number }} elementos de favoritos" "NODES_REMOVED": "Removed {{ number }} items from favorites"
} }
} }
} }

View File

@ -711,10 +711,10 @@
}, },
"NODE_FAVORITE_DIRECTIVE": { "NODE_FAVORITE_DIRECTIVE": {
"MESSAGES": { "MESSAGES": {
"NODE_ADDED": "Ajout de {{ name }} aux favoris", "NODE_ADDED": "Added {{ name }} to favorites",
"NODES_ADDED": "Ajout des éléments {{ number }} aux favoris", "NODES_ADDED": "Added {{ number }} items to favorites",
"NODE_REMOVED": "Suppression de {{ name }} des favoris", "NODE_REMOVED": "Removed {{ name }} from favorites",
"NODES_REMOVED": "Les éléments {{ number }} supprimés des favoris" "NODES_REMOVED": "Removed {{ number }} items from favorites"
} }
} }
} }

View File

@ -711,10 +711,10 @@
}, },
"NODE_FAVORITE_DIRECTIVE": { "NODE_FAVORITE_DIRECTIVE": {
"MESSAGES": { "MESSAGES": {
"NODE_ADDED": "Aggiunto {{ name }} ai preferiti", "NODE_ADDED": "Added {{ name }} to favorites",
"NODES_ADDED": "Aggiunti {{ number }} elementi ai preferiti", "NODES_ADDED": "Added {{ number }} items to favorites",
"NODE_REMOVED": "Rimosso {{ name }} dai preferiti", "NODE_REMOVED": "Removed {{ name }} from favorites",
"NODES_REMOVED": "Rimosso {{ number }} elementi dai preferiti" "NODES_REMOVED": "Removed {{ number }} items from favorites"
} }
} }
} }

View File

@ -711,10 +711,10 @@
}, },
"NODE_FAVORITE_DIRECTIVE": { "NODE_FAVORITE_DIRECTIVE": {
"MESSAGES": { "MESSAGES": {
"NODE_ADDED": "Dodano {{ name }} do ulubionych", "NODE_ADDED": "Added {{ name }} to favorites",
"NODES_ADDED": "Dodano {{ number }} elementy do ulubionych", "NODES_ADDED": "Added {{ number }} items to favorites",
"NODE_REMOVED": "Usunięto {{ name }} z ulubionych", "NODE_REMOVED": "Removed {{ name }} from favorites",
"NODES_REMOVED": "Usunięto {{ number }} elementy z ulubionych" "NODES_REMOVED": "Removed {{ number }} items from favorites"
} }
} }
} }

View File

@ -711,10 +711,10 @@
}, },
"NODE_FAVORITE_DIRECTIVE": { "NODE_FAVORITE_DIRECTIVE": {
"MESSAGES": { "MESSAGES": {
"NODE_ADDED": "Adicionado {{ name }} aos favoritos", "NODE_ADDED": "Added {{ name }} to favorites",
"NODES_ADDED": "Adicionados {{ number }} itens aos favoritos", "NODES_ADDED": "Added {{ number }} items to favorites",
"NODE_REMOVED": "Removido {{ name }} dos favoritos", "NODE_REMOVED": "Removed {{ name }} from favorites",
"NODES_REMOVED": "Removido {{ number }} itens dos favoritos" "NODES_REMOVED": "Removed {{ number }} items from favorites"
} }
} }
} }

View File

@ -2,15 +2,15 @@
@use '@angular/material' as mat; @use '@angular/material' as mat;
@mixin adf-breadcrumb-theme($theme) { @mixin adf-breadcrumb-theme($theme) {
$config: mat.m2-get-color-config($theme); $config: mat.get-color-config($theme);
$foreground-palette: map.get($config, foreground); $foreground-palette: map.get($config, foreground);
$primary-palette: map.get($config, primary); $primary-palette: map.get($config, primary);
$text-color: mat.m2-get-color-from-palette($foreground-palette, text); $text-color: mat.get-color-from-palette($foreground-palette, text);
$primary: mat.m2-get-color-from-palette($primary-palette, text); $primary: mat.get-color-from-palette($primary-palette, text);
adf-breadcrumb { adf-breadcrumb {
.adf-breadcrumb__show-all-button-icon--rotate { .adf-breadcrumb__show-all-button-icon--rotate {
color: mat.m2-get-color-from-palette($primary-palette, 500); color: mat.get-color-from-palette($primary-palette, 500);
} }
.adf-breadcrumb__item-wrapper { .adf-breadcrumb__item-wrapper {

View File

@ -1,3 +0,0 @@
# DEPRECATION WARNING
The custom theme is deprecated and will be removed in the future. To generate a custom theme, please refer to the Angular Material [Custom Theme section](https://v18.material.angular.dev/guide/theming#custom-theme).

View File

@ -2,19 +2,22 @@
@use '@angular/material' as mat; @use '@angular/material' as mat;
@import './theme/theme-data'; @import './theme/theme-data';
$custom-theme: mat.m2-define-light-theme( $custom-theme: mat.define-light-theme(
( (
color: ( color: (
primary: map.get($palettes, primary), primary: map.get($palettes, primary),
accent: map.get($palettes, accent), accent: map.get($palettes, accent),
warn: map.get($palettes, warning) warn: map.get($palettes, warning),
), ),
typography: $app-typography typography: $app-typography,
) )
); );
@if $background-color { @if $background-color {
$custom-theme: get-custom-background-color($background-color, $custom-theme); $custom-theme: get-custom-background-color(
$background-color,
$custom-theme
);
} }
@if $text-color { @if $text-color {

View File

@ -1,27 +1,31 @@
@use '@angular/material' as mat; @use '@angular/material' as mat;
@import './default-colors'; @import './default-colors.scss';
@import './custom-palette-creator'; @import './custom-palette-creator.scss';
@function get-mat-palettes($primary-color, $accent-color) { @function get-mat-palettes($primary-color, $accent-color) {
$mat-primary-palette: null; $mat-primary-palette: null;
@if ($primary-color) { @if ($primary-color) {
$custom-theme-primary-palette: create-color-palette($primary-color, 'primary'); $custom-theme-primary-palette: create-color-palette($primary-color, 'primary');
$mat-primary-palette: mat.m2-define-palette($custom-theme-primary-palette, 500); $mat-primary-palette: mat.define-palette($custom-theme-primary-palette, 500);
} @else { } @else {
$mat-primary-palette: mat.m2-define-palette($default-primary, A100); $mat-primary-palette: mat.define-palette($default-primary, A100);
} }
$mat-accent-palette: null; $mat-accent-palette: null;
@if ($accent-color) { @if ($accent-color) {
$custom-theme-accent-palette: create-color-palette($accent-color, 'accent'); $custom-theme-accent-palette: create-color-palette($accent-color, 'accent');
$mat-accent-palette: mat.m2-define-palette($custom-theme-accent-palette, 500); $mat-accent-palette: mat.define-palette($custom-theme-accent-palette, 500);
} @else { } @else {
$mat-accent-palette: mat.m2-define-palette($default-accent); $mat-accent-palette: mat.define-palette($default-accent);
} }
$mat-warn-palette: mat.m2-define-palette($default-warn, A100); $mat-warn-palette: mat.define-palette($default-warn, A100);
@return (primary: $mat-primary-palette, accent: $mat-accent-palette, warning: $mat-warn-palette); @return (
primary: $mat-primary-palette,
accent: $mat-accent-palette,
warning: $mat-warn-palette,
);
} }

View File

@ -3,37 +3,37 @@
@import '../variables/font-family'; @import '../variables/font-family';
@function get-mat-typography($base-font-size, $font-family) { @function get-mat-typography($base-font-size, $font-family) {
$custom-typography: mat.m2-define-typography-config( $custom-typography: mat.define-typography-config(
$font-family: 'Muli, Roboto, "Helvetica Neue", sans-serif', $font-family: 'Muli, Roboto, "Helvetica Neue", sans-serif',
$headline-1: mat.m2-define-typography-level(112px, 112px, 300), $headline-1: mat.define-typography-level(112px, 112px, 300),
$headline-2: mat.m2-define-typography-level(56px, 56px, 400), $headline-2: mat.define-typography-level(56px, 56px, 400),
$headline-3: mat.m2-define-typography-level(45px, 48px, 400), $headline-3: mat.define-typography-level(45px, 48px, 400),
$headline-4: mat.m2-define-typography-level(34px, 40px, 400), $headline-4: mat.define-typography-level(34px, 40px, 400),
$headline-5: mat.m2-define-typography-level(24px, 32px, 400), $headline-5: mat.define-typography-level(24px, 32px, 400),
$headline-6: mat.m2-define-typography-level(20px, 32px, 500), $headline-6: mat.define-typography-level(20px, 32px, 500),
$subtitle-1: mat.m2-define-typography-level(16px, 28px, 400), $subtitle-1: mat.define-typography-level(16px, 28px, 400),
$body-1: mat.m2-define-typography-level(15px, 24px, 400), $body-1: mat.define-typography-level(15px, 24px, 400),
$subtitle-2: mat.m2-define-typography-level(14px, 24px, 500), $subtitle-2: mat.define-typography-level(14px, 24px, 500),
$body-2: mat.m2-define-typography-level(14px, 20px, 400), $body-2: mat.define-typography-level(14px, 20px, 400),
$caption: mat.m2-define-typography-level(12px, 20px, 400), $caption: mat.define-typography-level(12px, 20px, 400),
$button: mat.m2-define-typography-level(14px, 14px, 500), $button: mat.define-typography-level(14px, 14px, 500),
// Line-height must be unit-less fraction of the font-size. // Line-height must be unit-less fraction of the font-size.
); );
@if $base-font-size { @if $base-font-size {
$custom-typography: mat.m2-define-typography-config( $custom-typography: mat.define-typography-config(
$headline-1: mat.m2-define-typography-level(8rem, 8rem, 300), $headline-1: mat.define-typography-level(8rem, 8rem, 300),
$headline-2: mat.m2-define-typography-level(4rem, 4rem, 400), $headline-2: mat.define-typography-level(4rem, 4rem, 400),
$headline-3: mat.m2-define-typography-level(3.21rem, 3.21rem, 400), $headline-3: mat.define-typography-level(3.21rem, 3.21rem, 400),
$headline-4: mat.m2-define-typography-level(2.42rem, 2.85rem, 400), $headline-4: mat.define-typography-level(2.42rem, 2.85rem, 400),
$headline-5: mat.m2-define-typography-level(1.71rem, 2.28rem, 400), $headline-5: mat.define-typography-level(1.71rem, 2.28rem, 400),
$headline-6: mat.m2-define-typography-level(1.42rem, 2.28rem, 500), $headline-6: mat.define-typography-level(1.42rem, 2.28rem, 500),
$subtitle-1: mat.m2-define-typography-level(1.14rem, 2rem, 400), $subtitle-1: mat.define-typography-level(1.14rem, 2rem, 400),
$body-1: mat.m2-define-typography-level(1.07rem, 1.71rem, 400), $body-1: mat.define-typography-level(1.07rem, 1.71rem, 400),
$subtitle-2: mat.m2-define-typography-level(1rem, 1.71rem, 500), $subtitle-2: mat.define-typography-level(1rem, 1.71rem, 500),
$body-2: mat.m2-define-typography-level(1rem, 1.42rem, 400), $body-2: mat.define-typography-level(1rem, 1.42rem, 400),
$caption: mat.m2-define-typography-level(0.86rem, 1.42rem, 400), $caption: mat.define-typography-level(0.86rem, 1.42rem, 400),
$button: mat.m2-define-typography-level(1rem, 1rem, 500), $button: mat.define-typography-level(1rem, 1rem, 500),
$font-family: $default-font-family $font-family: $default-font-family
); );
} }

View File

@ -1,123 +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 { AdfHttpClient } from '@alfresco/adf-core/api';
import { StorageService } from '../common';
import { StoragePrefixFactory } from './app-config-storage-prefix.factory';
import { loadAppConfig } from './app-config.loader';
import { AppConfigService, AppConfigValues } from './app-config.service';
import { fakeAsync, TestBed, tick } from '@angular/core/testing';
import { HttpClientModule } from '@angular/common/http';
import { of } from 'rxjs';
describe('loadAppConfig', () => {
let appConfigServiceSpy: jasmine.SpyObj<AppConfigService>;
let storageServiceSpy: jasmine.SpyObj<StorageService>;
let adfHttpClientSpy: jasmine.SpyObj<AdfHttpClient>;
let storagePrefixFactorySpy: jasmine.SpyObj<StoragePrefixFactory>;
let appConfigGetSpy: jasmine.Spy;
let appConfigLoadSpy: jasmine.Spy;
let factoryFunction: () => void;
beforeEach(() => {
adfHttpClientSpy = jasmine.createSpyObj('AdfHttpClient', ['setDefaultSecurityOption']);
storagePrefixFactorySpy = jasmine.createSpyObj('StoragePrefixFactory', ['getPrefix']);
TestBed.configureTestingModule({
imports: [HttpClientModule],
providers: [
{ provide: AppConfigService },
{ provide: StorageService },
{
provide: AdfHttpClient,
useValue: adfHttpClientSpy
},
{ provide: StoragePrefixFactory, useValue: storagePrefixFactorySpy }
]
});
appConfigServiceSpy = TestBed.inject(AppConfigService) as jasmine.SpyObj<AppConfigService>;
appConfigGetSpy = spyOn(appConfigServiceSpy, 'get');
appConfigLoadSpy = spyOn(appConfigServiceSpy, 'load');
appConfigLoadSpy.and.callFake((callback: () => void) => {
callback();
});
storageServiceSpy = TestBed.inject(StorageService) as jasmine.SpyObj<StorageService>;
spyOnProperty(storageServiceSpy, 'prefix', 'get').and.callThrough();
storagePrefixFactorySpy.getPrefix.and.returnValue({ subscribe: () => {} } as any);
factoryFunction = loadAppConfig(appConfigServiceSpy, storageServiceSpy, adfHttpClientSpy, storagePrefixFactorySpy);
});
it('should disable CSRF based on app config', () => {
appConfigGetSpy.and.callFake((key: string): any => {
if (key === AppConfigValues.DISABLECSRF) {
return true;
}
});
factoryFunction();
expect(appConfigServiceSpy.get).toHaveBeenCalledWith('disableCSRF', true);
expect(adfHttpClientSpy.disableCsrf).toBeTrue();
});
it('should set default security option when auth.withCredentials is defined', () => {
appConfigServiceSpy.get.and.callFake((key: string): any => {
if (key === AppConfigValues.AUTH_WITH_CREDENTIALS) {
return true;
}
});
factoryFunction();
expect(adfHttpClientSpy.setDefaultSecurityOption).toHaveBeenCalledWith({ withCredentials: true });
});
it('should set storage prefix from app config', () => {
appConfigServiceSpy.get.and.callFake((key: string, _default): any => {
if (key === AppConfigValues.STORAGE_PREFIX) {
return 'test-prefix';
}
});
factoryFunction();
expect(appConfigServiceSpy.get).toHaveBeenCalledWith(AppConfigValues.STORAGE_PREFIX, '');
expect(storageServiceSpy.prefix).toEqual('test-prefix_');
});
it('should update storage prefix from storagePrefixFactory', fakeAsync(() => {
storagePrefixFactorySpy.getPrefix.and.returnValue(of('new-amazing-prefix'));
factoryFunction();
tick();
expect(storagePrefixFactorySpy.getPrefix).toHaveBeenCalled();
expect(storageServiceSpy.prefix).toEqual('new-amazing-prefix_');
}));
it('should call appConfigService.load with the init function', () => {
factoryFunction();
expect(appConfigLoadSpy).toHaveBeenCalledWith(jasmine.any(Function));
});
});

View File

@ -37,11 +37,6 @@ export function loadAppConfig(
) { ) {
const init = () => { const init = () => {
adfHttpClient.disableCsrf = appConfigService.get<boolean>(AppConfigValues.DISABLECSRF, true); adfHttpClient.disableCsrf = appConfigService.get<boolean>(AppConfigValues.DISABLECSRF, true);
const withCredentials = appConfigService.get<boolean>(AppConfigValues.AUTH_WITH_CREDENTIALS);
if (withCredentials !== undefined && withCredentials !== null) {
adfHttpClient.setDefaultSecurityOption({ withCredentials });
}
storageService.prefix = appConfigService.get<string>(AppConfigValues.STORAGE_PREFIX, ''); storageService.prefix = appConfigService.get<string>(AppConfigValues.STORAGE_PREFIX, '');
storagePrefixFactory.getPrefix().subscribe((property) => { storagePrefixFactory.getPrefix().subscribe((property) => {

View File

@ -78,7 +78,6 @@ describe('DateFnsUtils', () => {
it('should parse alternative ISO datetime', () => { it('should parse alternative ISO datetime', () => {
const result = DateFnsUtils.parseDate('1982-03-13T10:00:000Z', `yyyy-MM-dd'T'HH:mm:sssXXX`); const result = DateFnsUtils.parseDate('1982-03-13T10:00:000Z', `yyyy-MM-dd'T'HH:mm:sssXXX`);
expect(result.toISOString()).toBe('1982-03-13T10:00:00.000Z'); expect(result.toISOString()).toBe('1982-03-13T10:00:00.000Z');
}); });

View File

@ -4,9 +4,9 @@
<div *ngIf="hasTabs()" class="alfresco-tabs-widget"> <div *ngIf="hasTabs()" class="alfresco-tabs-widget">
<mat-tab-group> <mat-tab-group>
<mat-tab *ngFor="let tab of visibleTabs()" [label]="tab.title | translate "> <mat-tab *ngFor="let tab of visibleTabs()" [label]="tab.title | translate ">
<ng-container class="adf-form-tab-content"> <div class="adf-form-tab-content">
<ng-template *ngTemplateOutlet="render; context: { fieldToRender: tab.fields }" /> <ng-template *ngTemplateOutlet="render; context: { fieldToRender: tab.fields }" />
</ng-container> </div>
</mat-tab> </mat-tab>
</mat-tab-group> </mat-tab-group>
</div> </div>

View File

@ -15,9 +15,11 @@
.alfresco-tabs-widget { .alfresco-tabs-widget {
width: 100%; width: 100%;
position: absolute;
.adf-form-tab-group { .adf-form-tab-group {
width: 100%; width: 100%;
z-index: 9999;
} }
#{ms.$mat-tab-body} { #{ms.$mat-tab-body} {
@ -28,22 +30,21 @@
z-index: 10; z-index: 10;
margin: 0; margin: 0;
background-color: white; background-color: white;
position: absolute; position: sticky;
width: 96%; /* stylelint-disable-next-line value-no-vendor-prefix */
position: -webkit-sticky; /* macOS/iOS Safari */
/* stylelint-disable-next-line declaration-no-important */ /* stylelint-disable-next-line declaration-no-important */
margin-left: 0 !important; margin-left: 0 !important;
/* stylelint-disable-next-line declaration-no-important */
margin-right: 10px !important;
top: 0; top: 0;
} }
#{ms.$mat-tab-body-wrapper} { #{ms.$mat-tab-body-wrapper} {
padding-top: 5%; padding-top: 16px;
} }
} }
.mat-mdc-card-content:first-child { .mat-mdc-card-content:first-child {
padding-top: 1em; padding-top: 0;
} }
.adf-container-widget { .adf-container-widget {

View File

@ -146,7 +146,7 @@ describe('FormFieldModel', () => {
}); });
expect(field.options).toEqual([{ id: 'id_one', name: 'One' }]); expect(field.options).toEqual([{ id: 'id_one', name: 'One' }]);
expect(field.value).toEqual({ id: 'id_one', name: 'One' }); expect(field.value).toEqual('id_one');
}); });
it('should add value (selected options) to field options if NOT present (multiple selection)', () => { it('should add value (selected options) to field options if NOT present (multiple selection)', () => {
@ -176,7 +176,7 @@ describe('FormFieldModel', () => {
expect(field.hasEmptyValue).toBe(true); expect(field.hasEmptyValue).toBe(true);
expect(field.emptyOption).toEqual({ id: 'empty', name: 'Chose one...' }); expect(field.emptyOption).toEqual({ id: 'empty', name: 'Chose one...' });
expect(field.value).toEqual({ id: 'empty', name: 'Chose one...' }); expect(field.value).toEqual('empty');
}); });
it('should set hasEmptyValue to true if "empty" option is present in options', () => { it('should set hasEmptyValue to true if "empty" option is present in options', () => {
@ -272,8 +272,7 @@ describe('FormFieldModel', () => {
options: [], options: [],
value: { id: 'delayed-option-id', name: 'Delayed option' } value: { id: 'delayed-option-id', name: 'Delayed option' }
}); });
expect(field.value).toBe('delayed-option-id');
expect(field.value).toEqual({ id: 'delayed-option-id', name: 'Delayed option' });
}); });
}); });
}); });
@ -771,7 +770,7 @@ describe('FormFieldModel', () => {
]; ];
}); });
it('should update form with selected option and options from which we chose when is a string', () => { it('should update form with selected option and options from which we chose', () => {
field.value = 'restOpt2'; field.value = 'restOpt2';
field.updateForm(); field.updateForm();
@ -1024,7 +1023,7 @@ describe('FormFieldModel', () => {
expect(field.options).toEqual(staticOptions); expect(field.options).toEqual(staticOptions);
}); });
it('should selected option appear in form values string', () => { it('should selected option appear in form values', () => {
const field = getFieldConfig('manual', staticOptions, 'opt2'); const field = getFieldConfig('manual', staticOptions, 'opt2');
field.updateForm(); field.updateForm();
@ -1032,15 +1031,6 @@ describe('FormFieldModel', () => {
expect(field.value).toEqual('opt2'); expect(field.value).toEqual('opt2');
expect(field.form.values['dropdown_field']).toEqual({ id: 'opt2', name: 'Option 2' }); expect(field.form.values['dropdown_field']).toEqual({ id: 'opt2', name: 'Option 2' });
}); });
it('should selected option appear in form values obj', () => {
const field = getFieldConfig('manual', staticOptions, { id: 'opt3', name: 'opt3' });
field.updateForm();
expect(field.value).toEqual({ id: 'opt3', name: 'opt3' });
expect(field.form.values['dropdown_field']).toEqual({ id: 'opt3', name: 'opt3' });
});
}); });
describe('radio buttons field', () => { describe('radio buttons field', () => {

View File

@ -331,13 +331,13 @@ export class FormFieldModel extends FormWidgetModel {
const isEmptyValue = !value || [this.emptyOption.id, this.emptyOption.name].includes(value); const isEmptyValue = !value || [this.emptyOption.id, this.emptyOption.name].includes(value);
if (isEmptyValue) { if (isEmptyValue) {
return this.emptyOption; return this.emptyOption.id;
} }
} }
if (this.isValidOption(value)) { if (this.isValidOption(value)) {
this.addOption({ id: value.id, name: value.name }); this.addOption({ id: value.id, name: value.name });
return value; return value.id;
} }
if (this.hasMultipleValues) { if (this.hasMultipleValues) {
@ -436,17 +436,6 @@ export class FormFieldModel extends FormWidgetModel {
this.form.values[this.id] = matchingOption || null; this.form.values[this.id] = matchingOption || null;
} }
if (typeof this.value === 'object') {
if (this.value.id === 'empty' || this.value.id === '') {
this.form.values[this.id] = null;
break;
}
const matchingOption: FormFieldOption = this.options.find((opt) => opt.id === this.value.id);
this.form.values[this.id] = matchingOption;
}
break; break;
} }
case FormFieldTypes.RADIO_BUTTONS: { case FormFieldTypes.RADIO_BUTTONS: {

View File

@ -51,7 +51,7 @@
"REST_API_FAILED": "No se puede acceder al servidor '{{ hostname }}'", "REST_API_FAILED": "No se puede acceder al servidor '{{ hostname }}'",
"VARIABLE_DROPDOWN_OPTIONS_FAILED": "Ha habido un problema al cargar elementos desplegables. Contacte con el administrador.", "VARIABLE_DROPDOWN_OPTIONS_FAILED": "Ha habido un problema al cargar elementos desplegables. Contacte con el administrador.",
"DATA_TABLE_LOAD_FAILED": "Ha habido un problema al cargar elementos de tabla. Contacte con el administrador.", "DATA_TABLE_LOAD_FAILED": "Ha habido un problema al cargar elementos de tabla. Contacte con el administrador.",
"DATA_TABLE_EMPTY_CONTENT": "No se encontraron datos", "DATA_TABLE_EMPTY_CONTENT": "No data found",
"EXTERNAL_PROPERTY_LOAD_FAILED": "Ha habido un problema al cargar una propiedad externa. Contacte con el administrador.", "EXTERNAL_PROPERTY_LOAD_FAILED": "Ha habido un problema al cargar una propiedad externa. Contacte con el administrador.",
"FILE_NAME": "Nombre del fichero", "FILE_NAME": "Nombre del fichero",
"TITLE": "Título", "TITLE": "Título",

View File

@ -51,7 +51,7 @@
"REST_API_FAILED": "Le serveur '{{ hostname }}' n'est pas accessible", "REST_API_FAILED": "Le serveur '{{ hostname }}' n'est pas accessible",
"VARIABLE_DROPDOWN_OPTIONS_FAILED": "Un problème est survenu lors du chargement des éléments de la liste déroulante. Veuillez contacter un administrateur.", "VARIABLE_DROPDOWN_OPTIONS_FAILED": "Un problème est survenu lors du chargement des éléments de la liste déroulante. Veuillez contacter un administrateur.",
"DATA_TABLE_LOAD_FAILED": "Un problème est survenu lors du chargement des éléments du tableau. Veuillez contacter un administrateur.", "DATA_TABLE_LOAD_FAILED": "Un problème est survenu lors du chargement des éléments du tableau. Veuillez contacter un administrateur.",
"DATA_TABLE_EMPTY_CONTENT": "Données introuvables", "DATA_TABLE_EMPTY_CONTENT": "No data found",
"EXTERNAL_PROPERTY_LOAD_FAILED": "Un problème est survenu lors du chargement de la propriété externe. Veuillez contacter un administrateur.", "EXTERNAL_PROPERTY_LOAD_FAILED": "Un problème est survenu lors du chargement de la propriété externe. Veuillez contacter un administrateur.",
"FILE_NAME": "Nom de fichier", "FILE_NAME": "Nom de fichier",
"TITLE": "Titre", "TITLE": "Titre",

View File

@ -51,7 +51,7 @@
"REST_API_FAILED": "Il server '{{ hostname }}' non è raggiungibile", "REST_API_FAILED": "Il server '{{ hostname }}' non è raggiungibile",
"VARIABLE_DROPDOWN_OPTIONS_FAILED": "Si è verificato un problema durante il caricamento degli elementi a discesa. Si prega di contattare lamministratore.", "VARIABLE_DROPDOWN_OPTIONS_FAILED": "Si è verificato un problema durante il caricamento degli elementi a discesa. Si prega di contattare lamministratore.",
"DATA_TABLE_LOAD_FAILED": "Si è verificato un problema durante il caricamento degli elementi della tabella. Si prega di contattare lamministratore.", "DATA_TABLE_LOAD_FAILED": "Si è verificato un problema durante il caricamento degli elementi della tabella. Si prega di contattare lamministratore.",
"DATA_TABLE_EMPTY_CONTENT": "Nessun dato trovato", "DATA_TABLE_EMPTY_CONTENT": "No data found",
"EXTERNAL_PROPERTY_LOAD_FAILED": "Si è verificato un problema durante il caricamento delle proprietà esterne. Si prega di contattare lamministratore.", "EXTERNAL_PROPERTY_LOAD_FAILED": "Si è verificato un problema durante il caricamento delle proprietà esterne. Si prega di contattare lamministratore.",
"FILE_NAME": "Nome file", "FILE_NAME": "Nome file",
"TITLE": "Titolo", "TITLE": "Titolo",

View File

@ -51,7 +51,7 @@
"REST_API_FAILED": "Serwer '{{ hostname }}' jest nieosiągalny", "REST_API_FAILED": "Serwer '{{ hostname }}' jest nieosiągalny",
"VARIABLE_DROPDOWN_OPTIONS_FAILED": "Wystąpił problem podczas ładowania elementów rozwijania. Skontaktuj się z administratorem.", "VARIABLE_DROPDOWN_OPTIONS_FAILED": "Wystąpił problem podczas ładowania elementów rozwijania. Skontaktuj się z administratorem.",
"DATA_TABLE_LOAD_FAILED": "Wystąpił problem podczas ładowania elementów tabeli. Skontaktuj się z administratorem.", "DATA_TABLE_LOAD_FAILED": "Wystąpił problem podczas ładowania elementów tabeli. Skontaktuj się z administratorem.",
"DATA_TABLE_EMPTY_CONTENT": "Brak danych", "DATA_TABLE_EMPTY_CONTENT": "No data found",
"EXTERNAL_PROPERTY_LOAD_FAILED": "Wystąpił problem podczas ładowania właściwości zewnętrznej. Skontaktuj się z administratorem.", "EXTERNAL_PROPERTY_LOAD_FAILED": "Wystąpił problem podczas ładowania właściwości zewnętrznej. Skontaktuj się z administratorem.",
"FILE_NAME": "Nazwa pliku", "FILE_NAME": "Nazwa pliku",
"TITLE": "Tytuł", "TITLE": "Tytuł",

View File

@ -51,7 +51,7 @@
"REST_API_FAILED": "O servidor `{{ hostname }}` não pode ser acedido", "REST_API_FAILED": "O servidor `{{ hostname }}` não pode ser acedido",
"VARIABLE_DROPDOWN_OPTIONS_FAILED": "Problema ao carregar elementos suspensos. Entre em contacto com o administrador.", "VARIABLE_DROPDOWN_OPTIONS_FAILED": "Problema ao carregar elementos suspensos. Entre em contacto com o administrador.",
"DATA_TABLE_LOAD_FAILED": "Problema ao carregar os elementos da tabela. Entre em contacto com o administrador.", "DATA_TABLE_LOAD_FAILED": "Problema ao carregar os elementos da tabela. Entre em contacto com o administrador.",
"DATA_TABLE_EMPTY_CONTENT": "Não foram encontrados dados", "DATA_TABLE_EMPTY_CONTENT": "No data found",
"EXTERNAL_PROPERTY_LOAD_FAILED": "Houve um problema ao carregar a propriedade externa. Entre em contacto com o administrador.", "EXTERNAL_PROPERTY_LOAD_FAILED": "Houve um problema ao carregar a propriedade externa. Entre em contacto com o administrador.",
"FILE_NAME": "Nome do ficheiro", "FILE_NAME": "Nome do ficheiro",
"TITLE": "Título", "TITLE": "Título",

View File

@ -26,70 +26,70 @@
// map SCSS variables to expose as CSS variables // map SCSS variables to expose as CSS variables
$defaults: ( $defaults: (
// theme colors // theme colors
--theme-primary-color: mat.m2-get-color-from-palette($primary), --theme-primary-color: mat.get-color-from-palette($primary),
--theme-primary-color-default-contrast: mat.m2-get-color-from-palette($primary, default-contrast), --theme-primary-color-default-contrast: mat.get-color-from-palette($primary, default-contrast),
--theme-header-text-color: mat.m2-get-color-from-palette($primary, default-contrast), --theme-header-text-color: mat.get-color-from-palette($primary, default-contrast),
--adf-theme-primary-50: mat.m2-get-color-from-palette($primary, 50), --adf-theme-primary-50: mat.get-color-from-palette($primary, 50),
--adf-theme-primary-100: mat.m2-get-color-from-palette($primary, 100), --adf-theme-primary-100: mat.get-color-from-palette($primary, 100),
--adf-theme-primary-300: mat.m2-get-color-from-palette($primary, 300), --adf-theme-primary-300: mat.get-color-from-palette($primary, 300),
--adf-theme-primary-900: mat.m2-get-color-from-palette($primary, 900), --adf-theme-primary-900: mat.get-color-from-palette($primary, 900),
--theme-warn-color: mat.m2-get-color-from-palette($warn), --theme-warn-color: mat.get-color-from-palette($warn),
--theme-warn-color-a700: mat.m2-get-color-from-palette($warn, A700), --theme-warn-color-a700: mat.get-color-from-palette($warn, A700),
--theme-warn-color-default-contrast: mat.m2-get-color-from-palette($warn, default-contrast), --theme-warn-color-default-contrast: mat.get-color-from-palette($warn, default-contrast),
--theme-accent-color: mat.m2-get-color-from-palette($accent), --theme-accent-color: mat.get-color-from-palette($accent),
--theme-accent-color-a200: mat.m2-get-color-from-palette($accent, A200), --theme-accent-color-a200: mat.get-color-from-palette($accent, A200),
--theme-accent-color-default-contrast: mat.m2-get-color-from-palette($accent, default-contrast), --theme-accent-color-default-contrast: mat.get-color-from-palette($accent, default-contrast),
--theme-accent-500: mat.m2-get-color-from-palette($accent, 500), --theme-accent-500: mat.get-color-from-palette($accent, 500),
--adf-theme-foreground-base-color: mat.m2-get-color-from-palette($foreground, base), --adf-theme-foreground-base-color: mat.get-color-from-palette($foreground, base),
--adf-theme-foreground-base-color-065: mat.m2-get-color-from-palette($foreground, base, 0.65), --adf-theme-foreground-base-color-065: mat.get-color-from-palette($foreground, base, 0.65),
--adf-theme-foreground-base-color-045: mat.m2-get-color-from-palette($foreground, base, 0.45), --adf-theme-foreground-base-color-045: mat.get-color-from-palette($foreground, base, 0.45),
--adf-theme-foreground-disabled-text-color: mat.m2-get-color-from-palette($foreground, disabled-text), --adf-theme-foreground-disabled-text-color: mat.get-color-from-palette($foreground, disabled-text),
--adf-theme-foreground-divider-color: mat.m2-get-color-from-palette($foreground, divider), --adf-theme-foreground-divider-color: mat.get-color-from-palette($foreground, divider),
--adf-theme-foreground-icon-color: mat.m2-get-color-from-palette($foreground, icon), --adf-theme-foreground-icon-color: mat.get-color-from-palette($foreground, icon),
--adf-theme-foreground-icon-color-054: mat.m2-get-color-from-palette($foreground, icon, 0.54), --adf-theme-foreground-icon-color-054: mat.get-color-from-palette($foreground, icon, 0.54),
--adf-theme-foreground-secondary-text-color: mat.m2-get-color-from-palette($foreground, secondary-text), --adf-theme-foreground-secondary-text-color: mat.get-color-from-palette($foreground, secondary-text),
--adf-theme-foreground-text-color: mat.m2-get-color-from-palette($foreground, text), --adf-theme-foreground-text-color: mat.get-color-from-palette($foreground, text),
--adf-theme-foreground-text-color-087: mat.m2-get-color-from-palette($foreground, text, 0.87), --adf-theme-foreground-text-color-087: mat.get-color-from-palette($foreground, text, 0.87),
--adf-theme-foreground-text-color-075: mat.m2-get-color-from-palette($foreground, text, 0.75), --adf-theme-foreground-text-color-075: mat.get-color-from-palette($foreground, text, 0.75),
--adf-theme-foreground-text-color-064: mat.m2-get-color-from-palette($foreground, text, 0.64), --adf-theme-foreground-text-color-064: mat.get-color-from-palette($foreground, text, 0.64),
--adf-theme-foreground-text-color-054: mat.m2-get-color-from-palette($foreground, text, 0.54), --adf-theme-foreground-text-color-054: mat.get-color-from-palette($foreground, text, 0.54),
--adf-theme-foreground-text-color-040: mat.m2-get-color-from-palette($foreground, text, 0.4), --adf-theme-foreground-text-color-040: mat.get-color-from-palette($foreground, text, 0.4),
--adf-theme-foreground-text-color-027: mat.m2-get-color-from-palette($foreground, text, 0.27), --adf-theme-foreground-text-color-027: mat.get-color-from-palette($foreground, text, 0.27),
--adf-theme-foreground-text-color-025: mat.m2-get-color-from-palette($foreground, text, 0.25), --adf-theme-foreground-text-color-025: mat.get-color-from-palette($foreground, text, 0.25),
--adf-theme-foreground-text-color-014: mat.m2-get-color-from-palette($foreground, text, 0.14), --adf-theme-foreground-text-color-014: mat.get-color-from-palette($foreground, text, 0.14),
--adf-theme-foreground-text-color-007: mat.m2-get-color-from-palette($foreground, text, 0.07), --adf-theme-foreground-text-color-007: mat.get-color-from-palette($foreground, text, 0.07),
--adf-theme-background-card-color: mat.m2-get-color-from-palette($background, card), --adf-theme-background-card-color: mat.get-color-from-palette($background, card),
--adf-theme-background-card-color-087: mat.m2-get-color-from-palette($background, card, 0.87), --adf-theme-background-card-color-087: mat.get-color-from-palette($background, card, 0.87),
--theme-background-color: mat.m2-get-color-from-palette($background, background), --theme-background-color: mat.get-color-from-palette($background, background),
--adf-theme-background-dialog-color: mat.m2-get-color-from-palette($background, dialog), --adf-theme-background-dialog-color: mat.get-color-from-palette($background, dialog),
--adf-theme-background-hover-color: mat.m2-get-color-from-palette($background, hover), --adf-theme-background-hover-color: mat.get-color-from-palette($background, hover),
--adf-theme-background-selected-button-color: mat.m2-get-color-from-palette($background, selected-button), --adf-theme-background-selected-button-color: mat.get-color-from-palette($background, selected-button),
--adf-theme-background-status-bar-color: mat.m2-get-color-from-palette($background, status-bar), --adf-theme-background-status-bar-color: mat.get-color-from-palette($background, status-bar),
--adf-theme-background-unselected-chip-color: mat.m2-get-color-from-palette($background, unselected-chip), --adf-theme-background-unselected-chip-color: mat.get-color-from-palette($background, unselected-chip),
// typography // typography
--theme-font-family: mat.m2-font-family($typography), --theme-font-family: mat.font-family($typography),
--theme-font-weight: normal, --theme-font-weight: normal,
--theme-body-1-font-size: mat.m2-font-size($typography, body-2), --theme-body-1-font-size: mat.font-size($typography, body-2),
--theme-body-2-font-size: mat.m2-font-size($typography, subtitle-2), --theme-body-2-font-size: mat.font-size($typography, subtitle-2),
--theme-body-1-line-height: mat.m2-line-height($typography, body-2), --theme-body-1-line-height: mat.line-height($typography, body-2),
--theme-display-1-font-size: mat.m2-font-size($typography, headline-4), --theme-display-1-font-size: mat.font-size($typography, headline-4),
--theme-display-3-font-size: mat.m2-font-size($typography, headline-2), --theme-display-3-font-size: mat.font-size($typography, headline-2),
--theme-display-4-font-size: mat.m2-font-size($typography, headline-1), --theme-display-4-font-size: mat.font-size($typography, headline-1),
--theme-caption-font-size: mat.m2-font-size($typography, caption), --theme-caption-font-size: mat.font-size($typography, caption),
--theme-title-font-size: mat.m2-font-size($typography, headline-6), --theme-title-font-size: mat.font-size($typography, headline-6),
--theme-subheading-1-font-size: mat.m2-font-size($typography, body-1), --theme-subheading-1-font-size: mat.font-size($typography, body-1),
--theme-subheading-2-font-size: mat.m2-font-size($typography, subtitle-1), --theme-subheading-2-font-size: mat.font-size($typography, subtitle-1),
--theme-button-font-size: mat.m2-font-size($typography, button), --theme-button-font-size: mat.font-size($typography, button),
--theme-headline-font-size: mat.m2-font-size($typography, headline-5), --theme-headline-font-size: mat.font-size($typography, headline-5),
--theme-headline-line-height: mat.m2-line-height($typography, headline-5), --theme-headline-line-height: mat.line-height($typography, headline-5),
--theme-adf-icon-1-font-size: map-get($custom-css-variables, 'theme-adf-icon-1-font-size'), --theme-adf-icon-1-font-size: map-get($custom-css-variables, 'theme-adf-icon-1-font-size'),
--theme-adf-picture-1-font-size: map-get($custom-css-variables, 'theme-adf-picture-1-font-size'), --theme-adf-picture-1-font-size: map-get($custom-css-variables, 'theme-adf-picture-1-font-size'),
--theme-adf-task-footer-font-size: map-get($custom-css-variables, 'theme-adf-task-footer-font-size'), --theme-adf-task-footer-font-size: map-get($custom-css-variables, 'theme-adf-task-footer-font-size'),
--theme-adf-task-title-font-size: map-get($custom-css-variables, 'theme-adf-task-title-font-size'), --theme-adf-task-title-font-size: map-get($custom-css-variables, 'theme-adf-task-title-font-size'),
// specific colors // specific colors
--adf-theme-mat-grey-color-a200: mat.m2-get-color-from-palette(mat.$m2-grey-palette, 'A200'), --adf-theme-mat-grey-color-a200: mat.get-color-from-palette(mat.$grey-palette, A200),
--adf-theme-mat-grey-color-a400: mat.m2-get-color-from-palette(mat.$m2-grey-palette, 'A400'), --adf-theme-mat-grey-color-a400: mat.get-color-from-palette(mat.$grey-palette, A400),
--adf-theme-mat-grey-color-50: mat.m2-get-color-from-palette(mat.$m2-grey-palette, 50), --adf-theme-mat-grey-color-50: mat.get-color-from-palette(mat.$grey-palette, 50),
// spacing // spacing
--adf-theme-spacing: map-get($custom-css-variables, 'theme-adf-spacing'), --adf-theme-spacing: map-get($custom-css-variables, 'theme-adf-spacing'),
// components // components

View File

@ -1,18 +1,18 @@
@use '@angular/material' as mat; @use '@angular/material' as mat;
$alfresco-typography: mat.m2-define-typography-config( $alfresco-typography: mat.define-typography-config(
$font-family: 'Roboto, "Helvetica Neue", sans-serif', $font-family: 'Roboto, "Helvetica Neue", sans-serif',
$headline-1: mat.m2-define-typography-level(112px, 112px, 300), $headline-1: mat.define-typography-level(112px, 112px, 300),
$headline-2: mat.m2-define-typography-level(56px, 56px, 400), $headline-2: mat.define-typography-level(56px, 56px, 400),
$headline-3: mat.m2-define-typography-level(45px, 48px, 400), $headline-3: mat.define-typography-level(45px, 48px, 400),
$headline-4: mat.m2-define-typography-level(34px, 40px, 400), $headline-4: mat.define-typography-level(34px, 40px, 400),
$headline-5: mat.m2-define-typography-level(24px, 32px, 400), $headline-5: mat.define-typography-level(24px, 32px, 400),
$headline-6: mat.m2-define-typography-level(20px, 32px, 500), $headline-6: mat.define-typography-level(20px, 32px, 500),
$subtitle-1: mat.m2-define-typography-level(16px, 28px, 400), $subtitle-1: mat.define-typography-level(16px, 28px, 400),
$body-1: mat.m2-define-typography-level(15px, 24px, 400), $body-1: mat.define-typography-level(15px, 24px, 400),
$subtitle-2: mat.m2-define-typography-level(14px, 24px, 500), $subtitle-2: mat.define-typography-level(14px, 24px, 500),
$body-2: mat.m2-define-typography-level(14px, 20px, 400), $body-2: mat.define-typography-level(14px, 20px, 400),
$caption: mat.m2-define-typography-level(12px, 20px, 400), $caption: mat.define-typography-level(12px, 20px, 400),
$button: mat.m2-define-typography-level(14px, 14px, 500), $button: mat.define-typography-level(14px, 14px, 500),
// Line-height must be unit-less fraction of the font-size. // Line-height must be unit-less fraction of the font-size.
); );

View File

@ -27,7 +27,7 @@
class="adf-viewer-pdf-viewer" class="adf-viewer-pdf-viewer"
(window:resize)="onResize()"> (window:resize)="onResize()">
<div [id]="randomPdfId + '-viewer-viewerPdf'" <div [id]="randomPdfId + '-viewer-viewerPdf'"
class="adf-pdfViewer pdfViewer" class="adf-pdfViewer"
role="document" role="document"
tabindex="0" tabindex="0"
aria-expanded="true"> aria-expanded="true">

View File

@ -114,7 +114,7 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
displayPage: number; displayPage: number;
totalPages: number; totalPages: number;
loadingPercent: number; loadingPercent: number;
pdfViewer: PDFViewer; pdfViewer: any;
pdfJsWorkerUrl: string; pdfJsWorkerUrl: string;
pdfJsWorkerInstance: Worker; pdfJsWorkerInstance: Worker;
currentScaleMode: PdfScaleMode = 'init'; currentScaleMode: PdfScaleMode = 'init';
@ -132,12 +132,7 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
documentOverflow = false; documentOverflow = false;
get currentScaleText(): string { get currentScaleText(): string {
const currentScaleValueStr = this.pdfViewer?.currentScaleValue; return this.pdfViewer?.currentScaleValue ? Math.round(this.pdfViewer.currentScaleValue * 100) + '%' : '';
const scaleNumber = Number(currentScaleValueStr);
const currentScaleText = scaleNumber ? `${Math.round(scaleNumber * 100)}%` : '';
return currentScaleText;
} }
private pdfjsLib = inject(PDFJS_MODULE); private pdfjsLib = inject(PDFJS_MODULE);
@ -457,9 +452,10 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
*/ */
setScaleUpdatePages(newScale: number) { setScaleUpdatePages(newScale: number) {
if (this.pdfViewer) { if (this.pdfViewer) {
if (!this.isSameScale(this.pdfViewer.currentScaleValue, newScale.toString())) { if (!this.isSameScale(this.pdfViewer.currentScaleValue, newScale)) {
this.pdfViewer.currentScaleValue = newScale.toString(); this.pdfViewer.currentScaleValue = newScale;
} }
this.pdfViewer.update(); this.pdfViewer.update();
} }
this.setDocumentOverflow(); this.setDocumentOverflow();
@ -472,7 +468,7 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
* @param newScale - new scale page * @param newScale - new scale page
* @returns `true` if the scale is the same, otherwise `false` * @returns `true` if the scale is the same, otherwise `false`
*/ */
isSameScale(oldScale: string, newScale: string): boolean { isSameScale(oldScale: number, newScale: number): boolean {
return newScale === oldScale; return newScale === oldScale;
} }

View File

@ -5,9 +5,6 @@ export default {
testEnvironment: 'jsdom', testEnvironment: 'jsdom',
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'], setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
coverageDirectory: '../../../coverage/libs/js-api', coverageDirectory: '../../../coverage/libs/js-api',
moduleMNameMapper: {
'^pdfjs-dist$': 'pdfjs-dist/legacy/build/pdf'
},
transform: { transform: {
'^.+\\.(ts|mjs|js|html)$': [ '^.+\\.(ts|mjs|js|html)$': [
'jest-preset-angular', 'jest-preset-angular',

View File

@ -1 +1 @@
22.14.0 20.18.1

View File

@ -78,10 +78,11 @@
<mat-card-content class="adf-form-container-card-content"> <mat-card-content class="adf-form-container-card-content">
<adf-form-renderer [formDefinition]="form" [readOnly]="readOnly" /> <adf-form-renderer [formDefinition]="form" [readOnly]="readOnly" />
</mat-card-content> </mat-card-content>
<mat-card-actions *ngIf="form.hasOutcomes()" class="adf-cloud-form-content-card-actions" align="end"> <div class="adf-cloud-form-content-card-actions">
<mat-card-actions *ngIf="form.hasOutcomes()" class="adf-form-mat-card-actions" align="end">
<mat-checkbox <mat-checkbox
id="adf-form-open-next-task" id="adf-form-open-next-task"
*ngIf="showNextTaskCheckbox && showCompleteButton" *ngIf="showNextTaskCheckbox"
[checked]="isNextTaskCheckboxChecked" [checked]="isNextTaskCheckboxChecked"
(change)="onNextTaskCheckboxCheckedChanged($event)" (change)="onNextTaskCheckboxCheckedChanged($event)"
>{{ 'ADF_CLOUD_TASK_FORM.OPEN_NEXT_TASK.LABEL' | translate }}</mat-checkbox >{{ 'ADF_CLOUD_TASK_FORM.OPEN_NEXT_TASK.LABEL' | translate }}</mat-checkbox
@ -102,6 +103,7 @@
</button> </button>
</ng-container> </ng-container>
</mat-card-actions> </mat-card-actions>
</div>
</div> </div>
</mat-card> </mat-card>
</div> </div>

View File

@ -72,10 +72,8 @@
} }
&-content-card { &-content-card {
padding-bottom: 2em;
overflow-y: auto; overflow-y: auto;
position: static; position: static;
height: 70%;
&-fullscreen { &-fullscreen {
padding: 0; padding: 0;
@ -93,6 +91,14 @@
} }
} }
} }
&-actions {
position: fixed;
bottom: 0;
width: -webkit-fill-available;
z-index: 1;
background-color: white;
}
} }
&-sidebars { &-sidebars {

View File

@ -1192,7 +1192,7 @@ describe('FormCloudComponent', () => {
expect(form.fieldValidators.length).toBe(10); expect(form.fieldValidators.length).toBe(10);
}); });
it('should allow controlling [open next task] checkbox visibility', async () => { it('should allow controlling [open next task] checkbox visibility', () => {
const formModel = new FormModel({ fields: [{ id: 'field2' }] }); const formModel = new FormModel({ fields: [{ id: 'field2' }] });
formComponent.form = formModel; formComponent.form = formModel;
@ -1202,19 +1202,14 @@ describe('FormCloudComponent', () => {
}; };
fixture.detectChanges(); fixture.detectChanges();
await fixture.whenStable();
expect(isCheckboxShown()).toBeFalse(); expect(isCheckboxShown()).toBeFalse();
formComponent.showNextTaskCheckbox = true; formComponent.showNextTaskCheckbox = true;
formComponent.showCompleteButton = true;
fixture.detectChanges(); fixture.detectChanges();
await fixture.whenStable();
expect(isCheckboxShown()).toBeTrue(); expect(isCheckboxShown()).toBeTrue();
formComponent.showNextTaskCheckbox = false; formComponent.showNextTaskCheckbox = false;
formComponent.showCompleteButton = false;
fixture.detectChanges(); fixture.detectChanges();
await fixture.whenStable();
expect(isCheckboxShown()).toBeFalse(); expect(isCheckboxShown()).toBeFalse();
}); });
@ -1222,58 +1217,32 @@ describe('FormCloudComponent', () => {
const formModel = new FormModel({ fields: [{ id: 'field2' }] }); const formModel = new FormModel({ fields: [{ id: 'field2' }] });
formComponent.form = formModel; formComponent.form = formModel;
formComponent.showNextTaskCheckbox = true; formComponent.showNextTaskCheckbox = true;
formComponent.showCompleteButton = true;
fixture.detectChanges(); fixture.detectChanges();
await fixture.whenStable();
const isCheckboxChecked = async () => { const isCheckboxChecked = async () => {
if (formComponent.showNextTaskCheckbox && formComponent.showCompleteButton) { const checkbox = await documentRootLoader.getHarness(MatCheckboxHarness.with({ selector: '#adf-form-open-next-task' }));
const checkbox = await documentRootLoader.getHarness(MatCheckboxHarness.with({ selector: '#adf-form-open-next-task' })); return checkbox.isChecked();
return checkbox.isChecked();
}
return null;
}; };
expect(await isCheckboxChecked()).toBeFalse(); expect(await isCheckboxChecked()).toBeFalse();
formComponent.isNextTaskCheckboxChecked = true; formComponent.isNextTaskCheckboxChecked = true;
formComponent.showCompleteButton = true;
fixture.detectChanges(); fixture.detectChanges();
await fixture.whenStable();
expect(await isCheckboxChecked()).toBeTrue(); expect(await isCheckboxChecked()).toBeTrue();
formComponent.isNextTaskCheckboxChecked = false; formComponent.isNextTaskCheckboxChecked = false;
formComponent.showCompleteButton = false;
fixture.detectChanges(); fixture.detectChanges();
await fixture.whenStable(); expect(await isCheckboxChecked()).toBeFalse();
// Skip the checkbox visibility test if it's not supposed to be visible
if (formComponent.showNextTaskCheckbox && formComponent.showCompleteButton) {
expect(await isCheckboxChecked()).toBeFalse();
} else {
// Alternative test when checkbox shouldn't be visible
const checkboxElement = fixture.debugElement.query(By.css('#adf-form-open-next-task'));
expect(checkboxElement).toBeNull();
}
}); });
it('should call onNextTaskCheckboxCheckedChanged when the checkbox is checked', async () => { it('should call onNextTaskCheckboxCheckedChanged when the checkbox is checked', async () => {
// Add fields to make sure the components are shown which contain the checkbox // Add fields to make sure the components are shown which contain the the checkbox
const formModel = new FormModel({ fields: [{ id: 'field2' }] }); const formModel = new FormModel({ fields: [{ id: 'field2' }] });
formComponent.form = formModel; formComponent.form = formModel;
// Set both required properties to make the checkbox visible
formComponent.showNextTaskCheckbox = true; formComponent.showNextTaskCheckbox = true;
formComponent.showCompleteButton = true;
fixture.detectChanges(); fixture.detectChanges();
await fixture.whenStable(); const checkbox = await documentRootLoader.getHarnessOrNull(MatCheckboxHarness);
// Use a specific selector to target the correct checkbox
const checkbox = await documentRootLoader.getHarnessOrNull(MatCheckboxHarness.with({ selector: '#adf-form-open-next-task' }));
// Ensure checkbox was found
expect(checkbox).not.toBeNull();
spyOn(formComponent.nextTaskCheckboxCheckedChanged, 'emit'); spyOn(formComponent.nextTaskCheckboxCheckedChanged, 'emit');
await checkbox.check(); await checkbox.check();
@ -1742,13 +1711,4 @@ describe('retrieve metadata on submit', () => {
expect(formComponent.disableSaveButton).toBeFalse(); expect(formComponent.disableSaveButton).toBeFalse();
}); });
it('should not show next task checkbox when complete button is not shown', () => {
formComponent.showNextTaskCheckbox = false;
formComponent.showCompleteButton = false;
fixture.detectChanges();
const checkbox = fixture.debugElement.query(By.css('#adf-form-open-next-task'));
expect(checkbox).toBeNull();
});
}); });

View File

@ -128,10 +128,6 @@ export class FormCloudComponent extends FormBaseComponent implements OnChanges,
@Input() @Input()
isNextTaskCheckboxChecked = false; isNextTaskCheckboxChecked = false;
/** Toggle rendering of the `Complete` button. */
@Input()
showCompleteButton = false;
/** Emitted when the form is submitted with the `Save` or custom outcomes. */ /** Emitted when the form is submitted with the `Save` or custom outcomes. */
@Output() @Output()
formSaved = new EventEmitter<FormModel>(); formSaved = new EventEmitter<FormModel>();

View File

@ -983,34 +983,6 @@ describe('DropdownCloudWidgetComponent', () => {
expect(widget.field.options.length).toEqual(0); expect(widget.field.options.length).toEqual(0);
}; };
it('should set dropdownControl value without emitting events if the mapping is a string', () => {
widget.field = {
value: 'testValue',
options: [],
isVisible: true
} as any; // Mock field
spyOn(widget.dropdownControl, 'setValue').and.callThrough();
widget['setFormControlValue']();
expect(widget.dropdownControl.setValue).toHaveBeenCalledWith({ id: 'testValue', name: '' }, { emitEvent: false });
expect(widget.dropdownControl.value).toEqual({ id: 'testValue', name: '' });
});
it('should set dropdownControl value without emitting events if is an object', () => {
widget.field = {
value: { id: 'testValueObj', name: 'testValueObjName' },
options: [],
isVisible: true
} as any; // Mock field
spyOn(widget.dropdownControl, 'setValue').and.callThrough();
widget['setFormControlValue']();
expect(widget.dropdownControl.setValue).toHaveBeenCalledWith({ id: 'testValueObj', name: 'testValueObjName' }, { emitEvent: false });
expect(widget.dropdownControl.value).toEqual({ id: 'testValueObj', name: 'testValueObjName' });
});
it('should display options persisted from process variable', async () => { it('should display options persisted from process variable', async () => {
widget.field = getVariableDropdownWidget( widget.field = getVariableDropdownWidget(
'variables.json-variable', 'variables.json-variable',

View File

@ -195,15 +195,7 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI
} }
private setFormControlValue(): void { private setFormControlValue(): void {
if (Array.isArray(this.field.value)) { this.dropdownControl.setValue(this.field?.value, { emitEvent: false });
this.dropdownControl.setValue(this.field?.value, { emitEvent: false });
} else if (this.field?.value && typeof this.field?.value === 'object') {
this.dropdownControl.setValue({ id: this.field?.value.id, name: this.field?.value.name }, { emitEvent: false });
} else if (this.field.value === null) {
this.dropdownControl.setValue(this.field?.value, { emitEvent: false });
} else {
this.dropdownControl.setValue({ id: this.field?.value, name: '' }, { emitEvent: false });
}
} }
private updateFormControlState(): void { private updateFormControlState(): void {
@ -477,11 +469,7 @@ export class DropdownCloudWidgetComponent extends WidgetComponent implements OnI
const fieldValueIds = this.field.value.map((valueOption) => valueOption.id); const fieldValueIds = this.field.value.map((valueOption) => valueOption.id);
return fieldValueIds.every((valueOptionId) => optionIdList.includes(valueOptionId)); return fieldValueIds.every((valueOptionId) => optionIdList.includes(valueOptionId));
} else { } else {
if (this.field?.value && typeof this.field?.value === 'object') { return [...this.field.options].map((option) => option.id).includes(this.field.value);
return [...this.field.options].map((option) => option.id).includes(this.field.value.id);
} else {
return [...this.field.options].map((option) => option.id).includes(this.field.value);
}
} }
} }

View File

@ -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 { FormFieldModel } from '@alfresco/adf-core';
import { FormControl } from '@angular/forms';
import { defaultValueValidator } from './validators';
import { DEFAULT_OPTION } from './dropdown-cloud.widget';
describe('defaultValueValidator', () => {
let mockField: FormFieldModel;
beforeEach(() => {
mockField = new FormFieldModel(null, {
options: [
{ id: DEFAULT_OPTION.id, name: DEFAULT_OPTION.name },
{ id: 'opt_1', name: 'Option 1' },
{ id: 'opt_2', name: 'Option 2' }
]
});
});
it('should return null when a valid option is selected', () => {
const validator = defaultValueValidator(mockField);
const control = new FormControl({ id: 'opt_1' });
const result = validator(control);
expect(result).toBeNull();
});
it('should return a required error when no valid option is selected', () => {
const validator = defaultValueValidator(mockField);
const control = new FormControl(null);
const result = validator(control);
expect(result).toEqual({ required: true });
});
it('should return a required error when the default "choose one" option is selected', () => {
const validator = defaultValueValidator(mockField);
const control = new FormControl(DEFAULT_OPTION.id);
const result = validator(control);
expect(result).toEqual({ required: true });
});
});

View File

@ -30,7 +30,6 @@ export const defaultValueValidator =
const isSomeOptionSelected = optionsWithNoDefaultValue.some((dropdownOption) => { const isSomeOptionSelected = optionsWithNoDefaultValue.some((dropdownOption) => {
const isOptionSelected = dropdownOption.id === control.value?.id; const isOptionSelected = dropdownOption.id === control.value?.id;
return isOptionSelected; return isOptionSelected;
}); });

View File

@ -381,7 +381,7 @@
"DESTINATION_FOLDER_PATH_ERROR": "La ruta de destino es incorrecta o no existe, vuelva a -mi- ubicación" "DESTINATION_FOLDER_PATH_ERROR": "La ruta de destino es incorrecta o no existe, vuelva a -mi- ubicación"
}, },
"OPEN_NEXT_TASK": { "OPEN_NEXT_TASK": {
"LABEL": "Abrir la siguiente tarea" "LABEL": "Open next task"
} }
}, },
"ADF_CLOUD_FORM_COMPONENT": { "ADF_CLOUD_FORM_COMPONENT": {

View File

@ -381,7 +381,7 @@
"DESTINATION_FOLDER_PATH_ERROR": "Le chemin de destination est incorrect ou n'existe pas. Revenir à 'mon emplacement'" "DESTINATION_FOLDER_PATH_ERROR": "Le chemin de destination est incorrect ou n'existe pas. Revenir à 'mon emplacement'"
}, },
"OPEN_NEXT_TASK": { "OPEN_NEXT_TASK": {
"LABEL": "Ouvrir la tâche suivante" "LABEL": "Open next task"
} }
}, },
"ADF_CLOUD_FORM_COMPONENT": { "ADF_CLOUD_FORM_COMPONENT": {

View File

@ -381,7 +381,7 @@
"DESTINATION_FOLDER_PATH_ERROR": "Il percorso di destinazione non è corretto o non esiste: eseguire il ripristino alla posizione personale" "DESTINATION_FOLDER_PATH_ERROR": "Il percorso di destinazione non è corretto o non esiste: eseguire il ripristino alla posizione personale"
}, },
"OPEN_NEXT_TASK": { "OPEN_NEXT_TASK": {
"LABEL": "Apri l'attività successiva" "LABEL": "Open next task"
} }
}, },
"ADF_CLOUD_FORM_COMPONENT": { "ADF_CLOUD_FORM_COMPONENT": {

View File

@ -381,7 +381,7 @@
"DESTINATION_FOLDER_PATH_ERROR": "Ścieżka docelowa jest nieprawidłowa lub nie istnieje. Cofnij do mojej lokalizacji" "DESTINATION_FOLDER_PATH_ERROR": "Ścieżka docelowa jest nieprawidłowa lub nie istnieje. Cofnij do mojej lokalizacji"
}, },
"OPEN_NEXT_TASK": { "OPEN_NEXT_TASK": {
"LABEL": "Otwórz następne zadanie" "LABEL": "Open next task"
} }
}, },
"ADF_CLOUD_FORM_COMPONENT": { "ADF_CLOUD_FORM_COMPONENT": {

View File

@ -381,7 +381,7 @@
"DESTINATION_FOLDER_PATH_ERROR": "O caminho de destino está incorreto ou não existe, reverter para a localização -meu- " "DESTINATION_FOLDER_PATH_ERROR": "O caminho de destino está incorreto ou não existe, reverter para a localização -meu- "
}, },
"OPEN_NEXT_TASK": { "OPEN_NEXT_TASK": {
"LABEL": "Abrir a próxima tarefa" "LABEL": "Open next task"
} }
}, },
"ADF_CLOUD_FORM_COMPONENT": { "ADF_CLOUD_FORM_COMPONENT": {

View File

@ -26,8 +26,8 @@ import { LocalPreferenceCloudService } from '../../../../services/local-preferen
import { mockProcessFilters } from '../../mock/process-filters-cloud.mock'; import { mockProcessFilters } from '../../mock/process-filters-cloud.mock';
import { AppConfigService, AppConfigServiceMock, NoopTranslateModule } from '@alfresco/adf-core'; import { AppConfigService, AppConfigServiceMock, NoopTranslateModule } from '@alfresco/adf-core';
import { ProcessListCloudService } from '../../../process-list/services/process-list-cloud.service'; import { ProcessListCloudService } from '../../../process-list/services/process-list-cloud.service';
import { ApolloModule } from 'apollo-angular';
import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { ApolloTestingModule } from 'apollo-angular/testing';
const ProcessFilterCloudServiceMock = { const ProcessFilterCloudServiceMock = {
getProcessFilters: () => of(mockProcessFilters), getProcessFilters: () => of(mockProcessFilters),
@ -44,7 +44,7 @@ describe('ProcessFiltersCloudComponent', () => {
const configureTestingModule = (searchApiMethod: 'GET' | 'POST') => { const configureTestingModule = (searchApiMethod: 'GET' | 'POST') => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [NoopTranslateModule, NoopAnimationsModule, ProcessFiltersCloudComponent, ApolloTestingModule], imports: [NoopTranslateModule, NoopAnimationsModule, ProcessFiltersCloudComponent],
providers: [ providers: [
{ provide: PROCESS_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService }, { provide: PROCESS_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService },
{ provide: AppConfigService, useClass: AppConfigServiceMock }, { provide: AppConfigService, useClass: AppConfigServiceMock },
@ -55,7 +55,8 @@ describe('ProcessFiltersCloudComponent', () => {
getProcessListCounter: () => of(10) getProcessListCounter: () => of(10)
} }
}, },
{ provide: ProcessFilterCloudService, useValue: ProcessFilterCloudServiceMock } { provide: ProcessFilterCloudService, useValue: ProcessFilterCloudServiceMock },
ApolloModule
] ]
}); });
fixture = TestBed.createComponent(ProcessFiltersCloudComponent); fixture = TestBed.createComponent(ProcessFiltersCloudComponent);

View File

@ -32,7 +32,6 @@ import {
import { ProcessFilterCloudModel } from '../models/process-filter-cloud.model'; import { ProcessFilterCloudModel } from '../models/process-filter-cloud.model';
import { IdentityUserService } from '../../../people/services/identity-user.service'; import { IdentityUserService } from '../../../people/services/identity-user.service';
import { NotificationCloudService } from '../../../services/notification-cloud.service'; import { NotificationCloudService } from '../../../services/notification-cloud.service';
import { ApolloTestingModule } from 'apollo-angular/testing';
describe('ProcessFilterCloudService', () => { describe('ProcessFilterCloudService', () => {
let service: ProcessFilterCloudService; let service: ProcessFilterCloudService;
@ -52,7 +51,7 @@ describe('ProcessFilterCloudService', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [ProcessServiceCloudTestingModule, ApolloTestingModule], imports: [ProcessServiceCloudTestingModule],
providers: [{ provide: PROCESS_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService }] providers: [{ provide: PROCESS_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService }]
}); });
service = TestBed.inject(ProcessFilterCloudService); service = TestBed.inject(ProcessFilterCloudService);

View File

@ -21,6 +21,7 @@ import { MatDialog } from '@angular/material/dialog';
import { of, Subject } from 'rxjs'; import { of, Subject } from 'rxjs';
import { TASK_FILTERS_SERVICE_TOKEN } from '../../../../../services/cloud-token.service'; import { TASK_FILTERS_SERVICE_TOKEN } from '../../../../../services/cloud-token.service';
import { LocalPreferenceCloudService } from '../../../../../services/local-preference-cloud.service'; import { LocalPreferenceCloudService } from '../../../../../services/local-preference-cloud.service';
import { ProcessServiceCloudTestingModule } from '../../../../../testing/process-service-cloud.testing.module';
import { AppsProcessCloudService } from '../../../../../app/services/apps-process-cloud.service'; import { AppsProcessCloudService } from '../../../../../app/services/apps-process-cloud.service';
import { fakeApplicationInstance, fakeApplicationInstanceWithEnvironment } from '../../../../../app/mock/app-model.mock'; import { fakeApplicationInstance, fakeApplicationInstanceWithEnvironment } from '../../../../../app/mock/app-model.mock';
import { ServiceTaskFilterCloudService } from '../../../services/service-task-filter-cloud.service'; import { ServiceTaskFilterCloudService } from '../../../services/service-task-filter-cloud.service';
@ -37,11 +38,6 @@ import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatExpansionPanelHarness } from '@angular/material/expansion/testing'; import { MatExpansionPanelHarness } from '@angular/material/expansion/testing';
import { MatSelectHarness } from '@angular/material/select/testing'; import { MatSelectHarness } from '@angular/material/select/testing';
import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing'; import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing';
import { NoopAuthModule, NoopTranslateModule } from '@alfresco/adf-core';
import { ApolloTestingModule } from 'apollo-angular/testing';
import { DateAdapter, MAT_DATE_LOCALE } from '@angular/material/core';
import { DateFnsAdapter } from '@angular/material-date-fns-adapter';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
describe('EditServiceTaskFilterCloudComponent', () => { describe('EditServiceTaskFilterCloudComponent', () => {
let loader: HarnessLoader; let loader: HarnessLoader;
@ -57,19 +53,8 @@ describe('EditServiceTaskFilterCloudComponent', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [ imports: [ProcessServiceCloudTestingModule, MatIconTestingModule, EditServiceTaskFilterCloudComponent],
MatIconTestingModule, providers: [MatDialog, { provide: TASK_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService }]
NoopAnimationsModule,
EditServiceTaskFilterCloudComponent,
NoopTranslateModule,
ApolloTestingModule,
NoopAuthModule
],
providers: [
MatDialog,
{ provide: TASK_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService },
{ provide: DateAdapter, useClass: DateFnsAdapter, deps: [MAT_DATE_LOCALE] }
]
}); });
fixture = TestBed.createComponent(EditServiceTaskFilterCloudComponent); fixture = TestBed.createComponent(EditServiceTaskFilterCloudComponent);
component = fixture.componentInstance; component = fixture.componentInstance;

View File

@ -22,6 +22,7 @@ import { MatDialog } from '@angular/material/dialog';
import { of, Subject } from 'rxjs'; import { of, Subject } from 'rxjs';
import { TASK_FILTERS_SERVICE_TOKEN } from '../../../../../services/cloud-token.service'; import { TASK_FILTERS_SERVICE_TOKEN } from '../../../../../services/cloud-token.service';
import { LocalPreferenceCloudService } from '../../../../../services/local-preference-cloud.service'; import { LocalPreferenceCloudService } from '../../../../../services/local-preference-cloud.service';
import { ProcessServiceCloudTestingModule } from '../../../../../testing/process-service-cloud.testing.module';
import { AppsProcessCloudService } from '../../../../../app/services/apps-process-cloud.service'; import { AppsProcessCloudService } from '../../../../../app/services/apps-process-cloud.service';
import { fakeApplicationInstance } from '../../../../../app/mock/app-model.mock'; import { fakeApplicationInstance } from '../../../../../app/mock/app-model.mock';
import { EditTaskFilterCloudComponent } from './edit-task-filter-cloud.component'; import { EditTaskFilterCloudComponent } from './edit-task-filter-cloud.component';
@ -55,11 +56,6 @@ import { MatSelectHarness } from '@angular/material/select/testing';
import { MatExpansionPanelHarness } from '@angular/material/expansion/testing'; import { MatExpansionPanelHarness } from '@angular/material/expansion/testing';
import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing'; import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing';
import { PeopleCloudComponent } from '@alfresco/adf-process-services-cloud'; import { PeopleCloudComponent } from '@alfresco/adf-process-services-cloud';
import { ApolloTestingModule } from 'apollo-angular/testing';
import { ADF_DATE_FORMATS, NoopAuthModule, NoopTranslateModule } from '@alfresco/adf-core';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { DateAdapter, MAT_DATE_FORMATS, MAT_DATE_LOCALE } from '@angular/material/core';
import { DateFnsAdapter } from '@angular/material-date-fns-adapter';
describe('EditTaskFilterCloudComponent', () => { describe('EditTaskFilterCloudComponent', () => {
let loader: HarnessLoader; let loader: HarnessLoader;
@ -77,21 +73,8 @@ describe('EditTaskFilterCloudComponent', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [ imports: [ProcessServiceCloudTestingModule, PeopleCloudComponent, MatIconTestingModule, EditTaskFilterCloudComponent],
NoopAuthModule, providers: [MatDialog, { provide: TASK_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService }]
NoopAnimationsModule,
NoopTranslateModule,
PeopleCloudComponent,
MatIconTestingModule,
EditTaskFilterCloudComponent,
ApolloTestingModule
],
providers: [
MatDialog,
{ provide: TASK_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService },
{ provide: DateAdapter, useClass: DateFnsAdapter, deps: [MAT_DATE_LOCALE] },
{ provide: MAT_DATE_FORMATS, useValue: ADF_DATE_FORMATS }
]
}); });
fixture = TestBed.createComponent(EditTaskFilterCloudComponent); fixture = TestBed.createComponent(EditTaskFilterCloudComponent);
component = fixture.componentInstance; component = fixture.componentInstance;

View File

@ -40,6 +40,7 @@ import { MatCheckboxModule } from '@angular/material/checkbox';
import { DateRangeFilterComponent } from '../../../../../common/date-range-filter/date-range-filter.component'; import { DateRangeFilterComponent } from '../../../../../common/date-range-filter/date-range-filter.component';
import { PeopleCloudComponent } from '../../../../../people/components/people-cloud.component'; import { PeopleCloudComponent } from '../../../../../people/components/people-cloud.component';
import { TaskAssignmentFilterCloudComponent } from '../../task-assignment-filter/task-assignment-filter.component'; import { TaskAssignmentFilterCloudComponent } from '../../task-assignment-filter/task-assignment-filter.component';
import { ApolloModule } from 'apollo-angular';
@Component({ @Component({
selector: 'adf-cloud-edit-task-filter', selector: 'adf-cloud-edit-task-filter',
@ -60,7 +61,8 @@ import { TaskAssignmentFilterCloudComponent } from '../../task-assignment-filter
MatCheckboxModule, MatCheckboxModule,
DateRangeFilterComponent, DateRangeFilterComponent,
PeopleCloudComponent, PeopleCloudComponent,
TaskAssignmentFilterCloudComponent TaskAssignmentFilterCloudComponent,
ApolloModule
], ],
templateUrl: './edit-task-filter-cloud.component.html', templateUrl: './edit-task-filter-cloud.component.html',
styleUrls: ['./edit-task-filter-cloud.component.scss'], styleUrls: ['./edit-task-filter-cloud.component.scss'],

View File

@ -15,13 +15,14 @@
* limitations under the License. * limitations under the License.
*/ */
import { AppConfigService, NoopAuthModule, NoopTranslateModule } from '@alfresco/adf-core'; import { AppConfigService } from '@alfresco/adf-core';
import { SimpleChange } from '@angular/core'; import { SimpleChange } from '@angular/core';
import { ComponentFixture, TestBed, fakeAsync, flush } from '@angular/core/testing'; import { ComponentFixture, TestBed, fakeAsync, flush } from '@angular/core/testing';
import { By } from '@angular/platform-browser'; import { By } from '@angular/platform-browser';
import { first, of, throwError } from 'rxjs'; import { first, of, throwError } from 'rxjs';
import { TASK_FILTERS_SERVICE_TOKEN } from '../../../../services/cloud-token.service'; import { TASK_FILTERS_SERVICE_TOKEN } from '../../../../services/cloud-token.service';
import { LocalPreferenceCloudService } from '../../../../services/local-preference-cloud.service'; import { LocalPreferenceCloudService } from '../../../../services/local-preference-cloud.service';
import { ProcessServiceCloudTestingModule } from '../../../../testing/process-service-cloud.testing.module';
import { defaultTaskFiltersMock, fakeGlobalFilter, taskNotifications } from '../../mock/task-filters-cloud.mock'; import { defaultTaskFiltersMock, fakeGlobalFilter, taskNotifications } from '../../mock/task-filters-cloud.mock';
import { TaskFilterCloudService } from '../../services/task-filter-cloud.service'; import { TaskFilterCloudService } from '../../services/task-filter-cloud.service';
import { TaskFiltersCloudComponent } from './task-filters-cloud.component'; import { TaskFiltersCloudComponent } from './task-filters-cloud.component';
@ -30,8 +31,6 @@ import { HarnessLoader } from '@angular/cdk/testing';
import { MatActionListItemHarness } from '@angular/material/list/testing'; import { MatActionListItemHarness } from '@angular/material/list/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { TaskFilterCloudAdapter } from '../../../../models/filter-cloud-model'; import { TaskFilterCloudAdapter } from '../../../../models/filter-cloud-model';
import { ApolloTestingModule } from 'apollo-angular/testing';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
describe('TaskFiltersCloudComponent', () => { describe('TaskFiltersCloudComponent', () => {
let loader: HarnessLoader; let loader: HarnessLoader;
@ -47,7 +46,7 @@ describe('TaskFiltersCloudComponent', () => {
const configureTestingModule = (searchApiMethod: 'GET' | 'POST') => { const configureTestingModule = (searchApiMethod: 'GET' | 'POST') => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [NoopAuthModule, NoopAnimationsModule, NoopTranslateModule, TaskFiltersCloudComponent, ApolloTestingModule], imports: [ProcessServiceCloudTestingModule, TaskFiltersCloudComponent],
providers: [{ provide: TASK_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService }] providers: [{ provide: TASK_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService }]
}); });
taskFilterService = TestBed.inject(TaskFilterCloudService); taskFilterService = TestBed.inject(TaskFilterCloudService);

View File

@ -34,9 +34,9 @@ import { HttpClientTestingModule } from '@angular/common/http/testing';
import { NotificationCloudService } from '../../../services/notification-cloud.service'; import { NotificationCloudService } from '../../../services/notification-cloud.service';
import { ProcessServiceCloudTestingModule } from '../../../testing/process-service-cloud.testing.module'; import { ProcessServiceCloudTestingModule } from '../../../testing/process-service-cloud.testing.module';
import { IdentityUserService } from '../../../people/services/identity-user.service'; import { IdentityUserService } from '../../../people/services/identity-user.service';
import { ApolloModule } from 'apollo-angular';
import { StorageService } from '@alfresco/adf-core'; import { StorageService } from '@alfresco/adf-core';
import { TaskStatusFilter } from '../public-api'; import { TaskStatusFilter } from '../public-api';
import { ApolloTestingModule } from 'apollo-angular/testing';
describe('TaskFilterCloudService', () => { describe('TaskFilterCloudService', () => {
let service: TaskFilterCloudService; let service: TaskFilterCloudService;
@ -56,7 +56,7 @@ describe('TaskFilterCloudService', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [HttpClientTestingModule, ProcessServiceCloudTestingModule, ApolloTestingModule], imports: [HttpClientTestingModule, ProcessServiceCloudTestingModule, ApolloModule],
providers: [{ provide: TASK_FILTERS_SERVICE_TOKEN, useClass: UserPreferenceCloudService }] providers: [{ provide: TASK_FILTERS_SERVICE_TOKEN, useClass: UserPreferenceCloudService }]
}); });
service = TestBed.inject(TaskFilterCloudService); service = TestBed.inject(TaskFilterCloudService);
@ -265,7 +265,7 @@ describe('Inject [LocalPreferenceCloudService] into the TaskFilterCloudService',
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [HttpClientTestingModule, ProcessServiceCloudTestingModule, ApolloTestingModule], imports: [HttpClientTestingModule, ProcessServiceCloudTestingModule, ApolloModule],
providers: [{ provide: TASK_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService }] providers: [{ provide: TASK_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService }]
}); });
service = TestBed.inject(TaskFilterCloudService); service = TestBed.inject(TaskFilterCloudService);

View File

@ -20,7 +20,8 @@ import { of, throwError } from 'rxjs';
import { By } from '@angular/platform-browser'; import { By } from '@angular/platform-browser';
import { ComponentFixture, TestBed, fakeAsync, flush, discardPeriodicTasks } from '@angular/core/testing'; import { ComponentFixture, TestBed, fakeAsync, flush, discardPeriodicTasks } from '@angular/core/testing';
import { AlfrescoApiService } from '@alfresco/adf-content-services'; import { AlfrescoApiService } from '@alfresco/adf-content-services';
import { AppConfigService, NoopAuthModule, NoopTranslateModule } from '@alfresco/adf-core'; import { AppConfigService } from '@alfresco/adf-core';
import { ProcessServiceCloudTestingModule } from '../../../testing/process-service-cloud.testing.module';
import { TaskCloudService } from '../../services/task-cloud.service'; import { TaskCloudService } from '../../services/task-cloud.service';
import { import {
assignedTaskDetailsCloudMock, assignedTaskDetailsCloudMock,
@ -33,8 +34,6 @@ import {
import { HarnessLoader } from '@angular/cdk/testing'; import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatSelectHarness } from '@angular/material/select/testing'; import { MatSelectHarness } from '@angular/material/select/testing';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
describe('TaskHeaderCloudComponent', () => { describe('TaskHeaderCloudComponent', () => {
let component: TaskHeaderCloudComponent; let component: TaskHeaderCloudComponent;
@ -61,7 +60,7 @@ describe('TaskHeaderCloudComponent', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [TaskHeaderCloudComponent, HttpClientTestingModule, NoopTranslateModule, NoopAuthModule, NoopAnimationsModule] imports: [ProcessServiceCloudTestingModule, TaskHeaderCloudComponent]
}); });
appConfigService = TestBed.inject(AppConfigService); appConfigService = TestBed.inject(AppConfigService);
appConfigService.config = { appConfigService.config = {
@ -189,9 +188,8 @@ describe('TaskHeaderCloudComponent', () => {
fixture.detectChanges(); fixture.detectChanges();
expect(taskCloudService.updateTask).toHaveBeenCalled(); expect(taskCloudService.updateTask).toHaveBeenCalled();
}); });
// This test is keep failing even though not clearly it just triggers an error in the afterAll so it's hidden
// eslint-disable-next-line it('should roll back task description on error', fakeAsync(() => {
xit('should roll back task description on error', fakeAsync(() => {
spyOn(taskCloudService, 'updateTask').and.returnValue(throwError('fake')); spyOn(taskCloudService, 'updateTask').and.returnValue(throwError('fake'));
fixture.detectChanges(); fixture.detectChanges();

View File

@ -941,7 +941,7 @@ describe('FormComponent', () => {
let dropdownField = formFields.find((field) => field.id === 'dropdownId'); let dropdownField = formFields.find((field) => field.id === 'dropdownId');
let radioField = formFields.find((field) => field.id === 'radio'); let radioField = formFields.find((field) => field.id === 'radio');
expect(dropdownField.value).toEqual({ id: 'empty', name: 'Choose one...' }); expect(dropdownField.value).toBe('empty');
expect(radioField.value).toBeNull(); expect(radioField.value).toBeNull();
const formValues: any = {}; const formValues: any = {};
@ -961,10 +961,7 @@ describe('FormComponent', () => {
dropdownField = formFields.find((field) => field.id === 'dropdownId'); dropdownField = formFields.find((field) => field.id === 'dropdownId');
radioField = formFields.find((field) => field.id === 'radio'); radioField = formFields.find((field) => field.id === 'radio');
expect(dropdownField.value).toEqual({ expect(dropdownField.value).toBe('dropdown_option_2');
id: 'dropdown_option_2',
name: 'Dropdown option 2'
});
expect(radioField.value).toBe('radio_option_3'); expect(radioField.value).toBe('radio_option_3');
}); });

14687
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -29,25 +29,26 @@
"process services-cloud" "process services-cloud"
], ],
"dependencies": { "dependencies": {
"@angular/animations": "18.2.13", "@angular/animations": "17.1.3",
"@angular/cdk": "18.2.14", "@angular/cdk": "17.1.2",
"@angular/common": "18.2.13", "@angular/common": "17.1.3",
"@angular/compiler": "18.2.13", "@angular/compiler": "17.1.3",
"@angular/core": "18.2.13", "@angular/core": "17.1.3",
"@angular/forms": "18.2.13", "@angular/forms": "17.1.3",
"@angular/material": "18.2.14", "@angular/material": "17.1.2",
"@angular/material-date-fns-adapter": "18.2.14", "@angular/material-date-fns-adapter": "17.1.2",
"@angular/platform-browser": "18.2.13", "@angular/platform-browser": "17.1.3",
"@angular/platform-browser-dynamic": "18.2.13", "@angular/platform-browser-dynamic": "17.1.3",
"@angular/router": "18.2.13", "@angular/router": "17.1.3",
"@apollo/client": "3.13.1", "@apollo/client": "^3.13.4",
"@cspell/eslint-plugin": "8.16.1", "@cspell/eslint-plugin": "8.16.1",
"@mat-datetimepicker/core": "14.0.0", "@mat-datetimepicker/core": "13.0.2",
"@ngx-translate/core": "^14.0.0", "@ngx-translate/core": "^14.0.0",
"@nx/webpack": "^20.0.0", "@nx/webpack": "^20.0.0",
"@valano/change-font-size": "^1.0.0",
"angular-oauth2-oidc": "17.0.2", "angular-oauth2-oidc": "17.0.2",
"angular-oauth2-oidc-jwks": "^17.0.2", "angular-oauth2-oidc-jwks": "^17.0.2",
"apollo-angular": "10.0.3", "apollo-angular": "6.0.0",
"chart.js": "4.4.4", "chart.js": "4.4.4",
"cropperjs": "1.6.2", "cropperjs": "1.6.2",
"date-fns": "^2.30.0", "date-fns": "^2.30.0",
@ -55,28 +56,27 @@
"event-emitter": "^0.3.5", "event-emitter": "^0.3.5",
"graphql-ws": "^5.16.0", "graphql-ws": "^5.16.0",
"material-icons": "^1.13.12", "material-icons": "^1.13.12",
"minimatch-browser": "1.0.0", "minimatch": "^10.0.1",
"ng2-charts": "^4.1.1", "ng2-charts": "^4.1.1",
"node-fetch": "^3.3.2",
"pdfjs-dist": "5.1.91", "pdfjs-dist": "5.1.91",
"raphael": "2.3.0", "raphael": "2.3.0",
"rxjs": "7.8.1", "rxjs": "7.8.1",
"superagent": "^9.0.1", "superagent": "^9.0.1",
"ts-morph": "^20.0.0", "ts-morph": "^20.0.0",
"tslib": "2.8.1", "tslib": "2.8.1",
"zone.js": "0.14.10" "zone.js": "0.14.8"
}, },
"devDependencies": { "devDependencies": {
"@alfresco/eslint-plugin-eslint-angular": "file:lib/eslint-angular", "@alfresco/eslint-plugin-eslint-angular": "file:lib/eslint-angular",
"@angular-devkit/architect": "0.1802.13", "@angular-devkit/architect": "0.1701.4",
"@angular-devkit/build-angular": "18.2.14", "@angular-devkit/build-angular": "17.3.16",
"@angular-devkit/core": "18.2.13", "@angular-devkit/core": "17.1.4",
"@angular-devkit/schematics": "18.2.13", "@angular-devkit/schematics": "17.1.4",
"@angular-eslint/eslint-plugin": "17.0.1", "@angular-eslint/eslint-plugin": "17.0.1",
"@angular-eslint/eslint-plugin-template": "17.0.1", "@angular-eslint/eslint-plugin-template": "17.0.1",
"@angular-eslint/template-parser": "17.0.1", "@angular-eslint/template-parser": "17.0.1",
"@angular/cli": "~17.1.0", "@angular/cli": "~17.1.0",
"@angular/compiler-cli": "18.2.13", "@angular/compiler-cli": "17.1.3",
"@chromatic-com/storybook": "1.7.0", "@chromatic-com/storybook": "1.7.0",
"@editorjs/code": "2.9.3", "@editorjs/code": "2.9.3",
"@editorjs/editorjs": "2.30.8", "@editorjs/editorjs": "2.30.8",
@ -84,20 +84,20 @@
"@editorjs/inline-code": "1.5.1", "@editorjs/inline-code": "1.5.1",
"@editorjs/list": "2.0.4", "@editorjs/list": "2.0.4",
"@editorjs/marker": "1.4.0", "@editorjs/marker": "1.4.0",
"@editorjs/paragraph": "^2.11.7", "@editorjs/paragraph": "2.11.7",
"@editorjs/underline": "1.2.1", "@editorjs/underline": "1.2.1",
"@nx/angular": "19.2.0", "@nx/angular": "17.3.1",
"@nx/eslint-plugin": "20.6.0", "@nx/eslint-plugin": "20.6.0",
"@nx/js": "18.3.5", "@nx/js": "17.3.1",
"@nx/node": "20.6.2", "@nx/node": "20.6.2",
"@nx/storybook": "20.6.4", "@nx/storybook": "20.6.4",
"@nx/workspace": "18.3.5", "@nx/workspace": "17.3.1",
"@paperist/types-remark": "0.1.3", "@paperist/types-remark": "0.1.3",
"@playwright/test": "1.46.1", "@playwright/test": "1.46.1",
"@schematics/angular": "17.1.4", "@schematics/angular": "17.1.4",
"@storybook/addon-essentials": "8.4.7", "@storybook/addon-essentials": "8.4.7",
"@storybook/addon-interactions": "8.4.7", "@storybook/addon-interactions": "8.4.7",
"@storybook/angular": "8.4.7", "@storybook/angular": "^8.4.6",
"@storybook/core-server": "8.4.7", "@storybook/core-server": "8.4.7",
"@storybook/manager-api": "^8.4.5", "@storybook/manager-api": "^8.4.5",
"@storybook/theming": "^8.2.9", "@storybook/theming": "^8.2.9",
@ -108,6 +108,7 @@
"@types/jest": "^29.5.14", "@types/jest": "^29.5.14",
"@types/jsdom": "^21.1.5", "@types/jsdom": "^21.1.5",
"@types/minimatch": "5.1.2", "@types/minimatch": "5.1.2",
"@types/mocha": "^10.0.6",
"@types/node": "^18.16.9", "@types/node": "^18.16.9",
"@types/pdfjs-dist": "2.10.378", "@types/pdfjs-dist": "2.10.378",
"@types/shelljs": "^0.8.15", "@types/shelljs": "^0.8.15",
@ -116,7 +117,6 @@
"@typescript-eslint/parser": "6.21.0", "@typescript-eslint/parser": "6.21.0",
"@typescript-eslint/typescript-estree": "7.1.1", "@typescript-eslint/typescript-estree": "7.1.1",
"@typescript-eslint/utils": "^8.8.1", "@typescript-eslint/utils": "^8.8.1",
"@valano/change-font-size": "^1.0.1",
"ajv": "^8.12.0", "ajv": "^8.12.0",
"commander": "12.0.0", "commander": "12.0.0",
"dotenv": "16.4.7", "dotenv": "16.4.7",
@ -159,12 +159,9 @@
"lint-staged": "15.2.9", "lint-staged": "15.2.9",
"mocha": "10.7.3", "mocha": "10.7.3",
"moment": "^2.29.4", "moment": "^2.29.4",
"ng-packagr": "18.2.1", "ng-packagr": "17.1.2",
"nock": "13.5.5", "nock": "13.5.5",
"npm-run-all": "^4.1.5",
"nx": "^20.0.0", "nx": "^20.0.0",
"postcss": "8.4.41",
"postcss-sass": "^0.5.0",
"prettier": "2.8.8", "prettier": "2.8.8",
"resize-observer-polyfill": "^1.5.1", "resize-observer-polyfill": "^1.5.1",
"rimraf": "6.0.1", "rimraf": "6.0.1",
@ -174,11 +171,8 @@
"stylelint": "16.8.2", "stylelint": "16.8.2",
"stylelint-config-standard-scss": "^13.1.0", "stylelint-config-standard-scss": "^13.1.0",
"ts-node": "^10.9.2", "ts-node": "^10.9.2",
"tsconfig-paths": "^4.1.1", "typescript": "5.3.3",
"typescript": "5.5.4", "webpack": "5.97.1"
"webdriver-manager": "12.1.9",
"webpack": "5.97.1",
"webpack-cli": "^5.1.4"
}, },
"license": "Apache-2.0", "license": "Apache-2.0",
"engines": { "engines": {