diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 71cced969c..9d773ca4ce 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -18,3 +18,27 @@ updates: open-pull-requests-limit: 15 labels: - 'npm dependencies' + groups: + angular-cli/devkit: + patterns: + - '@angular/cli' + - '@angular-devkit/*' + angular: + patterns: + - '@angular/*' + exclude-patterns: + - '@angular/cli' + angular-eslint: + patterns: + - '@angular-eslint/*' + fortawesome: + patterns: + - '@fortawesome/*' + exclude-patterns: + - '@fortawesome/angular-fontawesome' + commitlint: + patterns: + - '@commitlint/*' + typescript-eslint: + patterns: + - '@typescript-eslint/*' diff --git a/.github/workflows/ci_optimize_svgs.yml b/.github/workflows/ci_optimize_svgs.yml index 420ce66c59..d7bfd7b96e 100644 --- a/.github/workflows/ci_optimize_svgs.yml +++ b/.github/workflows/ci_optimize_svgs.yml @@ -17,15 +17,15 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # ratchet:actions/checkout@v3 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # ratchet:actions/checkout@v3 - name: Optimize SVGs - uses: ericcornelissen/svgo-action@ee5df7574a3256289c336be4a5e3118666096c86 # ratchet:ericcornelissen/svgo-action@v3 + uses: ericcornelissen/svgo-action@7a6e0d9e931266b92d0bd87bca0b84123306f91d # ratchet:ericcornelissen/svgo-action@v4.0.4 id: svgo with: repo-token: ${{secrets.GITHUB_TOKEN}} svgo-version: 3 # defaults to 2 - name: Commit optimizations - uses: stefanzweifel/git-auto-commit-action@3ea6ae190baf489ba007f7c92608f33ce20ef04a # ratchet:stefanzweifel/git-auto-commit-action@v4 + uses: stefanzweifel/git-auto-commit-action@8756aa072ef5b4a080af5dc8fef36c5d586e521d # ratchet:stefanzweifel/git-auto-commit-action@v4 if: ${{steps.svgo.outputs.DID_OPTIMIZE}} with: commit_message: 'fix(assets): optimize ${{steps.svgo.outputs.OPTIMIZED_COUNT}} SVG(s) with SVGO' diff --git a/.github/workflows/ci_workflow.yml b/.github/workflows/ci_workflow.yml index 28e604cf63..9193e48751 100644 --- a/.github/workflows/ci_workflow.yml +++ b/.github/workflows/ci_workflow.yml @@ -23,14 +23,14 @@ jobs: strategy: matrix: os: [ubuntu-latest] - node-version: [14.21, 16.18, 18.x, 20.x] + node-version: [18.13, 20.9] # TODO (when Angular allows it): 21.x steps: - - name: Checkout repo - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # ratchet:actions/checkout@v3 + - name: Checkout repository + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # ratchet:actions/checkout@v3 with: fetch-depth: 0 # Get all history and branches - name: Set up node ${{ matrix.node-version}} - uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # ratchet:actions/setup-node@v3 + uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # ratchet:actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} cache: 'yarn' @@ -41,13 +41,13 @@ jobs: run: | yarn run test:ci - name: Upload code coverage - if: matrix.node-version == 16.18 # upload coverage report for current node version only - uses: codecov/codecov-action@894ff025c7b54547a9a2a1e9f228beae737ad3c2 # ratchet:codecov/codecov-action@v3.1.3 + if: matrix.node-version == 20.9 # upload coverage report for current node version only + uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # ratchet:codecov/codecov-action@v3.1.4 with: flags: unittests env_vars: ${{ matrix.os }}, ${{ matrix.node-version }} - name: Perform SonarCloud Analysis - if: matrix.node-version == 16.18 && github.event_name != 'pull_request' && github.repository_owner == env.MAIN_REPO_OWNER # perform SonarCloud analysis only for current node version and not with pull requests or forks(token issue) + if: matrix.node-version == 20.9 && github.event_name != 'pull_request' && github.repository_owner == env.MAIN_REPO_OWNER # perform SonarCloud analysis only for current node version and not with pull requests or forks(token issue) uses: SonarSource/sonarcloud-github-action@db501078e936e4b4c8773d1bb949ba9ddb7b6b6a # ratchet:SonarSource/sonarcloud-github-action@v1.9 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any @@ -65,12 +65,12 @@ jobs: strategy: matrix: os: [ubuntu-latest] - node-version: [16.18] + node-version: [20.9] steps: - - name: Checkout repo - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # ratchet:actions/checkout@v3 + - name: Checkout repository + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # ratchet:actions/checkout@v3 - name: Set up node ${{ matrix.node-version}} - uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # ratchet:actions/setup-node@v3 + uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # ratchet:actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} cache: 'yarn' @@ -95,7 +95,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - node-version: [16.18] + node-version: [20.9] steps: - name: Get tag version id: get_version diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index a1736a3b28..3a3efd1d47 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -23,18 +23,18 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # ratchet:actions/checkout@v3 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # ratchet:actions/checkout@v3 with: fetch-depth: 2 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@29b1f65c5e92e24fe6b6647da1eaabe529cec70f # ratchet:github/codeql-action/init@v2 + uses: github/codeql-action/init@cdcdbb579706841c47f7063dda365e292e5cad7a # ratchet:github/codeql-action/init@v2.13.4 with: languages: ${{ matrix.language }} # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@29b1f65c5e92e24fe6b6647da1eaabe529cec70f # ratchet:github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@cdcdbb579706841c47f7063dda365e292e5cad7a # ratchet:github/codeql-action/autobuild@v2.13.4 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@29b1f65c5e92e24fe6b6647da1eaabe529cec70f # ratchet:github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@cdcdbb579706841c47f7063dda365e292e5cad7a # ratchet:github/codeql-action/analyze@v2.13.4 with: category: '/language:${{matrix.language}}' diff --git a/CHANGELOG.md b/CHANGELOG.md index ab6e9aa77a..35d8b00466 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,289 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [0.11.1](https://github.com/webern-unibas-ch/awg-app/compare/v0.11.0...v0.11.1) (2023-12-18) + +### ⚠ BREAKING CHANGES + +- **app:** Node v14 and v16 support is removed. Minimal node version is now v18. + +### Bug Fixes + +- **app:** fix lint issues after update to v16 ([47b3c0b](https://github.com/webern-unibas-ch/awg-app/commit/47b3c0b4ca57b555f47c45705b8449d92b0ecdda)) +- **app:** fix linting errors after update to v16 ([0bec15a](https://github.com/webern-unibas-ch/awg-app/commit/0bec15a5d43d6652c5136a6f40539724a0332e66)) +- **app:** fix scss import path after update to v17 ([ac5974a](https://github.com/webern-unibas-ch/awg-app/commit/ac5974ad0cb1185eed3cd5ff10dcd82d30e17de3)) +- **app:** migrate to new control-flow syntax ([c2f729d](https://github.com/webern-unibas-ch/awg-app/commit/c2f729df58571c2df18ca5fd6a44f968b45c67c1)) +- **assets:** fix M317_Sk4-1von4-final ([8251d8a](https://github.com/webern-unibas-ch/awg-app/commit/8251d8a7d90f28861b6eb4b34bcfe45b5d2caca9); thanks to [@masthom](https://github.com/masthom)) +- **assets:** fix n-dash in textcritics for op. 12 ([6339fd7](https://github.com/webern-unibas-ch/awg-app/commit/6339fd79e1b69cb0221aeda9332a5b78773e334d)) +- **assets:** update files for m30 ([#1193](https://github.com/webern-unibas-ch/awg-app/issues/1193)) ([3bc34a8](https://github.com/webern-unibas-ch/awg-app/commit/3bc34a803b6de756dfcaf37b40165463a0ed7500); thanks to [@chael-mi](https://github.com/chael-mi)) +- **assets:** update measures in source description for m34 ([97993ad](https://github.com/webern-unibas-ch/awg-app/commit/97993ad7c0007b31fcf55a7c310671a2827ed2a7); thanks to [@chael-mi](https://github.com/chael-mi)) +- **assets:** update source description for m34 ([57850aa](https://github.com/webern-unibas-ch/awg-app/commit/57850aac9defe17602e4e3cb4940273f249774db); thanks to [@chael-mi](https://github.com/chael-mi)) +- **edition:** disable toggling of UnsupportedTypeCmp on fullScreen ([66a1428](https://github.com/webern-unibas-ch/awg-app/commit/66a1428b798a1ea474beaff376614880e12e92d6)) +- **edition:** fix linting errors after update to v17 ([e5678cb](https://github.com/webern-unibas-ch/awg-app/commit/e5678cb59dc7954b3cc052af85cc3ffd8d512d2d)) +- **edition:** improve check for empty sparql results ([b89b0fb](https://github.com/webern-unibas-ch/awg-app/commit/b89b0fb0d6d13a106b024d965edb8513e954e0d9)) +- **edition:** replace deprecated accordion syntax ([1effcef](https://github.com/webern-unibas-ch/awg-app/commit/1effcef21814b38e6ed7ff3f48fd8feb681f6318)) +- **edition:** use div instead of h2 for header in TriplesEditor ([5f0e0e0](https://github.com/webern-unibas-ch/awg-app/commit/5f0e0e016e5e99121d0d6dd5190b171986a7507c)) +- **gh-actions:** apply suggestions from code review ([393344b](https://github.com/webern-unibas-ch/awg-app/commit/393344b0f8af55efc71cda9c785d661b39510e1c)) +- **gh-actions:** apply suggestions from code review ([973b921](https://github.com/webern-unibas-ch/awg-app/commit/973b921ac124fcf84d35e15e2a205f3f15d444ec)) + +### Styles + +- **edition:** fix styles of GraphVisualizer in fullscreen mode ([e517ecb](https://github.com/webern-unibas-ch/awg-app/commit/e517ecb443eeed27db3f762d2c45cf4dc9500450)) +- **edition:** improve styles for SparqlEditor ([b80008e](https://github.com/webern-unibas-ch/awg-app/commit/b80008e90400a2f7188d8adfd2be47b721486653)) +- **edition:** improve styles for SparqlEditor ([2445d29](https://github.com/webern-unibas-ch/awg-app/commit/2445d2929cf1db76dbba7b0eae7dc1dd494d6a96)) + +### Code Refactoring + +- **edition:** replace deprecated NgbAccordion in SelectedResultsCmp ([ae313c3](https://github.com/webern-unibas-ch/awg-app/commit/ae313c3d5cae7683672d9788196628e26d8b758d)) +- **edition:** replace deprecated NgbAccordion in SparqlEditor ([93f043a](https://github.com/webern-unibas-ch/awg-app/commit/93f043a2f42f88f3bac575b807cf082c2faeb2dc)) +- **edition:** replace deprecated NgbAccordion in UnsupportedTypeCmp ([6b622d7](https://github.com/webern-unibas-ch/awg-app/commit/6b622d733affb5de0d03e7b4bdf4ff2968a56ba4)) +- **search:** replace deprecated NgbAccordion in LinkedObjectsCmp ([54fa23e](https://github.com/webern-unibas-ch/awg-app/commit/54fa23e11823ec53159625b3155e75128c6ab119)) + +### Documentation + +- **app:** add missing jsdocs ([349df13](https://github.com/webern-unibas-ch/awg-app/commit/349df13a4e44ff7889c5fc4e36889094de56a940)) +- **edition:** fix typo in ngbAccordion comments ([ba95e1b](https://github.com/webern-unibas-ch/awg-app/commit/ba95e1b0ae9ac66523bdb6ab91d4993786802e14)) +- **README:** add badge for node version ([1eefb4c](https://github.com/webern-unibas-ch/awg-app/commit/1eefb4c3ffce7d013b9f7632cbd30ba1a3ad2783)) + +### Tests + +- **app:** fix syntax of some more tests ([0e2ee55](https://github.com/webern-unibas-ch/awg-app/commit/0e2ee55788cf8c9b68994395291c6e352ea1ea82)) +- **core:** fix tests for interceptors after update to v17 ([e6125c2](https://github.com/webern-unibas-ch/awg-app/commit/e6125c2739756af02564c07bb8aff08a9b01e52c)) +- **edition:** fix tests after bootstrap upgrade ([c9c2b05](https://github.com/webern-unibas-ch/awg-app/commit/c9c2b05a4ffa91b9439b41d4d770c439596893b6)) +- **edition:** fix tests for components with new accordion ([f0e03a8](https://github.com/webern-unibas-ch/awg-app/commit/f0e03a8d727a84add45dbeff1198764586e1aa07)) + +### Continuous Integration + +- **app:** exclude Node 21 from ci workflow for now ([045b900](https://github.com/webern-unibas-ch/awg-app/commit/045b9006e901e8f740efed5d1cfea6a4671058bc)) +- **gh-actions:** add dependabot groups ([056d1a6](https://github.com/webern-unibas-ch/awg-app/commit/056d1a6e9dc59d10eda6254657096e670cda5d13)) +- **gh-actions:** add groups to dependabot config ([a557761](https://github.com/webern-unibas-ch/awg-app/commit/a557761868bacf431b14d7a92b8569203d9f65aa)) +- **gh-actions:** update dependabot.yml ([e95893f](https://github.com/webern-unibas-ch/awg-app/commit/e95893f7a6ba3c0c4b547848085438fa6b3c008b)) +- **gh-actions:** upgrade to node version 18.16 as default ([c9706dd](https://github.com/webern-unibas-ch/awg-app/commit/c9706dd1c56dff79efa67e862e20c6adccc9b1bb)) +- **github:** update .github/workflows/ci_optimize_svgs.yml ([4b01e73](https://github.com/webern-unibas-ch/awg-app/commit/4b01e738d64fb754870fca05e890473e26fd6eae)) + +### Build System + +- **app:** bump minimal supported node version to 18.13.0 ([894da1d](https://github.com/webern-unibas-ch/awg-app/commit/894da1d180707408befd0c22fca962e8489448e8)) +- **app:** drop support of node 16 ([cecfce3](https://github.com/webern-unibas-ch/awg-app/commit/cecfce30afcf8416fb5f70d5b67f6c45ab2a48ae)) +- **app:** increase budget ([6c0f70d](https://github.com/webern-unibas-ch/awg-app/commit/6c0f70d37b77eef07de469246101846f7a484ac8)) +- **app:** remove node 14 support ([25772de](https://github.com/webern-unibas-ch/awg-app/commit/25772de1d1308656939e40e427da7cf84e3fdb96)) +- **app:** switch to node 20 as default ([4d8a5a9](https://github.com/webern-unibas-ch/awg-app/commit/4d8a5a97bca2a4f84eaa7f70e497b0275c73a926)) +- **deps-dev:** bump [@angular-eslint](https://github.com/angular-eslint) packages from 16.0.1 to 16.0.2 ([08743df](https://github.com/webern-unibas-ch/awg-app/commit/08743df542fb7d9a654984e5e07e611e6e183ca9)) +- **deps-dev:** bump [@angular-eslint](https://github.com/angular-eslint) packages from 16.0.2 to 16.0.3 ([ccd8b48](https://github.com/webern-unibas-ch/awg-app/commit/ccd8b486f54f2a54625bc5667dbac1d1d8ba5624)) +- **deps-dev:** bump [@angular-eslint](https://github.com/angular-eslint) packages from 16.0.3 to 16.1.0 ([bd987fe](https://github.com/webern-unibas-ch/awg-app/commit/bd987fe57510e8cb74c694693edfe4964bea16a9)) +- **deps-dev:** bump [@angular-eslint](https://github.com/angular-eslint) packages from 16.1.0 to 16.1.1 ([463b496](https://github.com/webern-unibas-ch/awg-app/commit/463b4969930ff81b45481b30cf84000fa4e4bf02)) +- **deps-dev:** bump [@angular-eslint](https://github.com/angular-eslint) related packages to v16 ([afe35e9](https://github.com/webern-unibas-ch/awg-app/commit/afe35e92d7daf7cdfb9a0330dd8f724223e14dff)) +- **deps-dev:** bump [@angular](https://github.com/angular) packages from 16.0.1 to 16.0.2 ([c009ec1](https://github.com/webern-unibas-ch/awg-app/commit/c009ec16c868c031b0dbfac6ff0174e630f3ef16)) +- **deps-dev:** bump [@angular](https://github.com/angular) packages from 16.0.2 to 16.0.3 ([057e39b](https://github.com/webern-unibas-ch/awg-app/commit/057e39b2ec1e8a1a27d793bfd40bd11fb6294d77)) +- **deps-dev:** bump [@angular](https://github.com/angular) packages from 16.0.3 to 16.0.4 ([2a0eb1a](https://github.com/webern-unibas-ch/awg-app/commit/2a0eb1a23569e9024c148670a82f4a22fbbc7a61)) +- **deps-dev:** bump [@angular](https://github.com/angular) packages from 16.0.4 to 16.0.5 ([cb4b327](https://github.com/webern-unibas-ch/awg-app/commit/cb4b32738cabd262cab8acb4acac4c6d245afb81)) +- **deps-dev:** bump [@commitlint](https://github.com/commitlint) packages from 17.6.3 to 17.6.5 ([76fe873](https://github.com/webern-unibas-ch/awg-app/commit/76fe8730cbbfef827de2c887a7601bf88ce0fba7)) +- **deps-dev:** bump [@commitlint](https://github.com/commitlint) packages from 17.6.5 to 17.6.6 ([d5c506a](https://github.com/webern-unibas-ch/awg-app/commit/d5c506abd03457870830f87a02aa61fa76f69061)) +- **deps-dev:** bump [@commitlint](https://github.com/commitlint) packages from 17.6.6 to 17.6.7 ([c973f17](https://github.com/webern-unibas-ch/awg-app/commit/c973f17f8c2fccfa39ea630f70fc374bf7573dad)) +- **deps-dev:** bump [@commitlint](https://github.com/commitlint) packages from 17.6.7 to 17.7.1 ([04fe427](https://github.com/webern-unibas-ch/awg-app/commit/04fe427b3843f92274ea912f0ff98dda5b9f9bd8)) +- **deps-dev:** bump [@typescript-eslint](https://github.com/typescript-eslint) packages from 5.59.11 to 5.60.0 ([23b8976](https://github.com/webern-unibas-ch/awg-app/commit/23b89763ca58f347188ef6b6238875431c87d5ff)) +- **deps-dev:** bump [@typescript-eslint](https://github.com/typescript-eslint) packages from 5.59.5 to 5.59.7 ([59d6c75](https://github.com/webern-unibas-ch/awg-app/commit/59d6c75c51a9222237cb20bf5a45fd46f81b2ae3)) +- **deps-dev:** bump [@typescript-eslint](https://github.com/typescript-eslint) packages from 5.59.7 to 5.59.9 ([3a9cdac](https://github.com/webern-unibas-ch/awg-app/commit/3a9cdac415c55fd89310496f6c6fa539faffe8f0)) +- **deps-dev:** bump [@typescript-eslint](https://github.com/typescript-eslint) packages from 5.59.9 to 5.59.11 ([b5268d7](https://github.com/webern-unibas-ch/awg-app/commit/b5268d7bd4ac00439430041c3cba63885ca1d73f)) +- **deps-dev:** bump [@typescript-eslint](https://github.com/typescript-eslint) packages from 5.60.0 to 5.60.1 ([e22d014](https://github.com/webern-unibas-ch/awg-app/commit/e22d0143c3e4eb101a0624f58b388c0ee9143eb6)) +- **deps-dev:** bump [@typescript-eslint](https://github.com/typescript-eslint) packages from 5.60.1 to 5.61.0 ([df293b0](https://github.com/webern-unibas-ch/awg-app/commit/df293b07ecd94f4c08828dc2b1078e7285ac0ff7)) +- **deps-dev:** bump [@typescript-eslint](https://github.com/typescript-eslint) packages from 5.61.0 to 5.62.0 ([25a5166](https://github.com/webern-unibas-ch/awg-app/commit/25a5166736278527f4527727b99dad7bd20d151a)) +- **deps-dev:** bump [@typescript-eslint](https://github.com/typescript-eslint) packages from 5.62.0 to 6.0.0 ([5fc762d](https://github.com/webern-unibas-ch/awg-app/commit/5fc762d4ff1227be37b56e862e4257e2581a26c6)) +- **deps-dev:** bump [@typescript-eslint](https://github.com/typescript-eslint) packages from 6.0.0 to 6.1.0 ([03f3e42](https://github.com/webern-unibas-ch/awg-app/commit/03f3e42b13b46dad1356551b1650cbaf59e58d0a)) +- **deps-dev:** bump [@typescript-eslint](https://github.com/typescript-eslint) packages from 6.1.0 to 6.2.0 ([61995d4](https://github.com/webern-unibas-ch/awg-app/commit/61995d4283f651d4a3e0be9636aa5a55f1712270)) +- **deps-dev:** bump [@typescript-eslint](https://github.com/typescript-eslint) packages from 6.2.0 to 6.3.0 ([45946c3](https://github.com/webern-unibas-ch/awg-app/commit/45946c3fedd4b7a3bf7331a395f59ed64802d405)) +- **deps-dev:** bump [@typescript-eslint](https://github.com/typescript-eslint) packages from 6.3.0 to 6.5.0 ([0f932fc](https://github.com/webern-unibas-ch/awg-app/commit/0f932fca7aece06f22a7fd2f79e8236675c37b0f)) +- **deps-dev:** bump @angular-devkit/build-angular ([ef5d9eb](https://github.com/webern-unibas-ch/awg-app/commit/ef5d9ebdcc898bc6a156eb1bfbb86e8b0e249f25)) +- **deps-dev:** bump @angular/{cli,build-angular} from 16.0.1 to 16.0.2 ([225e730](https://github.com/webern-unibas-ch/awg-app/commit/225e730a20d4a2956a3c1010d8e0e0b9ab0e3168)) +- **deps-dev:** bump @angular/{cli,build-angular} from 16.0.3 to 16.0.4 ([b242c62](https://github.com/webern-unibas-ch/awg-app/commit/b242c62fa0d4e6c38e8180cfa3d1f3cd2e95e522)) +- **deps-dev:** bump @angular/{cli,build-angular} from 16.0.4 to 16.0.5 ([2eda7ee](https://github.com/webern-unibas-ch/awg-app/commit/2eda7ee84dbe3aae40409f4c1ff9b664d3ec2ce5)) +- **deps-dev:** bump @angular/{cli,build-angular} from 16.0.5 to 16.1.0 ([24899e8](https://github.com/webern-unibas-ch/awg-app/commit/24899e8e78180118d7830a4c2c7c6f3cdb84ccc7)) +- **deps-dev:** bump @angular/{cli,build-angular} from 16.1.0 to 16.1.1 ([02ea9d4](https://github.com/webern-unibas-ch/awg-app/commit/02ea9d4a72f057871e68a907d40c1bb54cc3a530)) +- **deps-dev:** bump @angular/{cli,build-angular} from 16.1.1 to 16.1.2 ([2b3cf24](https://github.com/webern-unibas-ch/awg-app/commit/2b3cf24e2b5296494ebae0df6876105ad6f024e9)) +- **deps-dev:** bump @angular/{cli,build-angular} from 16.1.2 to 16.1.3 ([d03f4f2](https://github.com/webern-unibas-ch/awg-app/commit/d03f4f26a8306d86e5b7b5323bb7c5c39ae8cdfb)) +- **deps-dev:** bump @angular/{cli,build-angular} from 16.1.3 to 16.1.4 ([f42dfee](https://github.com/webern-unibas-ch/awg-app/commit/f42dfee795b15b68850977fadc2f70250af72cd0)) +- **deps-dev:** bump @angular/{cli,build-angular} from 16.1.4 to 16.1.5 ([be137c5](https://github.com/webern-unibas-ch/awg-app/commit/be137c5a2c542864fdc84f87a7170be9dd95bca5)) +- **deps-dev:** bump @angular/{cli,build-angular} from 16.1.5 to 16.1.6 ([271215f](https://github.com/webern-unibas-ch/awg-app/commit/271215f305055c864bff0f4459eb269641ab7efa)) +- **deps-dev:** bump @angular/{cli,build-angular} from 16.1.6 to 16.2.0 ([7cd1aa5](https://github.com/webern-unibas-ch/awg-app/commit/7cd1aa5040543f24bcbeb69c9c17561f8f02ac25)) +- **deps-dev:** bump @angular/{cli,build-angular} from 16.2.0 to 16.2.1 ([e207122](https://github.com/webern-unibas-ch/awg-app/commit/e2071224050103538aef54eaed1b0c490f4b28cd)) +- **deps-dev:** bump @angular/cli from 16.0.2 to 16.0.3 ([fe75429](https://github.com/webern-unibas-ch/awg-app/commit/fe75429a4fb1133b105e0a128dfe5946e49cc003)) +- **deps-dev:** bump @commitlint/cli from 17.7.1 to 17.7.2 ([814e2b7](https://github.com/webern-unibas-ch/awg-app/commit/814e2b7e2886bd4d6817ba975f8e6a6d557b18f5)) +- **deps-dev:** bump @commitlint/cli from 17.7.2 to 17.8.0 ([dd4419c](https://github.com/webern-unibas-ch/awg-app/commit/dd4419ce7c31d1c8d5ef0d4147fc8419781e4166)) +- **deps-dev:** bump @commitlint/cli from 17.8.0 to 18.0.0 ([657548a](https://github.com/webern-unibas-ch/awg-app/commit/657548afc72c31c977cda1915f5a5e378b48d44a)) +- **deps-dev:** bump @commitlint/cli from 18.0.0 to 18.1.0 ([e1c46c5](https://github.com/webern-unibas-ch/awg-app/commit/e1c46c5eb814451720bf0654cfab7be550731006)) +- **deps-dev:** bump @commitlint/cli from 18.1.0 to 18.2.0 ([facc223](https://github.com/webern-unibas-ch/awg-app/commit/facc2231a181faf73f4e584d60a253a7a0d7df48)) +- **deps-dev:** bump @commitlint/cli from 18.2.0 to 18.4.3 ([06e5ab1](https://github.com/webern-unibas-ch/awg-app/commit/06e5ab185fdee3dd55c929741b84e58e7ef40a06)) +- **deps-dev:** bump @commitlint/config-angular from 17.7.0 to 17.8.0 ([4c2257d](https://github.com/webern-unibas-ch/awg-app/commit/4c2257d1a4bcc514e08d3f359399c9dfb292794e)) +- **deps-dev:** bump @commitlint/config-angular from 17.8.0 to 18.0.0 ([4e0a95a](https://github.com/webern-unibas-ch/awg-app/commit/4e0a95a638882bbd6fa057bfd988265d4e12a459)) +- **deps-dev:** bump @commitlint/config-angular from 18.0.0 to 18.1.0 ([75af917](https://github.com/webern-unibas-ch/awg-app/commit/75af917d36d7e7ce669d72a994caebe5392d1e23)) +- **deps-dev:** bump @commitlint/config-angular from 18.1.0 to 18.4.3 ([7e59dcb](https://github.com/webern-unibas-ch/awg-app/commit/7e59dcbbd1d33efd46c71c72329e0188b4c2ec24)) +- **deps-dev:** bump @compodoc/compodoc from 1.1.19 to 1.1.21 ([c1aaa33](https://github.com/webern-unibas-ch/awg-app/commit/c1aaa333e64dc49ee7c176d507d77a1099b9cc40)) +- **deps-dev:** bump @compodoc/compodoc from 1.1.21 to 1.1.22 ([59bea28](https://github.com/webern-unibas-ch/awg-app/commit/59bea283004d219349e5994f1c5cd25d5fa321fd)) +- **deps-dev:** bump @compodoc/compodoc from 1.1.22 to 1.1.23 ([edaf82b](https://github.com/webern-unibas-ch/awg-app/commit/edaf82badfe688acb7521c99a4aad55409683210)) +- **deps-dev:** bump @types/d3 from 7.4.0 to 7.4.1 ([238ff9b](https://github.com/webern-unibas-ch/awg-app/commit/238ff9be0edcd92fba9e0b3a009da05f16846b9d)) +- **deps-dev:** bump @types/d3 from 7.4.1 to 7.4.2 ([fe09435](https://github.com/webern-unibas-ch/awg-app/commit/fe09435ecf5ffd132c7849adc2888e83365c52dc)) +- **deps-dev:** bump @types/d3 from 7.4.2 to 7.4.3 ([7180d42](https://github.com/webern-unibas-ch/awg-app/commit/7180d424e3f35b1c0e66763ea902bbb18e4ca409)) +- **deps-dev:** bump @types/jasmine from 4.3.1 to 4.3.2 ([7afc871](https://github.com/webern-unibas-ch/awg-app/commit/7afc871c76d2fd24e223927ec618448f8f2d38e5)) +- **deps-dev:** bump @types/jasmine from 4.3.4 to 4.3.5 ([c83f06d](https://github.com/webern-unibas-ch/awg-app/commit/c83f06d1588ecd9d3844a95648ec12179bcfbba8)) +- **deps-dev:** bump @types/jasmine from 4.3.5 to 4.3.6 ([60900e7](https://github.com/webern-unibas-ch/awg-app/commit/60900e791f590fed4de0c1bc805e4b209e716a58)) +- **deps-dev:** bump @types/jasmine from 4.3.6 to 5.1.1 ([88e34ca](https://github.com/webern-unibas-ch/awg-app/commit/88e34ca676e82ae9dceaa6b152da3c0939c8b231)) +- **deps-dev:** bump @types/jasmine from 5.1.1 to 5.1.4 ([b937c34](https://github.com/webern-unibas-ch/awg-app/commit/b937c3449f00173bf4a1aa8ffae5cc6140cfacee)) +- **deps-dev:** bump @types/node from 16.18.27 to 18.16.8 ([802a60b](https://github.com/webern-unibas-ch/awg-app/commit/802a60b2ce5dfb32e48fa68a9ce6f88bfe0f6524)) +- **deps-dev:** bump @types/node from 18.16.14 to 18.16.16 ([5087558](https://github.com/webern-unibas-ch/awg-app/commit/5087558727533787b10f126b8dfe54ec50504148)) +- **deps-dev:** bump @types/node from 18.16.16 to 18.16.18 ([eaa9276](https://github.com/webern-unibas-ch/awg-app/commit/eaa927650d7c8d9981d4de94ca648773f8235c6e)) +- **deps-dev:** bump @types/node from 18.16.18 to 18.17.1 ([37345b3](https://github.com/webern-unibas-ch/awg-app/commit/37345b32af5e3f226afb5000b68aa17a9f9d448f)) +- **deps-dev:** bump @types/node from 18.16.8 to 18.16.14 ([83c0728](https://github.com/webern-unibas-ch/awg-app/commit/83c072808930f65a207b320c5650887a83184d9a)) +- **deps-dev:** bump @types/node from 18.17.1 to 18.17.4 ([e341179](https://github.com/webern-unibas-ch/awg-app/commit/e341179e5553888477691c352f0c793801d7c01e)) +- **deps-dev:** bump @types/node from 18.17.14 to 18.19.3 ([3f9b0a9](https://github.com/webern-unibas-ch/awg-app/commit/3f9b0a9513f32ff01996bc8bc872721bb0ea8ea1)) +- **deps-dev:** bump @types/node from 18.17.4 to 18.17.14 ([a629b06](https://github.com/webern-unibas-ch/awg-app/commit/a629b06b231511f82040b23c1e3c897364d89c65)) +- **deps-dev:** bump angular-cli-ghpages from 1.0.5 to 1.0.6 ([2d6a1d1](https://github.com/webern-unibas-ch/awg-app/commit/2d6a1d1bad242096d4351c95cc8a8e5f0839902c)) +- **deps-dev:** bump angular-cli-ghpages from 1.0.6 to 1.0.7 ([f7b7ade](https://github.com/webern-unibas-ch/awg-app/commit/f7b7aded1766675b1eddf6d7171ae6b32b10f94d)) +- **deps-dev:** bump conventional-recommended-bump from 6.1.0 to 7.0.1 ([7af4e57](https://github.com/webern-unibas-ch/awg-app/commit/7af4e574a24881178c309dde7bf0216658108864)) +- **deps-dev:** bump conventional-recommended-bump from 7.0.1 to 8.0.0 ([46fd461](https://github.com/webern-unibas-ch/awg-app/commit/46fd461f76089a67188b53542e2474a1822b4c17)) +- **deps-dev:** bump conventional-recommended-bump from 8.0.0 to 9.0.0 ([c987fb7](https://github.com/webern-unibas-ch/awg-app/commit/c987fb70cac4d035cd90a5dd508f6d7c668b87f2)) +- **deps-dev:** bump eslint from 8.40.0 to 8.41.0 ([8028df4](https://github.com/webern-unibas-ch/awg-app/commit/8028df4d78859df03e47992cca714f98a71265a0)) +- **deps-dev:** bump eslint from 8.41.0 to 8.42.0 ([d12ff0c](https://github.com/webern-unibas-ch/awg-app/commit/d12ff0ca836f12b35031a53ea20b9420110ff912)) +- **deps-dev:** bump eslint from 8.42.0 to 8.43.0 ([b96582e](https://github.com/webern-unibas-ch/awg-app/commit/b96582e3349322897a64c59921f00051a7049d00)) +- **deps-dev:** bump eslint from 8.43.0 to 8.44.0 ([032cd02](https://github.com/webern-unibas-ch/awg-app/commit/032cd025cd50d1cb9aa37c1c652bd85225066a13)) +- **deps-dev:** bump eslint from 8.44.0 to 8.45.0 ([47bacf3](https://github.com/webern-unibas-ch/awg-app/commit/47bacf3814fa1a2aa977334f7ad0de9caf139803)) +- **deps-dev:** bump eslint from 8.45.0 to 8.46.0 ([aa63714](https://github.com/webern-unibas-ch/awg-app/commit/aa63714eb8d94a94bb4a740aa0a68a1b3bdb5559)) +- **deps-dev:** bump eslint from 8.46.0 to 8.48.0 ([7f3cad2](https://github.com/webern-unibas-ch/awg-app/commit/7f3cad2ffa533fc454bc473931b5218079d432ba)) +- **deps-dev:** bump eslint from 8.48.0 to 8.49.0 ([82f7518](https://github.com/webern-unibas-ch/awg-app/commit/82f75182702dbfaf3e59f25063a6bce556868ce4)) +- **deps-dev:** bump eslint from 8.49.0 to 8.50.0 ([3622198](https://github.com/webern-unibas-ch/awg-app/commit/3622198b323c525457c650ab2fa3b3e6f4b3bb50)) +- **deps-dev:** bump eslint from 8.50.0 to 8.51.0 ([3c6ff6f](https://github.com/webern-unibas-ch/awg-app/commit/3c6ff6f7762c8b5396bcbcb50f4888e1cdd294ee)) +- **deps-dev:** bump eslint from 8.51.0 to 8.52.0 ([47e7fa8](https://github.com/webern-unibas-ch/awg-app/commit/47e7fa8cd96a65b03b3c0e919bcb70b4566bd34c)) +- **deps-dev:** bump eslint from 8.52.0 to 8.53.0 ([6778689](https://github.com/webern-unibas-ch/awg-app/commit/6778689900f4cc44d36ddb06b04b647212aac721)) +- **deps-dev:** bump eslint from 8.53.0 to 8.54.0 ([2360026](https://github.com/webern-unibas-ch/awg-app/commit/2360026f64f0799eb3fd64e09f4345079ca4939d)) +- **deps-dev:** bump eslint from 8.54.0 to 8.55.0 ([0550b57](https://github.com/webern-unibas-ch/awg-app/commit/0550b57e0d6ab1ce2bf44d540212edad3d3f79c7)) +- **deps-dev:** bump eslint from 8.55.0 to 8.56.0 ([127e6ec](https://github.com/webern-unibas-ch/awg-app/commit/127e6ec06a55a1673dd1c355cbf2128615f4c5a3)) +- **deps-dev:** bump eslint-config-prettier from 8.8.0 to 8.9.0 ([626ba65](https://github.com/webern-unibas-ch/awg-app/commit/626ba6547229b124650ebdfc5e279d3c1d898cd5)) +- **deps-dev:** bump eslint-config-prettier from 8.9.0 to 9.0.0 ([9c49399](https://github.com/webern-unibas-ch/awg-app/commit/9c493991db786ca8a2f2e3b106660e494f9e90aa)) +- **deps-dev:** bump eslint-config-prettier from 9.0.0 to 9.1.0 ([165f054](https://github.com/webern-unibas-ch/awg-app/commit/165f05426e30a96154b6786a4368e450dbc54996)) +- **deps-dev:** bump eslint-plugin-deprecation from 1.4.1 to 1.5.0 ([5024069](https://github.com/webern-unibas-ch/awg-app/commit/502406907b254ca76d8cc5fea78fe86fc049c386)) +- **deps-dev:** bump eslint-plugin-deprecation from 1.5.0 to 2.0.0 ([536833a](https://github.com/webern-unibas-ch/awg-app/commit/536833a43aba898a761b95ad7c92c6e3fae2bdcf)) +- **deps-dev:** bump eslint-plugin-import from 2.27.5 to 2.28.0 ([f36d17d](https://github.com/webern-unibas-ch/awg-app/commit/f36d17d506edb6c815089c220a62351fa4067e17)) +- **deps-dev:** bump eslint-plugin-import from 2.28.0 to 2.28.1 ([839b7b0](https://github.com/webern-unibas-ch/awg-app/commit/839b7b05fdd76e7e87068ebccce9301869bb1378)) +- **deps-dev:** bump eslint-plugin-import from 2.28.1 to 2.29.0 ([3dbde8b](https://github.com/webern-unibas-ch/awg-app/commit/3dbde8b6f15cf64be68c1a7bd448c3706a56442c)) +- **deps-dev:** bump eslint-plugin-import from 2.29.0 to 2.29.1 ([7495eb7](https://github.com/webern-unibas-ch/awg-app/commit/7495eb7127a337394c40207331510b394b40f668)) +- **deps-dev:** bump eslint-plugin-jsdoc from 43.0.7 to 44.2.3 ([b66d3fc](https://github.com/webern-unibas-ch/awg-app/commit/b66d3fc49baaa60cf0bf252c2229feb44e535c5f)) +- **deps-dev:** bump eslint-plugin-jsdoc from 44.2.3 to 44.2.4 ([3facb82](https://github.com/webern-unibas-ch/awg-app/commit/3facb82e8d4b781e34d6290b36cb5fad02266829)) +- **deps-dev:** bump eslint-plugin-jsdoc from 44.2.4 to 44.2.7 ([1da63a2](https://github.com/webern-unibas-ch/awg-app/commit/1da63a2b7b7212f47a3c5ac19ecddb747500b4dc)) +- **deps-dev:** bump eslint-plugin-jsdoc from 44.2.7 to 46.2.4 ([62a8a10](https://github.com/webern-unibas-ch/awg-app/commit/62a8a10041ec0525dabca1afea2fb96d217ceb6f)) +- **deps-dev:** bump eslint-plugin-jsdoc from 46.2.4 to 46.2.6 ([1ad71f0](https://github.com/webern-unibas-ch/awg-app/commit/1ad71f0a4575f5be7108ac2a12a467b5b895b359)) +- **deps-dev:** bump eslint-plugin-jsdoc from 46.2.6 to 46.4.2 ([9fa34a5](https://github.com/webern-unibas-ch/awg-app/commit/9fa34a55bfe4d1c4f34a849add24293d0ddb1c83)) +- **deps-dev:** bump eslint-plugin-jsdoc from 46.4.2 to 46.4.3 ([4d93ac4](https://github.com/webern-unibas-ch/awg-app/commit/4d93ac4e59803ff9b77f80f4f35d162ba09898e1)) +- **deps-dev:** bump eslint-plugin-jsdoc from 46.4.3 to 46.4.4 ([2f398c7](https://github.com/webern-unibas-ch/awg-app/commit/2f398c74b74934dd756c2e37d890e3af35ae113a)) +- **deps-dev:** bump eslint-plugin-jsdoc from 46.4.4 to 46.4.5 ([31475bf](https://github.com/webern-unibas-ch/awg-app/commit/31475bfa2153b77ae55fd5fdc351c064fcee9e1b)) +- **deps-dev:** bump eslint-plugin-jsdoc from 46.4.5 to 46.4.6 ([4e28491](https://github.com/webern-unibas-ch/awg-app/commit/4e2849155474cef0c6dd28e0b46401e6019fc820)) +- **deps-dev:** bump eslint-plugin-jsdoc from 46.4.6 to 46.5.1 ([a430c6e](https://github.com/webern-unibas-ch/awg-app/commit/a430c6e5f8b7ba41e1d46c2d35ee4729a0305aca)) +- **deps-dev:** bump eslint-plugin-jsdoc from 46.5.1 to 46.8.1 ([f63eaa5](https://github.com/webern-unibas-ch/awg-app/commit/f63eaa5e29047408fedbf18a07894b40ddc3a6ac)) +- **deps-dev:** bump eslint-plugin-jsdoc from 46.8.1 to 46.8.2 ([2126ce3](https://github.com/webern-unibas-ch/awg-app/commit/2126ce3302eb21b764a3db638378ee9dfec2b82f)) +- **deps-dev:** bump eslint-plugin-jsdoc from 46.8.2 to 46.9.1 ([24b7ef8](https://github.com/webern-unibas-ch/awg-app/commit/24b7ef8cba373a770f3eec40e22de2b57d187385)) +- **deps-dev:** bump eslint-plugin-prettier from 4.2.1 to 5.0.0 ([9fb65f1](https://github.com/webern-unibas-ch/awg-app/commit/9fb65f1e074fc302d3e3ae3324d47321a8d2527e)) +- **deps-dev:** bump eslint-plugin-prettier from 5.0.0 to 5.0.1 ([e009c34](https://github.com/webern-unibas-ch/awg-app/commit/e009c344fdcc3a4da2db7035438adb0fa1fdbb2b)) +- **deps-dev:** bump jasmine-core from 4.6.0 to 5.0.1 ([63458f0](https://github.com/webern-unibas-ch/awg-app/commit/63458f0fd082239a72e333be36d496e1fb4b3c58)) +- **deps-dev:** bump jasmine-core from 5.0.1 to 5.1.0 ([18e0542](https://github.com/webern-unibas-ch/awg-app/commit/18e05424a39b3c59ff1fe6057edfe223476cc09b)) +- **deps-dev:** bump jasmine-core from 5.1.0 to 5.1.1 ([25ee394](https://github.com/webern-unibas-ch/awg-app/commit/25ee394aa5596d48a367db77c990556c0fd7c6ef)) +- **deps-dev:** bump karma-coverage from 2.2.0 to 2.2.1 ([ae6590f](https://github.com/webern-unibas-ch/awg-app/commit/ae6590f31678be3167dd68a670b983232621c84f)) +- **deps-dev:** bump karma-jasmine-html-reporter from 2.0.0 to 2.1.0 ([3efab3e](https://github.com/webern-unibas-ch/awg-app/commit/3efab3eaf62d00d64180b92336ba574a39f6b61b)) +- **deps-dev:** bump lint-staged from 13.2.2 to 13.2.3 ([25b0942](https://github.com/webern-unibas-ch/awg-app/commit/25b09428f7f8d7ec5b1c2bf2bbe00f222d0a9196)) +- **deps-dev:** bump lint-staged from 13.2.3 to 14.0.1 ([098e5aa](https://github.com/webern-unibas-ch/awg-app/commit/098e5aa7f7cf2d73d5b737f47c6560d7353528ed)) +- **deps-dev:** bump lint-staged from 14.0.1 to 15.0.2 ([3a759a6](https://github.com/webern-unibas-ch/awg-app/commit/3a759a60d11fd0d9956b3ddd967a44ef96dc5c6d)) +- **deps-dev:** bump lint-staged from 15.0.2 to 15.1.0 ([0573820](https://github.com/webern-unibas-ch/awg-app/commit/05738209019a77370b8ad3c01f55d687b337db1c)) +- **deps-dev:** bump lint-staged from 15.1.0 to 15.2.0 ([7c441ad](https://github.com/webern-unibas-ch/awg-app/commit/7c441ad214810bf2f255ecbb511022eca38b89f5)) +- **deps-dev:** bump prettier from 2.8.8 to 3.0.0 ([4dd14ef](https://github.com/webern-unibas-ch/awg-app/commit/4dd14ef30dcc841adc8666efcf2cda754fbb2cd3)) +- **deps-dev:** bump prettier from 3.0.0 to 3.0.1 ([63824b6](https://github.com/webern-unibas-ch/awg-app/commit/63824b62d128ff652a406a276e2af1aebe0db8a7)) +- **deps-dev:** bump prettier from 3.0.1 to 3.0.3 ([8367ab3](https://github.com/webern-unibas-ch/awg-app/commit/8367ab349fb69ebd827945cb30a4f28eeb899c9f)) +- **deps-dev:** bump prettier from 3.0.3 to 3.1.1 ([de60a81](https://github.com/webern-unibas-ch/awg-app/commit/de60a81054a4d6d17065fc13c2e395aa89a4c22a)) +- **deps-dev:** bump the angular-cli-devkit group with 2 updates ([305602d](https://github.com/webern-unibas-ch/awg-app/commit/305602d52137ee7b4ec07f1739cb8519c67d3696)) +- **deps-dev:** bump the angular-cli-devkit group with 2 updates ([325c694](https://github.com/webern-unibas-ch/awg-app/commit/325c694312cb8c09dd76d3b56981d8032e4df556)) +- **deps-dev:** bump the angular-cli-devkit group with 2 updates ([9353ba7](https://github.com/webern-unibas-ch/awg-app/commit/9353ba7ab8704532aa14a410f35f866339739ce0)) +- **deps-dev:** bump the angular-cli-devkit group with 2 updates ([01a7918](https://github.com/webern-unibas-ch/awg-app/commit/01a79184ceec5af5b93cb1a114d7b51533fcf9c1)) +- **deps-dev:** bump the angular-cli-devkit group with 2 updates ([91e9326](https://github.com/webern-unibas-ch/awg-app/commit/91e9326e5a6b8830051b81696374ee4f3ab099e1)) +- **deps-dev:** bump the angular-eslint group with 5 updates ([c965190](https://github.com/webern-unibas-ch/awg-app/commit/c9651900c8fb77c462d2a3f57b17ef6ebf392771)) +- **deps-dev:** bump the angular-eslint group with 5 updates ([2b862d6](https://github.com/webern-unibas-ch/awg-app/commit/2b862d6d05aec3c85b390136e9462ccdc8a6958a)) +- **deps-dev:** bump the typescript-eslint group with 2 updates ([4aac2d2](https://github.com/webern-unibas-ch/awg-app/commit/4aac2d29e886b41357ca0fba57c2b34262fe86ac)) +- **deps-dev:** bump the typescript-eslint group with 2 updates ([3fc34b6](https://github.com/webern-unibas-ch/awg-app/commit/3fc34b60f3c6b90b1632ecff6313604f52a4e491)) +- **deps-dev:** bump the typescript-eslint group with 2 updates ([c732cb1](https://github.com/webern-unibas-ch/awg-app/commit/c732cb13d889320f9f097d08fa4bc85597d4fc34)) +- **deps-dev:** bump the typescript-eslint group with 2 updates ([08ea678](https://github.com/webern-unibas-ch/awg-app/commit/08ea6783300b91e112b7cc2c066c2159fe87edad)) +- **deps-dev:** bump the typescript-eslint group with 2 updates ([a5f1c5b](https://github.com/webern-unibas-ch/awg-app/commit/a5f1c5b9afe3b49e62faf9f72b0ed5b7f48e2d2f)) +- **deps-dev:** bump the typescript-eslint group with 2 updates ([965cf0f](https://github.com/webern-unibas-ch/awg-app/commit/965cf0f145cfd910c40022640a21058f0e36903f)) +- **deps-dev:** bump the typescript-eslint group with 2 updates ([51b5450](https://github.com/webern-unibas-ch/awg-app/commit/51b5450ab986d5c87ca6453f9c62f4ec801637b6)) +- **deps-dev:** bump the typescript-eslint group with 2 updates ([4ecd004](https://github.com/webern-unibas-ch/awg-app/commit/4ecd0048c443c62abd90d81d2e3ac2f1d4d638f7)) +- **deps-dev:** bump typescript from 4.9.5 to 5.0.4 ([52c5281](https://github.com/webern-unibas-ch/awg-app/commit/52c52815c49a54db20be803f3262b6e85bb57460)) +- **deps-dev:** bump webpack-bundle-analyzer from 4.8.0 to 4.9.0 ([f6d3523](https://github.com/webern-unibas-ch/awg-app/commit/f6d352366512c57b913f457c60de30154d4062b5)) +- **deps-dev:** bump webpack-bundle-analyzer from 4.9.0 to 4.9.1 ([78f3625](https://github.com/webern-unibas-ch/awg-app/commit/78f3625a80ac8c07dfc41fb57318fe1ba6cd1a54)) +- **deps-dev:** bump webpack-bundle-analyzer from 4.9.1 to 4.10.1 ([6cca129](https://github.com/webern-unibas-ch/awg-app/commit/6cca129a0c07d897acb197566b8cab166550284d)) +- **deps-dev:** clean up yarn.lock file after updates ([c0a8f55](https://github.com/webern-unibas-ch/awg-app/commit/c0a8f559bda37471f4838c028eaf8b3b162b82dd)) +- **deps:** add stream ([53510dc](https://github.com/webern-unibas-ch/awg-app/commit/53510dcd1e3a349ee6381337cb0be5f6e2f257ac)) +- **deps:** bump [@angular](https://github.com/angular) packages from 16.0.5 to 16.1.0 ([28cabcc](https://github.com/webern-unibas-ch/awg-app/commit/28cabcc4fdf003f450e35a0b97815f3093125963)) +- **deps:** bump [@angular](https://github.com/angular) packages from 16.1.0 to 16.1.1 ([f0452a0](https://github.com/webern-unibas-ch/awg-app/commit/f0452a0b7389618a6d100fc345f57d839f8380b5)) +- **deps:** bump [@angular](https://github.com/angular) packages from 16.1.1 to 16.1.2 ([ead3571](https://github.com/webern-unibas-ch/awg-app/commit/ead3571b9e8bb9d5b13fad4ceb0dd52f006c086f)) +- **deps:** bump [@angular](https://github.com/angular) packages from 16.1.2 to 16.1.3 ([e014d9d](https://github.com/webern-unibas-ch/awg-app/commit/e014d9d9c2e41577e2ba3c1811aed371ea8515b6)) +- **deps:** bump [@angular](https://github.com/angular) packages from 16.1.3 to 16.1.4 ([6063d2c](https://github.com/webern-unibas-ch/awg-app/commit/6063d2c4a1a0a9e3a5e9b1a5ab3d354ae9582740)) +- **deps:** bump [@angular](https://github.com/angular) packages from 16.1.4 to 16.1.5 ([62ce61c](https://github.com/webern-unibas-ch/awg-app/commit/62ce61cdaae2b6453a9b28540bf7063b3c877201)) +- **deps:** bump [@angular](https://github.com/angular) packages from 16.1.5 to 16.1.6 ([4cf70bf](https://github.com/webern-unibas-ch/awg-app/commit/4cf70bfe9f5b0c0650b891b061626ba8e0b9d150)) +- **deps:** bump [@angular](https://github.com/angular) packages from 16.1.6 to 16.1.7 ([eb933cf](https://github.com/webern-unibas-ch/awg-app/commit/eb933cf8f57f6163ce8dc4b46e20d861d90cc6e2)) +- **deps:** bump [@angular](https://github.com/angular) packages from 16.1.7 to 16.2.0 ([83f6b33](https://github.com/webern-unibas-ch/awg-app/commit/83f6b339fbdd0ec2dbd2bd37f7c2ad8f4f6f4bf6)) +- **deps:** bump [@angular](https://github.com/angular) packages from 16.2.0 to 16.2.3 ([2c35f47](https://github.com/webern-unibas-ch/awg-app/commit/2c35f47ff144557be27ce71218ce7925b67ae978)) +- **deps:** bump [@fortawesome](https://github.com/fortawesome) packages from 6.4.0 to 6.4.2 ([b4a30a4](https://github.com/webern-unibas-ch/awg-app/commit/b4a30a4c0ac117d579ac6aaa0fdc2041481456fd)) +- **deps:** bump [@fortawesome](https://github.com/fortawesome) packages from 6.4.2 to 6.5.1 ([62a8c47](https://github.com/webern-unibas-ch/awg-app/commit/62a8c470e3f3b337afe26d540803304c905b268d)) +- **deps:** bump @babel/traverse from 7.22.5 to 7.23.2 ([648d789](https://github.com/webern-unibas-ch/awg-app/commit/648d789485f72cede80f24c98280e7ffcd0ebee9)) +- **deps:** bump @codemirror/legacy-modes from 6.3.2 to 6.3.3 ([cd9f3ae](https://github.com/webern-unibas-ch/awg-app/commit/cd9f3ae15342d6255c79e83fe909f04668bfb3a3)) +- **deps:** bump @fortawesome/angular-fontawesome from 0.12.1 to 0.13.0 ([788f9a7](https://github.com/webern-unibas-ch/awg-app/commit/788f9a7831e7f71e71d3177a15206b9093d416a2)) +- **deps:** bump @fortawesome/angular-fontawesome from 0.13.0 to 0.14.0 ([88b1f0a](https://github.com/webern-unibas-ch/awg-app/commit/88b1f0a56f5d1ffc478991d8d7700bc4dfca225c)) +- **deps:** bump @ng-bootstrap/ng-bootstrap from 14.1.1 to 15.0.1 ([21d55a3](https://github.com/webern-unibas-ch/awg-app/commit/21d55a3d7120b9583799e1be92275ffdcacd7f12)) +- **deps:** bump @ng-bootstrap/ng-bootstrap from 15.0.1 to 15.1.0 ([b2aafee](https://github.com/webern-unibas-ch/awg-app/commit/b2aafeef71a390f5f4267636a8ca4b5ec15c4f5b)) +- **deps:** bump @ng-bootstrap/ng-bootstrap from 15.1.0 to 15.1.1 ([478b46c](https://github.com/webern-unibas-ch/awg-app/commit/478b46c3959f768893ef8cf52feafc683caa115e)) +- **deps:** bump @ng-bootstrap/ng-bootstrap from 15.1.1 to 15.1.2 ([cf5365e](https://github.com/webern-unibas-ch/awg-app/commit/cf5365e9e2e2e7c05ce927c45a7a8193f6b49400)) +- **deps:** bump @ng-bootstrap/ng-bootstrap from 15.1.2 to 16.0.0 ([f99f0ab](https://github.com/webern-unibas-ch/awg-app/commit/f99f0abf5b93795af09fffea3c480e9fa817f5d2)) +- **deps:** bump @popperjs/core from 2.11.7 to 2.11.8 ([9a48a76](https://github.com/webern-unibas-ch/awg-app/commit/9a48a7673a9b118993bafe5d2d639ca795c1ea53)) +- **deps:** bump actions/checkout from 3.5.2 to 3.5.3 ([2823b2b](https://github.com/webern-unibas-ch/awg-app/commit/2823b2bd520742e0ce176785daec753d42cf2961)) +- **deps:** bump actions/checkout from 3.5.3 to 3.6.0 ([2fb4e30](https://github.com/webern-unibas-ch/awg-app/commit/2fb4e3079451390e5f176f54c5cd8933d70eb861)) +- **deps:** bump actions/checkout from 3.6.0 to 4.0.0 ([71e3666](https://github.com/webern-unibas-ch/awg-app/commit/71e3666c018a68989131eb95f698afe2cb5d8bd0)) +- **deps:** bump actions/checkout from 4.0.0 to 4.1.0 ([7b45a44](https://github.com/webern-unibas-ch/awg-app/commit/7b45a44e8d6f076117eaf21cb61559f474318236)) +- **deps:** bump actions/checkout from 4.1.0 to 4.1.1 ([a982643](https://github.com/webern-unibas-ch/awg-app/commit/a982643e53d949d8afd66a8aa42656a16be3c728)) +- **deps:** bump actions/setup-node from 3.6.0 to 3.7.0 ([5086b81](https://github.com/webern-unibas-ch/awg-app/commit/5086b815102a86a3c871c0f213b4fc75449126ed)) +- **deps:** bump actions/setup-node from 3.7.0 to 3.8.1 ([c394401](https://github.com/webern-unibas-ch/awg-app/commit/c394401530efb85ab6ea27f33f665bb873c3490a)) +- **deps:** bump actions/setup-node from 3.8.1 to 3.8.2 ([f3c8aa1](https://github.com/webern-unibas-ch/awg-app/commit/f3c8aa19e1543281a8f9b1629c6ce07a4222abe7)) +- **deps:** bump actions/setup-node from 3.8.2 to 4.0.0 ([faab6b0](https://github.com/webern-unibas-ch/awg-app/commit/faab6b0b8bd2600cda0bfad5d070fc44dc26f30b)) +- **deps:** bump actions/setup-node from 4.0.0 to 4.0.1 ([7bf0612](https://github.com/webern-unibas-ch/awg-app/commit/7bf061218611e778064b5e0a0a3501da5a087b39)) +- **deps:** bump axios from 1.4.0 to 1.6.2 ([264d314](https://github.com/webern-unibas-ch/awg-app/commit/264d314d3c729391050069c81462083d215512bb)) +- **deps:** bump bootstrap from 5.3.0 to 5.3.1 ([0b30b61](https://github.com/webern-unibas-ch/awg-app/commit/0b30b61e0bbf664cb447d8e3190d7a81466dbfea)) +- **deps:** bump bootstrap from 5.3.1 to 5.3.2 ([a192202](https://github.com/webern-unibas-ch/awg-app/commit/a192202f16d83dedc3472551cba6d7a7434717b5)) +- **deps:** bump codecov/codecov-action from 3.1.3 to 3.1.4 ([e99d79e](https://github.com/webern-unibas-ch/awg-app/commit/e99d79e66959141449fd9559c196a30b3c8f4e4e)) +- **deps:** bump crypto-js from 4.1.1 to 4.2.0 ([dfb4a66](https://github.com/webern-unibas-ch/awg-app/commit/dfb4a6652f19f0a9ed48157f0599c07734d6900d)) +- **deps:** bump ericcornelissen/svgo-action from 3.1.4 to 4.0.0 ([fff90c0](https://github.com/webern-unibas-ch/awg-app/commit/fff90c041caf958b4276f8fbbe7d020b2de34dac)) +- **deps:** bump ericcornelissen/svgo-action from 4.0.0 to 4.0.4 ([73a8c2f](https://github.com/webern-unibas-ch/awg-app/commit/73a8c2fd4453f42ee73ddf7e9e4d2b03d2a88015)) +- **deps:** bump github/codeql-action from 2.3.3 to 2.3.5 ([d2d4bca](https://github.com/webern-unibas-ch/awg-app/commit/d2d4bcaf2ea50fb478ac77c37f3554e1c9687e66)) +- **deps:** bump github/codeql-action from 2.3.5 to 2.3.6 ([1bcecb2](https://github.com/webern-unibas-ch/awg-app/commit/1bcecb28dd65949b4d9125a1afabea53a013b482)) +- **deps:** bump github/codeql-action from 2.3.6 to 2.13.4 ([a36bff3](https://github.com/webern-unibas-ch/awg-app/commit/a36bff34e14fec57e77aa1433e51e9a61e331729)) +- **deps:** bump n3 from 1.16.4 to 1.17.0 ([e7ec5b1](https://github.com/webern-unibas-ch/awg-app/commit/e7ec5b191604c58f3a6d2db514134ce4180ddac2)) +- **deps:** bump n3 from 1.17.0 to 1.17.1 ([2c38c1b](https://github.com/webern-unibas-ch/awg-app/commit/2c38c1b5707b134aeab1166f09a1a8a2bc9ffd94)) +- **deps:** bump n3 from 1.17.1 to 1.17.2 ([83be72a](https://github.com/webern-unibas-ch/awg-app/commit/83be72a61b19977509b9c2de4d9dd5342bc8234c)) +- **deps:** bump semver from 5.7.1 to 5.7.2 ([57a04f8](https://github.com/webern-unibas-ch/awg-app/commit/57a04f8d4cc7259bf60998b88a8ba9d4dd610b01)) +- **deps:** bump socket.io-parser from 4.2.2 to 4.2.3 ([#1002](https://github.com/webern-unibas-ch/awg-app/issues/1002)) ([395466c](https://github.com/webern-unibas-ch/awg-app/commit/395466ca196d40b47175235d5cafbbb2fcf7dfd4)) +- **deps:** bump stefanzweifel/git-auto-commit-action ([38dccda](https://github.com/webern-unibas-ch/awg-app/commit/38dccda4bdaf4206419428a16eb99da8a76324b6)) +- **deps:** bump the angular group with 11 updates ([eefb937](https://github.com/webern-unibas-ch/awg-app/commit/eefb9371c874c8d87485515aea1aa5a2d45c2434)) +- **deps:** bump the angular group with 11 updates ([47fbfee](https://github.com/webern-unibas-ch/awg-app/commit/47fbfee2cb3ffe492cfe2a04bf65299da3d802ce)) +- **deps:** bump the angular group with 11 updates ([21694a6](https://github.com/webern-unibas-ch/awg-app/commit/21694a6c246e443013f28d480bfdb8fd8df04432)) +- **deps:** bump the angular group with 11 updates ([19933cc](https://github.com/webern-unibas-ch/awg-app/commit/19933ccdbda729efe62908d7ef13aa044603d962)) +- **deps:** bump the angular group with 11 updates ([e8b46ea](https://github.com/webern-unibas-ch/awg-app/commit/e8b46eae190c381069a9b9f6c5491a40e648eb4b)) +- **deps:** bump the angular group with 11 updates ([dd0c09e](https://github.com/webern-unibas-ch/awg-app/commit/dd0c09ecdd1441de3c6066815d7132186598bacb)) +- **deps:** bump tslib from 2.5.0 to 2.5.2 ([9bec3b4](https://github.com/webern-unibas-ch/awg-app/commit/9bec3b4acd81ce3b6ab28d0a65c2475d7efaadae)) +- **deps:** bump tslib from 2.5.2 to 2.5.3 ([9295fb9](https://github.com/webern-unibas-ch/awg-app/commit/9295fb9f4f634ec61332a6276deab38934f1c8f6)) +- **deps:** bump tslib from 2.5.3 to 2.6.0 ([b59e2ed](https://github.com/webern-unibas-ch/awg-app/commit/b59e2edb79f36c086d3e3ce51ee95aa05acd6134)) +- **deps:** bump tslib from 2.6.0 to 2.6.1 ([af12637](https://github.com/webern-unibas-ch/awg-app/commit/af12637cffa4d357bbe5ce3be7c72554a5ac069b)) +- **deps:** bump tslib from 2.6.1 to 2.6.2 ([4fd7eed](https://github.com/webern-unibas-ch/awg-app/commit/4fd7eed54f5177409feaeb22d469c40af3b20725)) +- **deps:** bump undici from 5.22.1 to 5.26.3 ([e2f8ac1](https://github.com/webern-unibas-ch/awg-app/commit/e2f8ac14d32a3d39f2b49d3bc694390982016116)) +- **deps:** bump word-wrap from 1.2.3 to 1.2.4 ([e9f7a24](https://github.com/webern-unibas-ch/awg-app/commit/e9f7a244cfb9f70e1e764759d445136ee7ba4f3d)) +- **deps:** bump zone.js from 0.13.0 to 0.13.1 ([fa03983](https://github.com/webern-unibas-ch/awg-app/commit/fa039834ef802b24b68224f50896a93817c43dcc)) +- **deps:** bump zone.js from 0.13.1 to 0.13.3 ([1dbb783](https://github.com/webern-unibas-ch/awg-app/commit/1dbb783f6f15450a78112cba20788d6a022733d5)) +- **deps:** fix bootstrap imports ([d7b5eb2](https://github.com/webern-unibas-ch/awg-app/commit/d7b5eb23bcb8f006f0a19f7a3b9b19d52f791e58)) +- **deps:** rebuild yarn.lock after updates ([b976fae](https://github.com/webern-unibas-ch/awg-app/commit/b976fae3ca8f655de34e4ce312a7300d9d13e41f)) +- **deps:** update [@angular](https://github.com/angular) to version 16 ([cc121b4](https://github.com/webern-unibas-ch/awg-app/commit/cc121b4f33d99e9fe86f58ebb876b46c96118eb9)) +- **deps:** update [@angular](https://github.com/angular) to version 17 ([b94b035](https://github.com/webern-unibas-ch/awg-app/commit/b94b0354621f2ccdbd60232a13949ae7f12c981d)) + ## [0.11.0](https://github.com/webern-unibas-ch/awg-app/compare/v0.10.2...v0.11.0) (2023-05-09) ### Features diff --git a/README.md b/README.md index 32f48dc5f2..ca510012c2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ # AWG App ![GitHub package.json version](https://img.shields.io/github/package-json/v/webern-unibas-ch/awg-app.svg) -[![GitHub package.json dependency version (prod)](https://img.shields.io/github/package-json/dependency-version/webern-unibas-ch/awg-app/@angular/core?color=red&label=angular&logo=angular)](https://github.com/angular/angular) +[![GitHub package.json dependency version (prod)](https://img.shields.io/github/package-json/dependency-version/webern-unibas-ch/awg-app/@angular/core?color=blue&label=angular&logo=angular)](https://github.com/angular/angular) +![Node.js version](https://img.shields.io/badge/node.js-%3E=v18.13.0-blue) ![GitHub commit activity](https://img.shields.io/github/commit-activity/m/webern-unibas-ch/awg-app) ![CI Workflow](https://github.com/webern-unibas-ch/awg-app/actions/workflows/ci_workflow.yml/badge.svg) [![codecov](https://codecov.io/gh/webern-unibas-ch/awg-app/branch/main/graph/badge.svg)](https://codecov.io/gh/webern-unibas-ch/awg-app) diff --git a/angular.json b/angular.json index 83f432e444..19d949c716 100644 --- a/angular.json +++ b/angular.json @@ -45,7 +45,7 @@ { "type": "initial", "maximumWarning": "2mb", - "maximumError": "3mb" + "maximumError": "4mb" }, { "type": "anyComponentStyle", @@ -96,10 +96,10 @@ "options": {}, "configurations": { "production": { - "browserTarget": "awg-app:build:production" + "buildTarget": "awg-app:build:production" }, "development": { - "browserTarget": "awg-app:build:development" + "buildTarget": "awg-app:build:development" } }, "defaultConfiguration": "development" @@ -107,7 +107,7 @@ "extract-i18n": { "builder": "@angular-devkit/build-angular:extract-i18n", "options": { - "browserTarget": "awg-app:build" + "buildTarget": "awg-app:build" } }, "test": { diff --git a/package.json b/package.json index 5248e20021..88f99cfa89 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "awg-app", - "version": "0.11.0", + "version": "0.11.1", "license": "MIT", "author": { "name": "Stefan Münnich", @@ -82,24 +82,24 @@ "deploy:ci": "ng deploy --no-build --message=\"Release $npm_package_name (v$npm_package_version) on gh-pages\"" }, "dependencies": { - "@angular/animations": "^15.2.9", - "@angular/common": "^15.2.9", - "@angular/compiler": "^15.2.9", - "@angular/core": "^15.2.9", - "@angular/forms": "^15.2.9", - "@angular/localize": "^15.2.9", - "@angular/platform-browser": "^15.2.9", - "@angular/platform-browser-dynamic": "^15.2.9", - "@angular/platform-server": "^15.2.9", - "@angular/router": "^15.2.9", - "@codemirror/legacy-modes": "^6.3.2", - "@fortawesome/angular-fontawesome": "^0.12.1", - "@fortawesome/fontawesome-svg-core": "^6.4.0", - "@fortawesome/free-solid-svg-icons": "^6.4.0", + "@angular/animations": "^17.0.7", + "@angular/common": "^17.0.7", + "@angular/compiler": "^17.0.7", + "@angular/core": "^17.0.7", + "@angular/forms": "^17.0.7", + "@angular/localize": "^17.0.7", + "@angular/platform-browser": "^17.0.7", + "@angular/platform-browser-dynamic": "^17.0.7", + "@angular/platform-server": "^17.0.7", + "@angular/router": "^17.0.7", + "@codemirror/legacy-modes": "^6.3.3", + "@fortawesome/angular-fontawesome": "^0.14.0", + "@fortawesome/fontawesome-svg-core": "^6.5.1", + "@fortawesome/free-solid-svg-icons": "^6.5.1", "@kolkov/ngx-gallery": "2.0.1", - "@ng-bootstrap/ng-bootstrap": "^14.1.1", - "@popperjs/core": "^2.11.7", - "bootstrap": "^5.2.3", + "@ng-bootstrap/ng-bootstrap": "^16.0.0", + "@popperjs/core": "^2.11.8", + "bootstrap": "^5.3.2", "codemirror": "^6.0.1", "d3-drag": "^3.0.0", "d3-fetch": "^3.0.1", @@ -108,58 +108,59 @@ "d3-zoom": "^3.0.0", "font-awesome": "^4.7.0", "json2typescript": "^1.5.1", - "n3": "^1.16.4", + "n3": "^1.17.2", "ngx-json-viewer": "^3.2.1", "rdfstore": "musicenfanthen/rdfstore-js#v0.9.18-alpha.12", "rxjs": "~7.8.1", "snapsvg": "^0.5.1", - "tslib": "^2.5.0", - "zone.js": "~0.13.0" + "stream": "^0.0.2", + "tslib": "^2.6.2", + "zone.js": "~0.14.2" }, "devDependencies": { - "@angular-devkit/build-angular": "^15.2.8", - "@angular-eslint/builder": "^15.2.1", - "@angular-eslint/eslint-plugin": "^15.2.1", - "@angular-eslint/eslint-plugin-template": "^15.2.1", - "@angular-eslint/schematics": "^15.2.1", - "@angular-eslint/template-parser": "^15.2.1", - "@angular/cli": "^15.2.8", - "@angular/compiler-cli": "^15.2.9", - "@commitlint/cli": "^17.6.3", - "@commitlint/config-angular": "^17.6.3", - "@compodoc/compodoc": "^1.1.19", - "@types/d3": "^7.4.0", - "@types/jasmine": "~4.3.1", - "@types/node": "^16.18.27", - "@typescript-eslint/eslint-plugin": "^5.59.5", - "@typescript-eslint/parser": "^5.59.5", - "angular-cli-ghpages": "^1.0.5", - "conventional-recommended-bump": "^6.1.0", - "eslint": "^8.40.0", - "eslint-config-prettier": "^8.8.0", + "@angular-devkit/build-angular": "^17.0.7", + "@angular-eslint/builder": "^17.1.1", + "@angular-eslint/eslint-plugin": "^17.1.1", + "@angular-eslint/eslint-plugin-template": "^17.1.1", + "@angular-eslint/schematics": "^17.1.1", + "@angular-eslint/template-parser": "^17.1.1", + "@angular/cli": "^17.0.7", + "@angular/compiler-cli": "^17.0.7", + "@commitlint/cli": "^18.4.3", + "@commitlint/config-angular": "^18.4.3", + "@compodoc/compodoc": "^1.1.23", + "@types/d3": "^7.4.3", + "@types/jasmine": "~5.1.4", + "@types/node": "^18.19.3", + "@typescript-eslint/eslint-plugin": "^6.14.0", + "@typescript-eslint/parser": "^6.14.0", + "angular-cli-ghpages": "^1.0.7", + "conventional-recommended-bump": "^9.0.0", + "eslint": "^8.56.0", + "eslint-config-prettier": "^9.1.0", "eslint-plugin-angular": "^4.1.0", - "eslint-plugin-deprecation": "^1.4.1", - "eslint-plugin-import": "^2.27.5", - "eslint-plugin-jsdoc": "^43.0.7", - "eslint-plugin-prettier": "^4.2.1", + "eslint-plugin-deprecation": "^2.0.0", + "eslint-plugin-import": "^2.29.1", + "eslint-plugin-jsdoc": "^46.9.1", + "eslint-plugin-prettier": "^5.0.1", "gzipper": "^7.2.0", "husky": "^8.0.3", - "jasmine-core": "~4.6.0", + "jasmine-core": "~5.1.1", "karma": "~6.4.2", "karma-chrome-launcher": "~3.2.0", - "karma-coverage": "^2.2.0", + "karma-coverage": "^2.2.1", "karma-jasmine": "~5.1.0", - "karma-jasmine-html-reporter": "^2.0.0", - "lint-staged": "^13.2.2", - "prettier": "^2.8.8", + "karma-jasmine-html-reporter": "^2.1.0", + "lint-staged": "^15.2.0", + "prettier": "^3.1.1", "source-map-explorer": "^2.5.3", "standard-version": "^9.5.0", - "typescript": "~4.9.5", - "webpack-bundle-analyzer": "^4.8.0" + "typescript": "~5.2.2", + "webpack-bundle-analyzer": "^4.10.1" }, "engines": { - "node": ">= 14.17.0", - "npm": ">= 6.14.13", + "node": ">= 18.13.0", + "npm": ">= 8.19.3", "yarn": "^1.22.0" } } diff --git a/src/app/app.globals.ts b/src/app/app.globals.ts index 162baa38b4..45f7e2e285 100644 --- a/src/app/app.globals.ts +++ b/src/app/app.globals.ts @@ -1,15 +1,15 @@ // THIS IS AN AUTO-GENERATED FILE. DO NOT CHANGE IT MANUALLY! -// Generated last time on Tue, May 9, 2023 5:47:59 PM +// Generated last time on Mon, Dec 18, 2023 4:45:49 PM /** * The latest version of the AWG App */ -export const appVersion = '0.11.0'; +export const appVersion = '0.11.1'; /** * The release date of the latest version of the AWG App */ -export const appVersionReleaseDate = '09. Mai 2023'; +export const appVersionReleaseDate = '18. Dezember 2023'; /** * The URL of the AWG App diff --git a/src/app/core/interceptors/caching/caching.interceptor.spec.ts b/src/app/core/interceptors/caching/caching.interceptor.spec.ts index 8100f9419e..abeeefaa83 100644 --- a/src/app/core/interceptors/caching/caching.interceptor.spec.ts +++ b/src/app/core/interceptors/caching/caching.interceptor.spec.ts @@ -1,11 +1,11 @@ import { + HTTP_INTERCEPTORS, HttpClient, HttpErrorResponse, HttpHeaders, HttpInterceptor, HttpRequest, HttpResponse, - HTTP_INTERCEPTORS, } from '@angular/common/http'; import { HttpClientTestingModule, HttpTestingController, TestRequest } from '@angular/common/http/testing'; import { TestBed, waitForAsync } from '@angular/core/testing'; @@ -15,7 +15,7 @@ import { throwError as observableThrowError } from 'rxjs'; import Spy = jasmine.Spy; import { cleanStylesFromDOM } from '@testing/clean-up-helper'; -import { expectSpyCall } from '@testing/expect-helper'; +import { expectSpyCall, expectToBe, expectToEqual } from '@testing/expect-helper'; import { getInterceptorInstance } from '@testing/interceptor-helper'; import { mockCache, mockConsole } from '@testing/mock-helper'; @@ -94,7 +94,7 @@ describe('CachingInterceptor (DONE)', () => { httpClient.get('/foo/bar').subscribe({ next: data => { - expect(data).toEqual(testData); + expectToEqual(data, testData); }, }); @@ -104,7 +104,7 @@ describe('CachingInterceptor (DONE)', () => { }); // Check for GET request - expect(call.request.method).toBe('GET'); + expectToBe(call.request.method, 'GET'); // Respond with mocked data call.flush(testData); @@ -126,13 +126,9 @@ describe('CachingInterceptor (DONE)', () => { expectSpyCall(cachePutSpy, 1, 0); // Mock cache has created response - expect(mockCache.get(expectedRequest)) - .withContext(`should equal ${expectedResponse}`) - .toEqual(expectedResponse); + expectToEqual(mockCache.get(expectedRequest), expectedResponse); // Spied service call returns created response - expect(httpCacheService.get(expectedRequest)) - .withContext(`should equal ${expectedResponse}`) - .toEqual(expectedResponse); + expectToEqual(httpCacheService.get(expectedRequest), expectedResponse); // Real service does not have created response expect((httpCacheService as any)._cachedResponses.has(expectedRequest.urlWithParams)).toBeFalse(); })); @@ -145,7 +141,7 @@ describe('CachingInterceptor (DONE)', () => { it('... should use mock console', () => { console.error('Test'); - expect(mockConsole.get(0)).toBe('Test'); + expectToBe(mockConsole.get(0), 'Test'); }); it('... should clear mock console after each run', () => { @@ -170,7 +166,7 @@ describe('CachingInterceptor (DONE)', () => { // Subscribe to GET Http Request httpClient.get(expectedUrl).subscribe({ next: data => { - expect(data).toEqual(testData); + expectToEqual(data, testData); }, }); @@ -187,14 +183,14 @@ describe('CachingInterceptor (DONE)', () => { // Subscribe to PUT Http Request httpClient.post(expectedUrl, testData).subscribe({ next: data => { - expect(data).toEqual(testData); + expectToEqual(data, testData); }, }); // Expect an HTTP request call = httpTestingController.expectOne(expectedUrl); - expect(call.request.method).withContext('should be POST').toEqual('POST'); - expect(call.request.url).withContext(`should be ${expectedUrl}`).toEqual(expectedUrl); + expectToEqual(call.request.method, 'POST'); + expectToEqual(call.request.url, expectedUrl); // Expect request to return the expected response after POST call.event(expectedResponse); @@ -208,14 +204,14 @@ describe('CachingInterceptor (DONE)', () => { // Subscribe to PUT Http Request httpClient.put(expectedUrl, testData).subscribe({ next: data => { - expect(data).toEqual(testData); + expectToEqual(data, testData); }, }); // Expect an HTTP request call = httpTestingController.expectOne(expectedUrl); - expect(call.request.method).withContext('should be PUT').toEqual('PUT'); - expect(call.request.url).withContext(`should be ${expectedUrl}`).toEqual(expectedUrl); + expectToEqual(call.request.method, 'PUT'); + expectToEqual(call.request.url, expectedUrl); // Expect request to return the expected response after PUT call.event(expectedResponse); @@ -236,7 +232,7 @@ describe('CachingInterceptor (DONE)', () => { // Subscribe to GET Http Request const sub = httpClient.get(expectedUrl).subscribe({ next: data => { - expect(data).toEqual(testData); + expectToEqual(data, testData); }, }); @@ -249,7 +245,10 @@ describe('CachingInterceptor (DONE)', () => { call.event(expectedResponse); // Make sure real request and expectedRequest are identical - expect(call.request).toEqual(expectedRequest); + expectToBe(call.request.method, expectedRequest.method); + expectToBe(call.request.urlWithParams, expectedRequest.urlWithParams); + expectToBe(call.request.responseType, expectedRequest.responseType); + expectToEqual(call.request.headers, expectedRequest.headers); // Expect spy calls expectSpyCall(interceptSpy, 1, call.request); @@ -257,9 +256,7 @@ describe('CachingInterceptor (DONE)', () => { expectSpyCall(cachePutSpy, 1, call.request); // Mock cache has created response - expect(mockCache.get(expectedRequest)) - .withContext(`should equal ${expectedResponse}`) - .toEqual(expectedResponse); + expectToEqual(mockCache.get(expectedRequest), expectedResponse); // Unsubscribe from first request sub.unsubscribe(); @@ -268,7 +265,7 @@ describe('CachingInterceptor (DONE)', () => { // Subscribe to new GET Http Request httpClient.get(expectedUrl).subscribe({ next: data => { - expect(data).toEqual(testData); + expectToEqual(data, testData); }, }); @@ -285,9 +282,7 @@ describe('CachingInterceptor (DONE)', () => { expectSpyCall(cachePutSpy, 1, call.request); // Mock cache still has response - expect(mockCache.get(expectedRequest)) - .withContext(`should equal ${expectedResponse}`) - .toEqual(expectedResponse); + expectToEqual(mockCache.get(expectedRequest), expectedResponse); })); it('... should put new requests to cache via httpCacheService', waitForAsync(() => { @@ -299,7 +294,7 @@ describe('CachingInterceptor (DONE)', () => { // Subscribe to GET Http Request httpClient.get(expectedUrl).subscribe({ next: data => { - expect(data).toEqual(testData); + expectToEqual(data, testData); }, }); @@ -316,12 +311,15 @@ describe('CachingInterceptor (DONE)', () => { expectSpyCall(cacheGetSpy, 1, call.request); expectSpyCall(cachePutSpy, 1, call.request); + // Make sure real request and expectedRequest are identical + expectToBe(call.request.method, expectedRequest.method); + expectToBe(call.request.urlWithParams, expectedRequest.urlWithParams); + expectToBe(call.request.responseType, expectedRequest.responseType); + expectToEqual(call.request.headers, expectedRequest.headers); + // Expect new cached response - expect(call.request).toEqual(expectedRequest); expect(mockCache.get(expectedRequest)).toBeTruthy(); - expect(mockCache.get(expectedRequest)) - .withContext(`should equal ${expectedResponse}`) - .toEqual(expectedResponse); + expectToEqual(mockCache.get(expectedRequest), expectedResponse); })); it('... should throw an error and log to console if request fails with HttpErrorResponse', waitForAsync(() => { @@ -343,7 +341,7 @@ describe('CachingInterceptor (DONE)', () => { // Subscribe to GET Http Request httpClient.get(expectedUrl).subscribe({ next: data => { - expect(data).toEqual(testData); + expectToEqual(data, testData); }, }); @@ -360,7 +358,7 @@ describe('CachingInterceptor (DONE)', () => { cachingInterceptor.intercept(call.request, httpHandlerSpy).subscribe({ next: () => fail('should have been failed'), error: err => { - expect(err).toEqual(expectedError); + expectToEqual(err, expectedError); }, complete: () => { fail('should have been failed'); @@ -369,7 +367,7 @@ describe('CachingInterceptor (DONE)', () => { expectSpyCall(cachePutSpy, 0); expectSpyCall(consoleSpy, 1, expectedLogMessage); - expect(mockConsole.get(0)).withContext(`should be ${expectedLogMessage}`).toBe(expectedLogMessage); + expectToBe(mockConsole.get(0), expectedLogMessage); })); it('... should throw an error, but not log to console if request fails with another error', waitForAsync(() => { @@ -385,7 +383,7 @@ describe('CachingInterceptor (DONE)', () => { // Subscribe to GET Http Request httpClient.get(expectedUrl).subscribe({ next: data => { - expect(data).toEqual(testData); + expectToEqual(data, testData); }, }); @@ -402,7 +400,7 @@ describe('CachingInterceptor (DONE)', () => { cachingInterceptor.intercept(call.request, httpHandlerSpy).subscribe({ next: () => fail('should not call next'), error: err => { - expect(err).toEqual(expectedError); + expectToEqual(err, expectedError); }, complete: () => { fail('should not complete'); diff --git a/src/app/core/interceptors/loading/loading.interceptor.spec.ts b/src/app/core/interceptors/loading/loading.interceptor.spec.ts index 717cf97e5b..148e8fe6b1 100644 --- a/src/app/core/interceptors/loading/loading.interceptor.spec.ts +++ b/src/app/core/interceptors/loading/loading.interceptor.spec.ts @@ -1,4 +1,4 @@ -import { HttpClient, HttpInterceptor, HttpResponse, HTTP_INTERCEPTORS } from '@angular/common/http'; +import { HTTP_INTERCEPTORS, HttpClient, HttpInterceptor, HttpResponse } from '@angular/common/http'; import { HttpClientTestingModule, HttpTestingController, TestRequest } from '@angular/common/http/testing'; import { TestBed, waitForAsync } from '@angular/core/testing'; import { Data } from '@angular/router'; @@ -7,7 +7,7 @@ import { of as observableOf, throwError as observableThrowError } from 'rxjs'; import Spy = jasmine.Spy; import { cleanStylesFromDOM } from '@testing/clean-up-helper'; -import { expectSpyCall } from '@testing/expect-helper'; +import { expectSpyCall, expectToBe, expectToEqual } from '@testing/expect-helper'; import { getInterceptorInstance } from '@testing/interceptor-helper'; import { AppConfig } from '@awg-app/app.config'; @@ -77,7 +77,7 @@ describe('LoadingInterceptor (DONE)', () => { httpClient.get('/foo/bar').subscribe({ next: data => { - expect(data).toEqual(testData); + expectToEqual(data, testData); }, }); @@ -87,7 +87,7 @@ describe('LoadingInterceptor (DONE)', () => { }); // Check for GET request - expect(call.request.method).toBe('GET'); + expectToBe(call.request.method, 'GET'); // Respond with mocked data call.flush(testData); @@ -104,7 +104,7 @@ describe('LoadingInterceptor (DONE)', () => { // Subscribe to GET Http Request httpClient.get(expectedUrl).subscribe({ next: data => { - expect(data).toEqual(testData); + expectToEqual(data, testData); }, }); })); @@ -165,7 +165,7 @@ describe('LoadingInterceptor (DONE)', () => { // Add another request to the stack loadingInterceptor.intercept(call.request, httpHandlerSpy).subscribe({ next: response => { - expect(response).withContext(`should equal ${expectedHttpResponse}`).toEqual(expectedHttpResponse); + expectToEqual(response, expectedHttpResponse); }, error: () => { fail('error should not have been called'); @@ -199,7 +199,7 @@ describe('LoadingInterceptor (DONE)', () => { loadingInterceptor.intercept(call.request, httpHandlerSpy).subscribe({ next: () => fail('should have been failed'), error: err => { - expect(err).toEqual(expectedError); + expectToEqual(err, expectedError); }, complete: () => { fail('should have been failed'); diff --git a/src/app/core/navbar/navbar.component.html b/src/app/core/navbar/navbar.component.html index fae487e847..310a798fe3 100644 --- a/src/app/core/navbar/navbar.component.html +++ b/src/app/core/navbar/navbar.component.html @@ -56,33 +56,35 @@ -
- - {{ editionRouteConstants.EDITION_INTRO.full }} - {{ editionRouteConstants.EDITION_SHEETS.full }} - {{ editionRouteConstants.EDITION_REPORT.full }} - -
+ @for (editionComplex of DISPLAYED_EDITION_COMPLEXES; track editionComplex) { +
+ + {{ editionRouteConstants.EDITION_INTRO.full }} + {{ editionRouteConstants.EDITION_SHEETS.full }} + {{ editionRouteConstants.EDITION_REPORT.full }} + +
+ }
  • -
    - - - von {{ pages.length }} -
    -
  • + @if (pages.length > 0) { +
  • +
    + + + + von {{ pages.length }} +
    +
  • + } diff --git a/src/app/shared/table/table.component.html b/src/app/shared/table/table.component.html index f0df7bc0e7..d1bc2a9b4d 100644 --- a/src/app/shared/table/table.component.html +++ b/src/app/shared/table/table.component.html @@ -1,109 +1,134 @@ -
    -
    - -
    - +@if (tableData.filteredRows && tableData.totalRows$ | async; as totalRows) { + +
    + +
    + +
    -
    - - -
    - - - + +} -
    - -
    -
    +} - - - - - - - - - - - -
    - {{ headerLabel }}  -
    - - - - - - iconsrc - - - - -
    - +@if (tableData?.paginatedRows$ | async; as paginatedRows) { + + + + @for (headerLabel of tableData?.header; track headerLabel) { + + } + + + + @for ( + row of paginatedRows + | orderBy: tableOptions.sortKey : tableOptions.reverse : tableOptions.isCaseInsensitive; + track row + ) { + + @for (headerLabel of tableData.header; track headerLabel) { + + } + + } + +
    + {{ headerLabel }}  + @if (headerLabel === tableOptions.selectedKey) { + + } +
    + @if (row[headerLabel].type && row[headerLabel].type === 'uri') { + + + + } + @if (row[headerLabel].type === 'search') { + + @if (row[headerLabel].icon) { + + iconsrc + + } + + } + @if (row[headerLabel].type !== 'uri' && row[headerLabel].type !== 'search') { + + + } +
    +} @else { -
    - -
    - - - +} -
    - - +} diff --git a/src/app/shared/table/table.component.spec.ts b/src/app/shared/table/table.component.spec.ts index a83da10f38..b2bd59f477 100644 --- a/src/app/shared/table/table.component.spec.ts +++ b/src/app/shared/table/table.component.spec.ts @@ -1,5 +1,5 @@ import { Component, DebugElement, EventEmitter, Input, Output } from '@angular/core'; -import { ComponentFixture, fakeAsync, TestBed, waitForAsync } from '@angular/core/testing'; +import { ComponentFixture, TestBed, fakeAsync, waitForAsync } from '@angular/core/testing'; import { FormsModule } from '@angular/forms'; import Spy = jasmine.Spy; @@ -14,6 +14,8 @@ import { BUTTON_CLICK_EVENTS, clickAndAwaitChanges } from '@testing/click-helper import { detectChangesOnPush } from '@testing/detect-changes-on-push-helper'; import { expectSpyCall, + expectToBe, + expectToEqual, getAndExpectDebugElementByCss, getAndExpectDebugElementByDirective, } from '@testing/expect-helper'; @@ -163,18 +165,12 @@ describe('TableComponent', () => { }); it('... should have faSortUp and faSortDown icons', () => { - expect(component.faSortUp).toBeDefined(); - expect(component.faSortUp).withContext(`should be faSortUp`).toBe(faSortUp); - - expect(component.faSortDown).toBeDefined(); - expect(component.faSortDown).withContext(`should be faSortDown`).toBe(faSortDown); + expectToBe(component.faSortUp, faSortUp); + expectToBe(component.faSortDown, faSortDown); }); it('... should have tableOptions', () => { - expect(component.tableOptions).toBeDefined(); - expect(component.tableOptions) - .withContext(`should equal ${expectedTableOptions}`) - .toEqual(expectedTableOptions); + expectToEqual(component.tableOptions, expectedTableOptions); }); it('... should not have called initTable()', () => { @@ -220,22 +216,15 @@ describe('TableComponent', () => { }); it('... should have tableTitle', () => { - expect(component.tableTitle).toBeDefined(); - expect(component.tableTitle).withContext(`should be ${expectedTableTitle}`).toBe(expectedTableTitle); + expectToBe(component.tableTitle, expectedTableTitle); }); it('... should have headerInputData', () => { - expect(component.headerInputData).toBeDefined(); - expect(component.headerInputData) - .withContext(`should equal ${expectedHeaderInputData}`) - .toEqual(expectedHeaderInputData); + expectToEqual(component.headerInputData, expectedHeaderInputData); }); it('... should have rowInputData', () => { - expect(component.rowInputData).toBeDefined(); - expect(component.rowInputData) - .withContext(`should equal ${expectedRowInputData}`) - .toEqual(expectedRowInputData); + expectToEqual(component.rowInputData, expectedRowInputData); }); describe('#initTable()', () => { @@ -251,15 +240,8 @@ describe('TableComponent', () => { it('... with headerInputData and rowInputData', waitForAsync(() => { expect(component.tableData).toBeDefined(); - expect(component.tableData.header).toBeDefined(); - expect(component.tableData.header) - .withContext(`should equal ${expectedHeaderInputData}`) - .toEqual(expectedHeaderInputData); - - expect(component.tableData.filteredRows).toBeDefined(); - expect(component.tableData.filteredRows) - .withContext(`should equal ${expectedRowInputData}`) - .toEqual(expectedRowInputData); + expectToEqual(component.tableData.header, expectedHeaderInputData); + expectToEqual(component.tableData.filteredRows, expectedRowInputData); expect(component.tableData.paginatedRows$).toBeDefined(); expectAsync(lastValueFrom(component.tableData.paginatedRows$)) @@ -282,11 +264,8 @@ describe('TableComponent', () => { expect(component.tableData).toBeDefined(); - expect(component.tableData.header).toBeDefined(); - expect(component.tableData.header).withContext(`should equal []`).toEqual([]); - - expect(component.tableData.filteredRows).toBeDefined(); - expect(component.tableData.filteredRows).withContext(`should equal []`).toEqual([]); + expectToEqual(component.tableData.header, []); + expectToEqual(component.tableData.filteredRows, []); expect(component.tableData.paginatedRows$).toBeDefined(); expectAsync(lastValueFrom(component.tableData.paginatedRows$)) @@ -308,11 +287,8 @@ describe('TableComponent', () => { expect(component.tableData).toBeDefined(); - expect(component.tableData.header).toBeDefined(); - expect(component.tableData.header).withContext(`should equal []`).toEqual([]); - - expect(component.tableData.filteredRows).toBeDefined(); - expect(component.tableData.filteredRows).withContext(`should equal []`).toEqual([]); + expectToEqual(component.tableData.header, []); + expectToEqual(component.tableData.filteredRows, []); expect(component.tableData.paginatedRows$).toBeDefined(); expectAsync(lastValueFrom(component.tableData.paginatedRows$)) @@ -334,11 +310,8 @@ describe('TableComponent', () => { expect(component.tableData).toBeDefined(); - expect(component.tableData.header).toBeDefined(); - expect(component.tableData.header).withContext(`should equal []`).toEqual([]); - - expect(component.tableData.filteredRows).toBeDefined(); - expect(component.tableData.filteredRows).withContext(`should equal []`).toEqual([]); + expectToEqual(component.tableData.header, []); + expectToEqual(component.tableData.filteredRows, []); expect(component.tableData.paginatedRows$).toBeDefined(); expectAsync(lastValueFrom(component.tableData.paginatedRows$)) @@ -354,17 +327,11 @@ describe('TableComponent', () => { }); it('... should set paginatorOptions', () => { - expect(component.paginatorOptions).toBeDefined(); - expect(component.paginatorOptions) - .withContext(`should equal ${expectedPaginatorOptions}`) - .toEqual(expectedPaginatorOptions); + expectToEqual(component.paginatorOptions, expectedPaginatorOptions); }); it('... should set paginatorOptions.collectionSize to tableData.rowInputData.length', () => { - expect(component.paginatorOptions.collectionSize).toBeDefined(); - expect(component.paginatorOptions.collectionSize) - .withContext(`should equal ${expectedPaginatorOptions.collectionSize}`) - .toEqual(expectedPaginatorOptions.collectionSize); + expectToEqual(component.paginatorOptions.collectionSize, expectedPaginatorOptions.collectionSize); }); it('... should set paginatorOptions.collectionSize to 0 if tableData.rowInputData is not given', () => { @@ -372,8 +339,7 @@ describe('TableComponent', () => { component.initTable(); fixture.detectChanges(); - expect(component.paginatorOptions.collectionSize).toBeDefined(); - expect(component.paginatorOptions.collectionSize).withContext(`should equal 0`).toEqual(0); + expectToEqual(component.paginatorOptions.collectionSize, 0); }); it('... should trigger onSort()', () => { @@ -395,7 +361,7 @@ describe('TableComponent', () => { }); it('... should trigger on change of searchFilter in input', async () => { - await fixture.whenStable(); // Needed to wait for the ngModelto be initialized, cf. https://github.com/angular/angular/issues/22606#issuecomment-514760743 + await fixture.whenStable(); // Needed to wait for the ngModel to be initialized, cf. https://github.com/angular/angular/issues/22606#issuecomment-514760743 const expectedSearchFilter = 'test'; const otherSearchFilter = 'other'; @@ -558,10 +524,8 @@ describe('TableComponent', () => { fixture.detectChanges(); expect(component.tableData).toBeDefined(); - expect(component.tableData.filteredRows.length).withContext(`should be 1`).toBe(1); - expect(component.tableData.filteredRows) - .withContext(`should equal ${[expectedRowInputData.at(0)]}`) - .toEqual([expectedRowInputData.at(0)]); + expectToEqual(component.tableData.filteredRows.length, 1); + expectToEqual(component.tableData.filteredRows, [expectedRowInputData.at(0)]); }); it('... by non-matching searchFilter (empty array)', async () => { @@ -570,8 +534,8 @@ describe('TableComponent', () => { fixture.detectChanges(); expect(component.tableData).toBeDefined(); - expect(component.tableData.filteredRows.length).withContext(`should be 0`).toBe(0); - expect(component.tableData.filteredRows).withContext(`should equal empty array`).toEqual([]); + expectToEqual(component.tableData.filteredRows.length, 0); + expectToEqual(component.tableData.filteredRows, []); }); it('... if a rowEntry is null or undefined', async () => { @@ -594,12 +558,8 @@ describe('TableComponent', () => { fixture.detectChanges(); expect(component.tableData).toBeDefined(); - expect(component.tableData.filteredRows.length) - .withContext(`should be ${expectedFilteredRows.length}`) - .toBe(expectedFilteredRows.length); - expect(component.tableData.filteredRows) - .withContext(`should equal ${expectedFilteredRows}`) - .toEqual(expectedFilteredRows); + expectToEqual(component.tableData.filteredRows.length, expectedFilteredRows.length); + expectToEqual(component.tableData.filteredRows, expectedFilteredRows); }); it('... if table data is empty (empty array)', async () => { @@ -610,11 +570,9 @@ describe('TableComponent', () => { expect(component.tableData).toBeDefined(); - expect(component.tableData.header).toBeDefined(); - expect(component.tableData.header).withContext(`should equal []`).toEqual([]); + expectToEqual(component.tableData.header, []); - expect(component.tableData.filteredRows).toBeDefined(); - expect(component.tableData.filteredRows).withContext(`should equal []`).toEqual([]); + expectToEqual(component.tableData.filteredRows, []); expect(component.tableData.paginatedRows$).toBeDefined(); await expectAsync(lastValueFrom(component.tableData.paginatedRows$)) @@ -700,56 +658,50 @@ describe('TableComponent', () => { })); it('... should set tableOptions.selectedKey to the given key', () => { - expect(component.tableOptions.selectedKey).toBeDefined(); - expect(component.tableOptions.selectedKey) - .withContext(`should equal ${expectedHeaderInputData[0]}`) - .toBe(expectedHeaderInputData[0]); + expectToBe(component.tableOptions.selectedKey, expectedHeaderInputData[0]); component.onSort('key'); - expect(component.tableOptions.selectedKey).toBe('key'); + expectToBe(component.tableOptions.selectedKey, 'key'); }); it('... should set tableOptions.sortKey', () => { - expect(component.tableOptions.sortKey).toBeDefined(); - expect(component.tableOptions.sortKey) - .withContext(`should equal ${expectedHeaderInputData[0] + '.label'}`) - .toBe(expectedHeaderInputData[0] + '.label'); + expectToBe(component.tableOptions.sortKey, expectedHeaderInputData[0] + '.label'); component.onSort('key'); - expect(component.tableOptions.sortKey).toBe('key.label'); + expectToBe(component.tableOptions.sortKey, 'key.label'); }); describe('should set tableOptions.reverse', () => { it('... to false by default', () => { - expect(component.tableOptions.reverse).toBe(false); + expectToBe(component.tableOptions.reverse, false); }); it('... to false when called with different keys', () => { - expect(component.tableOptions.reverse).toBe(false); + expectToBe(component.tableOptions.reverse, false); component.onSort('key'); - expect(component.tableOptions.reverse).toBe(false); + expectToBe(component.tableOptions.reverse, false); component.onSort('key2'); - expect(component.tableOptions.reverse).toBe(false); + expectToBe(component.tableOptions.reverse, false); }); it('... toggling false/true if key equals selected key', () => { component.onSort('key'); - expect(component.tableOptions.reverse).toBe(false); + expectToBe(component.tableOptions.reverse, false); component.onSort('key'); - expect(component.tableOptions.reverse).toBe(true); + expectToBe(component.tableOptions.reverse, true); component.onSort('key'); - expect(component.tableOptions.reverse).toBe(false); + expectToBe(component.tableOptions.reverse, false); }); }); @@ -757,10 +709,7 @@ describe('TableComponent', () => { it('... to faSortDown by default', () => { component.onSort('key'); - expect(component.tableOptions.sortIcon).toBeDefined(); - expect(component.tableOptions.sortIcon) - .withContext(`should equal ${faSortDown}`) - .toEqual(faSortDown); + expectToEqual(component.tableOptions.sortIcon, faSortDown); }); it('... to faSortUp if tableOptions.reverse is true', () => { @@ -768,47 +717,38 @@ describe('TableComponent', () => { component.onSort('key'); fixture.detectChanges(); - expect(component.tableOptions.sortIcon).toBeDefined(); - expect(component.tableOptions.sortIcon).withContext(`should equal ${faSortUp}`).toEqual(faSortUp); + expectToEqual(component.tableOptions.sortIcon, faSortUp); }); it('... toggling faSortDown/faSortUp if key equals selected key', () => { component.onSort('key'); - expect(component.tableOptions.sortIcon).toBeDefined(); - expect(component.tableOptions.sortIcon) - .withContext(`should equal ${faSortDown}`) - .toEqual(faSortDown); + expectToEqual(component.tableOptions.sortIcon, faSortDown); component.onSort('key'); - expect(component.tableOptions.sortIcon).toBeDefined(); - expect(component.tableOptions.sortIcon).withContext(`should equal ${faSortUp}`).toEqual(faSortUp); + expectToEqual(component.tableOptions.sortIcon, faSortUp); component.onSort('key'); - expect(component.tableOptions.sortIcon).toBeDefined(); - expect(component.tableOptions.sortIcon) - .withContext(`should equal ${faSortDown}`) - .toEqual(faSortDown); + expectToEqual(component.tableOptions.sortIcon, faSortDown); }); }); describe('should set tableOptions.reverse ', () => { it('... to false by default', () => { - expect(component.tableOptions.reverse).toBeDefined(); - expect(component.tableOptions.reverse).toBe(false); + expectToBe(component.tableOptions.reverse, false); }); it('... toggling true/false if tableOptions.selectedKey is the same as given key', () => { component.tableOptions.selectedKey = expectedHeaderInputData[0]; component.onSort(expectedHeaderInputData[0]); - expect(component.tableOptions.reverse).toBe(true); + expectToBe(component.tableOptions.reverse, true); component.onSort(expectedHeaderInputData[0]); - expect(component.tableOptions.reverse).toBe(false); + expectToBe(component.tableOptions.reverse, false); }); }); @@ -816,16 +756,11 @@ describe('TableComponent', () => { component.onSort(undefined); fixture.detectChanges(); - expect(component.tableOptions.selectedKey).toBeDefined(); - expect(component.tableOptions.selectedKey).toBe(expectedHeaderInputData[0]); - expect(component.tableOptions.sortKey).toBeDefined(); - expect(component.tableOptions.sortKey).toBe(expectedHeaderInputData[0] + '.label'); - expect(component.tableOptions.sortIcon).toBeDefined(); - expect(component.tableOptions.sortIcon).toEqual(faSortDown); - expect(component.tableOptions.reverse).toBeDefined(); - expect(component.tableOptions.reverse).toBe(false); - expect(component.tableOptions.isCaseInsensitive).toBeDefined(); - expect(component.tableOptions.isCaseInsensitive).toBe(false); + expectToBe(component.tableOptions.selectedKey, expectedHeaderInputData[0]); + expectToBe(component.tableOptions.sortKey, expectedHeaderInputData[0] + '.label'); + expectToEqual(component.tableOptions.sortIcon, faSortDown); + expectToBe(component.tableOptions.reverse, false); + expectToBe(component.tableOptions.isCaseInsensitive, false); }); }); @@ -951,23 +886,13 @@ describe('TableComponent', () => { de => de.injector.get(TablePaginationStubComponent) as TablePaginationStubComponent ); - expect(tablePaginationCmps.length).withContext('should have 2 pagination components').toBe(2); - - expect(tablePaginationCmps[0].collectionSize).toBeTruthy(); - expect(tablePaginationCmps[0].collectionSize) - .withContext(`should equal ${expectedRowInputData.length}`) - .toEqual(expectedRowInputData.length); - - expect(tablePaginationCmps[1].collectionSize).toBeTruthy(); - expect(tablePaginationCmps[1].collectionSize) - .withContext(`should equal ${expectedRowInputData.length}`) - .toEqual(expectedRowInputData.length); + expectToBe(tablePaginationCmps.length, 2); - expect(tablePaginationCmps[0].page).toBeTruthy(); - expect(tablePaginationCmps[0].page).withContext(`should be 1`).toBe(1); + expectToEqual(tablePaginationCmps[0].collectionSize, expectedRowInputData.length); + expectToEqual(tablePaginationCmps[1].collectionSize, expectedRowInputData.length); - expect(tablePaginationCmps[1].page).toBeTruthy(); - expect(tablePaginationCmps[1].page).withContext(`should be 1`).toBe(1); + expectToBe(tablePaginationCmps[0].page, 1); + expectToBe(tablePaginationCmps[1].page, 1); }); it('... should display TwelveToneSpinnerComponent (stubbed) while loading (paginatedRows are not available)', () => { diff --git a/src/app/shared/toast/toast.component.html b/src/app/shared/toast/toast.component.html index 3e0673a77f..41efebe2ca 100644 --- a/src/app/shared/toast/toast.component.html +++ b/src/app/shared/toast/toast.component.html @@ -1,13 +1,14 @@ - - - - - - {{ toast.textOrTpl }} - +@for (toast of toastService.toasts; track toast) { + + @if (isTemplate(toast)) { + + } @else { + {{ toast.textOrTpl }} + } + +} diff --git a/src/app/shared/view-handle-button-group/view-handle-button-group.component.html b/src/app/shared/view-handle-button-group/view-handle-button-group.component.html index 5178c38a1e..3c22b80a3a 100644 --- a/src/app/shared/view-handle-button-group/view-handle-button-group.component.html +++ b/src/app/shared/view-handle-button-group/view-handle-button-group.component.html @@ -2,7 +2,7 @@
    - + @for (viewHandle of viewHandles; track viewHandleTracker($index, viewHandle)) { - + }
    diff --git a/src/app/side-info/contact-info/contact-info.component.ts b/src/app/side-info/contact-info/contact-info.component.ts index b7a9c94d63..30a724f45a 100644 --- a/src/app/side-info/contact-info/contact-info.component.ts +++ b/src/app/side-info/contact-info/contact-info.component.ts @@ -62,7 +62,10 @@ export class ContactInfoComponent implements OnInit { * @param {CoreService} coreService Instance of the CoreService. * @param {DomSanitizer} sanitizer Instance of the Angular DomSanitizer. */ - constructor(private coreService: CoreService, private sanitizer: DomSanitizer) {} + constructor( + private coreService: CoreService, + private sanitizer: DomSanitizer + ) {} /** * Angular life cycle hook: ngOnInit. diff --git a/src/app/side-info/resource-info/resource-info.component.html b/src/app/side-info/resource-info/resource-info.component.html index cec41a9c5e..8214d5b914 100644 --- a/src/app/side-info/resource-info/resource-info.component.html +++ b/src/app/side-info/resource-info/resource-info.component.html @@ -14,169 +14,172 @@ ************************************************/ --> -
    -
    -
    - +@if (resourceInfoData) { +
    +
    +
    + +
    +
    + Aktuelle Suchanfrage +
    + @if (resourceInfoData?.searchResults?.query) { + {{ resourceInfoData?.searchResults?.query | json }} + } @else { + --- + } +
    -
    - Aktuelle Suchanfrage -
    - {{ - resourceInfoData?.searchResults?.query | json - }} - - --- - + +
    + + @if (resultSize) { +
    + +
    + } +
    -
    - -
    - - - - - -
    +} diff --git a/src/app/side-info/search-info/search-info.component.html b/src/app/side-info/search-info/search-info.component.html index 1fbdb17338..49d88604be 100644 --- a/src/app/side-info/search-info/search-info.component.html +++ b/src/app/side-info/search-info/search-info.component.html @@ -1,18 +1,24 @@
    -
    - {{ searchInfoHeader }} -
    + @if (searchInfoHeader$ | async; as searchInfoHeader) { +
    + {{ searchInfoHeader }} +
    + }

    - Suchanfrage: {{ searchInfoData?.query | json }} + Suchanfrage: + @if (searchInfoData$ | async; as searchInfoData) { + {{ searchInfoData?.query | json }} + }

    - Suchergebnisse: {{ searchInfoData?.nhits }} + Suchergebnisse: + @if (searchInfoData$ | async; as searchInfoData) { + {{ searchInfoData?.nhits }} + }

    diff --git a/src/app/views/contact-view/contact-view.component.html b/src/app/views/contact-view/contact-view.component.html index 418fdbc1f1..628958e590 100644 --- a/src/app/views/contact-view/contact-view.component.html +++ b/src/app/views/contact-view/contact-view.component.html @@ -12,7 +12,7 @@

    {{ pageMetaData.awgProjectName }}. Projekt-Website: {{ pageMetaData?.awgProjectUrl }} , abgerufen am - {{ today | date : dateFormat }}{{ today | date: dateFormat }}.

    @@ -22,7 +22,7 @@ {{ pageMetaData?.version }} vom {{ pageMetaData?.versionReleaseDate }}): {{ pageMetaData?.awgAppUrl }} , abgerufen am - {{ today | date : dateFormat }}{{ today | date: dateFormat }}.

    diff --git a/src/app/views/contact-view/contact-view.component.ts b/src/app/views/contact-view/contact-view.component.ts index 9be7dded68..2790cb8808 100644 --- a/src/app/views/contact-view/contact-view.component.ts +++ b/src/app/views/contact-view/contact-view.component.ts @@ -96,7 +96,10 @@ export class ContactViewComponent implements OnInit { * @param {CoreService} coreService Instance of the CoreService. * @param {Router} router Instance of the Angular router. */ - constructor(private coreService: CoreService, private router: Router) {} + constructor( + private coreService: CoreService, + private router: Router + ) {} /** * Angular life cycle hook: ngOnInit. diff --git a/src/app/views/data-view/data-outlets/bibliography/bibliography-detail/bibliography-detail.component.ts b/src/app/views/data-view/data-outlets/bibliography/bibliography-detail/bibliography-detail.component.ts index d9cebc0aa4..c961a50141 100644 --- a/src/app/views/data-view/data-outlets/bibliography/bibliography-detail/bibliography-detail.component.ts +++ b/src/app/views/data-view/data-outlets/bibliography/bibliography-detail/bibliography-detail.component.ts @@ -45,7 +45,10 @@ export class BibliographyDetailComponent implements OnInit { * @param {BibliographyService} bibliographyService Instance of the BibliographyService. * @param {ConversionService} conversionService Instance of the ConversionService. */ - constructor(private bibliographyService: BibliographyService, private conversionService: ConversionService) {} + constructor( + private bibliographyService: BibliographyService, + private conversionService: ConversionService + ) {} /** * Angular life cycle hook: ngOnInit. diff --git a/src/app/views/data-view/data-outlets/bibliography/bibliography-list/bibliography-list.component.html b/src/app/views/data-view/data-outlets/bibliography/bibliography-list/bibliography-list.component.html index 0339a2ae2a..095cfecfa0 100644 --- a/src/app/views/data-view/data-outlets/bibliography/bibliography-list/bibliography-list.component.html +++ b/src/app/views/data-view/data-outlets/bibliography/bibliography-list/bibliography-list.component.html @@ -1,11 +1,11 @@
      -
    • - {{ bibItem.value[0] }} - - -
    • + @for (bibItem of bibList; track bibItem.obj_id) { +
    • + {{ bibItem.value[0] }} + @if (bibItem.obj_id === selectedBibItem.obj_id) { + + } +
    • + }
    diff --git a/src/app/views/data-view/data-outlets/bibliography/bibliography.component.html b/src/app/views/data-view/data-outlets/bibliography/bibliography.component.html index ac2c35c4eb..22cd7fea00 100644 --- a/src/app/views/data-view/data-outlets/bibliography/bibliography.component.html +++ b/src/app/views/data-view/data-outlets/bibliography/bibliography.component.html @@ -3,8 +3,7 @@

    Lade ... {{ (bibList$ | async)?.subjects.length }} von {{ nhits }} Einträgen

    - - +@if (isBibListLoaded && (bibList$ | async); as bibList) { + + +} diff --git a/src/app/views/data-view/data-outlets/resource-detail/resource-detail-html/resource-detail-html-content/imageobjects/imageobjects.component.html b/src/app/views/data-view/data-outlets/resource-detail/resource-detail-html/resource-detail-html-content/imageobjects/imageobjects.component.html index 5317d41d3f..0d446cee40 100644 --- a/src/app/views/data-view/data-outlets/resource-detail/resource-detail-html/resource-detail-html-content/imageobjects/imageobjects.component.html +++ b/src/app/views/data-view/data-outlets/resource-detail/resource-detail-html/resource-detail-html-content/imageobjects/imageobjects.component.html @@ -13,17 +13,18 @@ * ************************************************/ --> -
    - -
    - Abbildungen ({{ images?.length }}) -
    - - -
    - +@if (images?.length > 0) { +
    + +
    + Abbildungen ({{ images?.length }}) +
    + +
    + +
    +
    - -
    +} diff --git a/src/app/views/data-view/data-outlets/resource-detail/resource-detail-html/resource-detail-html-content/imageobjects/imageobjects.component.scss b/src/app/views/data-view/data-outlets/resource-detail/resource-detail-html/resource-detail-html-content/imageobjects/imageobjects.component.scss index 14c6aa908e..57d3d90093 100644 --- a/src/app/views/data-view/data-outlets/resource-detail/resource-detail-html/resource-detail-html-content/imageobjects/imageobjects.component.scss +++ b/src/app/views/data-view/data-outlets/resource-detail/resource-detail-html/resource-detail-html-content/imageobjects/imageobjects.component.scss @@ -1,4 +1,4 @@ -@import 'src/assets/themes/scss/shared'; +@import '/src/assets/themes/scss/shared'; /* slider container */ .awg-image-slider { diff --git a/src/app/views/data-view/data-outlets/resource-detail/resource-detail-html/resource-detail-html-content/linkedobjects/linkedobjects.component.html b/src/app/views/data-view/data-outlets/resource-detail/resource-detail-html/resource-detail-html-content/linkedobjects/linkedobjects.component.html index 626689d165..eecf1e8652 100644 --- a/src/app/views/data-view/data-outlets/resource-detail/resource-detail-html/resource-detail-html-content/linkedobjects/linkedobjects.component.html +++ b/src/app/views/data-view/data-outlets/resource-detail/resource-detail-html/resource-detail-html-content/linkedobjects/linkedobjects.component.html @@ -15,35 +15,47 @@
    - Verknüpfte Objekte ({{ totalNumber }}) + Verknüpfte Objekte ( + @if (incomingGroups) { + {{ totalNumber }} + } + )
    - - - - - {{ restypeGroup?.links?.length }} - {{ restypeGroup?.restypeLabel }} - - - - - - - +
    + @for (restypeGroup of incomingGroups; track restypeGroup; let i = $index) { +
    +
    + +
    +
    +
    + + + +
    +
    +
    + } +
    +
    - diff --git a/src/app/views/data-view/data-outlets/resource-detail/resource-detail-html/resource-detail-html-content/linkedobjects/linkedobjects.component.scss b/src/app/views/data-view/data-outlets/resource-detail/resource-detail-html/resource-detail-html-content/linkedobjects/linkedobjects.component.scss index 17a5d641ae..db96c837a7 100644 --- a/src/app/views/data-view/data-outlets/resource-detail/resource-detail-html/resource-detail-html-content/linkedobjects/linkedobjects.component.scss +++ b/src/app/views/data-view/data-outlets/resource-detail/resource-detail-html/resource-detail-html-content/linkedobjects/linkedobjects.component.scss @@ -12,29 +12,33 @@ * ************************************************/ +$textlinkColor: #333; +$backgroundColor: #e7e7e7; + +/* link objects */ ul.awg-linked-obj-list { padding-left: 0; + li { + &:hover { + background-color: $backgroundColor; + } + a { + width: inherit; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + display: block; + } + hr { + margin-top: 0.75rem; + margin-bottom: 0.75rem; + } + } } -ul.awg-linked-obj-list li:hover { - background-color: #e7e7e7; -} -ul.awg-linked-obj-list li a { - width: inherit; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - display: block; -} -ul.awg-linked-obj-list li hr { - margin-top: 0.75rem; - margin-bottom: 0.75rem; -} - -/* link objects */ .awg-linked-obj-link { cursor: pointer; - color: #333 !important; -} -.awg-linked-obj-link:hover { - border-bottom: none !important; + color: $textlinkColor !important; + &:hover { + border-bottom: none !important; + } } diff --git a/src/app/views/data-view/data-outlets/resource-detail/resource-detail-html/resource-detail-html-content/linkedobjects/linkedobjects.component.spec.ts b/src/app/views/data-view/data-outlets/resource-detail/resource-detail-html/resource-detail-html-content/linkedobjects/linkedobjects.component.spec.ts index 90fb718db7..69759979c1 100644 --- a/src/app/views/data-view/data-outlets/resource-detail/resource-detail-html/resource-detail-html-content/linkedobjects/linkedobjects.component.spec.ts +++ b/src/app/views/data-view/data-outlets/resource-detail/resource-detail-html/resource-detail-html-content/linkedobjects/linkedobjects.component.spec.ts @@ -1,8 +1,8 @@ import { DebugElement, NgModule } from '@angular/core'; -import { ComponentFixture, fakeAsync, TestBed, waitForAsync } from '@angular/core/testing'; +import { ComponentFixture, TestBed, fakeAsync, waitForAsync } from '@angular/core/testing'; import Spy = jasmine.Spy; -import { expectClosedPanelBody, expectOpenPanelBody } from '@testing/accordion-panel-helper'; +import { expectCollapsedPanel, expectOpenPanel } from '@testing/accordion-panel-helper'; import { click, clickAndAwaitChanges } from '@testing/click-helper'; import { detectChangesOnPush } from '@testing/detect-changes-on-push-helper'; import { expectSpyCall, expectToBe, expectToContain, getAndExpectDebugElementByCss } from '@testing/expect-helper'; @@ -27,8 +27,6 @@ describe('ResourceDetailHtmlContentLinkedObjectsComponent (DONE)', () => { let incomingLink2: ResourceDetailIncomingLink; let incomingLink3: ResourceDetailIncomingLink; const expectedTotalItems = 5; - const expectedFirstPanelId = 'incoming-linkgroup-0'; - const expectedSecondPanelId = 'incoming-linkgroup-1'; // Global NgbConfigModule @NgModule({ imports: [NgbAccordionModule], exports: [NgbAccordionModule] }) @@ -109,12 +107,9 @@ describe('ResourceDetailHtmlContentLinkedObjectsComponent (DONE)', () => { getAndExpectDebugElementByCss(headerDes[0], 'span#awg-incoming-size', 0, 0); }); - it('... should contain one ngb-accordion without panels (div.accordion-item) yet', () => { - // Ngb-accordion debug element - const accordionDes = getAndExpectDebugElementByCss(compDe, 'div.awg-linked-obj > ngb-accordion', 1, 1); - - // Panel - getAndExpectDebugElementByCss(accordionDes[0], 'div.accordion-item', 0, 0, 'yet'); + it('... should contain no div.accordion yet', () => { + // Div.accordion debug element + getAndExpectDebugElementByCss(compDe, 'div.accordion', 0, 0); }); }); @@ -181,19 +176,37 @@ describe('ResourceDetailHtmlContentLinkedObjectsComponent (DONE)', () => { expectToBe(sizeEl.textContent, component.totalNumber.toString()); }); - it('... should contain 2 ngb-panel elements in accordion (div.accordion-item) with header but no body (closed)', () => { - const accordionDes = getAndExpectDebugElementByCss(compDe, 'div.awg-linked-obj > ngb-accordion', 1, 1); + it('... should contain one div.accordion', () => { + // Div.accordion debug element + getAndExpectDebugElementByCss(compDe, 'div.accordion', 1, 1); + }); + + it('... should contain one div.accordion with 2 panels (div.accordion-item) with header and closed body', () => { + // Div.accordion debug element + const accordionDes = getAndExpectDebugElementByCss(compDe, 'div.accordion', 1, 1); // Panel debug elements const panelDes = getAndExpectDebugElementByCss(accordionDes[0], 'div.accordion-item', 2, 2); // Header debug elements - getAndExpectDebugElementByCss(panelDes[0], 'div.accordion-header', 1, 1, 'in first panel'); - getAndExpectDebugElementByCss(panelDes[1], 'div.accordion-header', 1, 1, 'in second panel'); + const panelHeaderDes1 = getAndExpectDebugElementByCss( + panelDes[0], + 'div.accordion-header', + 1, + 1, + 'in first panel' + ); + const panelHeaderDes2 = getAndExpectDebugElementByCss( + panelDes[1], + 'div.accordion-header', + 1, + 1, + 'in second panel' + ); - // Body debug elements - getAndExpectDebugElementByCss(panelDes[0], 'div.accordion-body', 0, 0, 'in first panel'); - getAndExpectDebugElementByCss(panelDes[1], 'div.accordion-body', 0, 0, 'in second panel'); + // Both panels closed first by default + expectCollapsedPanel(panelHeaderDes1[0], 'first panel closed'); + expectCollapsedPanel(panelHeaderDes2[0], 'second panel closed'); }); it('... should render incoming group length as badges in panel header (div.accordion-header)', () => { @@ -268,32 +281,32 @@ describe('ResourceDetailHtmlContentLinkedObjectsComponent (DONE)', () => { ); // Both panels closed first by default - expectClosedPanelBody(compDe, expectedFirstPanelId, 'first panel closed'); - expectClosedPanelBody(compDe, expectedSecondPanelId, 'second panel closed'); + expectCollapsedPanel(panelHeaderDes[0], 'first panel closed'); + expectCollapsedPanel(panelHeaderDes[1], 'second panel closed'); // Click first panel clickAndAwaitChanges(button0Des[0], fixture); - expectOpenPanelBody(compDe, expectedFirstPanelId, 'first panel open'); - expectClosedPanelBody(compDe, expectedSecondPanelId, 'second panel closed'); + expectOpenPanel(panelHeaderDes[0], 'first panel open'); + expectCollapsedPanel(panelHeaderDes[1], 'second panel closed'); // Click first panel again clickAndAwaitChanges(button0Des[0], fixture); - expectClosedPanelBody(compDe, expectedFirstPanelId, 'first panel closed'); - expectClosedPanelBody(compDe, expectedSecondPanelId, 'second panel closed'); + expectCollapsedPanel(panelHeaderDes[0], 'first panel closed'); + expectCollapsedPanel(panelHeaderDes[1], 'second panel closed'); // Click second panel clickAndAwaitChanges(button1Des[0], fixture); - expectClosedPanelBody(compDe, expectedFirstPanelId, 'first panel closed'); - expectOpenPanelBody(compDe, expectedSecondPanelId, 'second panel open'); + expectCollapsedPanel(panelHeaderDes[0], 'first panel closed'); + expectOpenPanel(panelHeaderDes[1], 'second panel open'); // Click second panel again clickAndAwaitChanges(button1Des[0], fixture); - expectClosedPanelBody(compDe, expectedFirstPanelId, 'first panel closed'); - expectClosedPanelBody(compDe, expectedSecondPanelId, 'second panel closed'); + expectCollapsedPanel(panelHeaderDes[0], 'first panel closed'); + expectCollapsedPanel(panelHeaderDes[1], 'second panel closed'); })); it('... should toggle panels alternately on click', fakeAsync(() => { @@ -322,20 +335,20 @@ describe('ResourceDetailHtmlContentLinkedObjectsComponent (DONE)', () => { ); // Both panels closed first by default - expectClosedPanelBody(compDe, expectedFirstPanelId, 'closed (first panel)'); - expectClosedPanelBody(compDe, expectedSecondPanelId, 'closed (second panel)'); + expectCollapsedPanel(panelHeaderDes[0], 'closed (first panel)'); + expectCollapsedPanel(panelHeaderDes[1], 'closed (second panel)'); // Click first panel clickAndAwaitChanges(button0Des[0], fixture); - expectOpenPanelBody(compDe, expectedFirstPanelId, 'opened (first panel)'); - expectClosedPanelBody(compDe, expectedSecondPanelId, 'closed (second panel)'); + expectOpenPanel(panelHeaderDes[0], 'opened (first panel)'); + expectCollapsedPanel(panelHeaderDes[1], 'closed (second panel)'); // Click second panel clickAndAwaitChanges(button1Des[0], fixture); - expectClosedPanelBody(compDe, expectedFirstPanelId, 'closed (first panel)'); - expectOpenPanelBody(compDe, expectedSecondPanelId, 'opened (second panel)'); + expectCollapsedPanel(panelHeaderDes[0], 'closed (first panel)'); + expectOpenPanel(panelHeaderDes[1], 'opened (second panel)'); })); describe('... should render panel content (div.accordion-body)', () => { @@ -345,23 +358,38 @@ describe('ResourceDetailHtmlContentLinkedObjectsComponent (DONE)', () => { /** * Click button to open first panel and get inner table */ - - // Button debug elements - const buttonDes = getAndExpectDebugElementByCss( + // Header debug elements + const panelHeaderDes = getAndExpectDebugElementByCss( compDe, - 'div.accordion-item > div.accordion-header button.accordion-button', + 'div.accordion-item > div.accordion-header', 2, 2 ); + // Button debug elements + const button0Des = getAndExpectDebugElementByCss( + panelHeaderDes[0], + 'button.accordion-button', + 1, + 1, + 'in first panel' + ); + const button1Des = getAndExpectDebugElementByCss( + panelHeaderDes[1], + 'button.accordion-button', + 1, + 1, + 'in second panel' + ); + // First button's native element to click on - const button0El = buttonDes[0].nativeElement; + const button0El = button0Des[0].nativeElement; // Open first panel click(button0El as HTMLElement); await detectChangesOnPush(fixture); // Replacement for fixture.detectChanges with OnPush - expectOpenPanelBody(compDe, expectedFirstPanelId, 'should have first panel opened'); + expectOpenPanel(panelHeaderDes[0], 'should have first panel opened'); // List debug elements listDes = getAndExpectDebugElementByCss(compDe, 'ul.awg-linked-obj-list', 1, 1); @@ -417,19 +445,35 @@ describe('ResourceDetailHtmlContentLinkedObjectsComponent (DONE)', () => { let anchorDes2: DebugElement[]; beforeEach(fakeAsync(() => { - // Button debug elements - const buttonDes = getAndExpectDebugElementByCss( + // Header debug elements + const panelHeaderDes = getAndExpectDebugElementByCss( compDe, - 'div.accordion-item > div.accordion-header button.accordion-button', + 'div.accordion-item > div.accordion-header', 2, 2 ); + // Button debug elements + const button0Des = getAndExpectDebugElementByCss( + panelHeaderDes[0], + 'button.accordion-button', + 1, + 1, + 'in first panel' + ); + const button1Des = getAndExpectDebugElementByCss( + panelHeaderDes[1], + 'button.accordion-button', + 1, + 1, + 'in second panel' + ); + // Open second panel - clickAndAwaitChanges(buttonDes[1], fixture); + clickAndAwaitChanges(button1Des[0], fixture); - expectClosedPanelBody(compDe, expectedFirstPanelId, 'should have first panel closed'); - expectOpenPanelBody(compDe, expectedSecondPanelId, 'should have second panel opened'); + expectCollapsedPanel(panelHeaderDes[0], 'should have first panel closed'); + expectOpenPanel(panelHeaderDes[1], 'should have second panel opened'); listDes = getAndExpectDebugElementByCss(compDe, 'ul.awg-linked-obj-list', 1, 1); listItemDes = getAndExpectDebugElementByCss(listDes[0], 'li', 3, 3); diff --git a/src/app/views/data-view/data-outlets/resource-detail/resource-detail-html/resource-detail-html-content/props/props.component.html b/src/app/views/data-view/data-outlets/resource-detail/resource-detail-html/resource-detail-html-content/props/props.component.html index 5a43a6c28f..adfab6fd44 100644 --- a/src/app/views/data-view/data-outlets/resource-detail/resource-detail-html/resource-detail-html-content/props/props.component.html +++ b/src/app/views/data-view/data-outlets/resource-detail/resource-detail-html/resource-detail-html-content/props/props.component.html @@ -1,21 +1,33 @@

    Objektdaten

    -
      -
    • -
      -
    • -
    • - - - {{ prop.label }} - - -
        -
      • - + @for (prop of props; track prop) { +
          + @if (prop.label === metaBreakLine) { +
        • +
        • -
        -
      • -
      + } + @if (prop!.values[0]) { +
    • + + @if (prop.label) { + + {{ prop.label }} + + } + + @for (value of prop.values; track value) { +
        + @if (value) { +
      • + +
      • + } +
      + } +
    • + } +
    + }
    diff --git a/src/app/views/data-view/data-outlets/resource-detail/resource-detail-html/resource-detail-html.component.html b/src/app/views/data-view/data-outlets/resource-detail/resource-detail-html/resource-detail-html.component.html index e163de7da5..1dec34301c 100644 --- a/src/app/views/data-view/data-outlets/resource-detail/resource-detail-html/resource-detail-html.component.html +++ b/src/app/views/data-view/data-outlets/resource-detail/resource-detail-html/resource-detail-html.component.html @@ -1,7 +1,8 @@ - - +@if (resourceDetailData?.content) { + + +} diff --git a/src/app/views/data-view/data-outlets/resource-detail/resource-detail.component.html b/src/app/views/data-view/data-outlets/resource-detail/resource-detail.component.html index 272df244a1..6019cdbebe 100644 --- a/src/app/views/data-view/data-outlets/resource-detail/resource-detail.component.html +++ b/src/app/views/data-view/data-outlets/resource-detail/resource-detail.component.html @@ -1,78 +1,80 @@ -
    - -
    - - - -
    -

    Die Anfrage "{{ errorMessage?.route }}" ist fehlgeschlagen.

    -

    Fehlermeldung: "{{ errorMessage?.statusText || errorMessage }}".

    -

    Möglicherweise gab es ein Problem mit der Internetverbindung oder dem verwendeten Suchbegriff.

    +@if (isLoading$ | async) { +
    +
    - +} @else { + + @if (errorMessage) { +
    +

    Die Anfrage "{{ errorMessage?.route }}" ist fehlgeschlagen.

    +

    Fehlermeldung: "{{ errorMessage?.statusText || errorMessage }}".

    +

    Möglicherweise gab es ein Problem mit der Internetverbindung oder dem verwendeten Suchbegriff.

    +
    + } - - + @if (resourceData) { +
    + + @if (resourceData.detail.header) { + + } + + +
    +
    + } - + TODO: remove + + --> +} diff --git a/src/app/views/data-view/data-outlets/search-overview.component.html b/src/app/views/data-view/data-outlets/search-overview.component.html index 1b0de9a7fb..887b2b9afd 100644 --- a/src/app/views/data-view/data-outlets/search-overview.component.html +++ b/src/app/views/data-view/data-outlets/search-overview.component.html @@ -1,8 +1,9 @@ - - +@if (searchRouterLinkButtons) { + + +} diff --git a/src/app/views/data-view/data-outlets/search-overview.component.ts b/src/app/views/data-view/data-outlets/search-overview.component.ts index f968851c6c..b338f552dd 100644 --- a/src/app/views/data-view/data-outlets/search-overview.component.ts +++ b/src/app/views/data-view/data-outlets/search-overview.component.ts @@ -35,7 +35,10 @@ export class SearchOverviewComponent implements OnInit { * @param {SideInfoService} sideInfoService Instance of the SideInfoService. * @param {ActivatedRoute} route Instance of the ActivatedRoute. */ - constructor(private sideInfoService: SideInfoService, private route: ActivatedRoute) {} + constructor( + private sideInfoService: SideInfoService, + private route: ActivatedRoute + ) {} /** * Angular life cycle hook: ngOnInit. diff --git a/src/app/views/data-view/data-outlets/search-panel/extended-search-form/extended-search-form.component.html b/src/app/views/data-view/data-outlets/search-panel/extended-search-form/extended-search-form.component.html index 128f3828a6..7d9a64eb91 100644 --- a/src/app/views/data-view/data-outlets/search-panel/extended-search-form/extended-search-form.component.html +++ b/src/app/views/data-view/data-outlets/search-panel/extended-search-form/extended-search-form.component.html @@ -1,116 +1,120 @@ -
    -
    - - -
    - - -
    - -
    - - -
    -
    - - -
    -
    - - -
    - -
    diff --git a/src/app/views/data-view/data-outlets/search-panel/search-panel.component.html b/src/app/views/data-view/data-outlets/search-panel/search-panel.component.html index 5270908821..c835d6033f 100644 --- a/src/app/views/data-view/data-outlets/search-panel/search-panel.component.html +++ b/src/app/views/data-view/data-outlets/search-panel/search-panel.component.html @@ -29,24 +29,27 @@
    -
    - -
    - - - -
    -

    Die Anfrage "{{ errorMessage?.route }}" ist fehlgeschlagen.

    -

    Fehlermeldung: "{{ errorMessage?.statusText || errorMessage }}".

    -

    Möglicherweise gab es ein Problem mit der Internetverbindung oder der Suchanfrage.

    +@if (isLoading$ | async) { +
    +
    +} @else { + @if (errorMessage) { +
    +

    Die Anfrage "{{ errorMessage?.route }}" ist fehlgeschlagen.

    +

    Fehlermeldung: "{{ errorMessage?.statusText || errorMessage }}".

    +

    Möglicherweise gab es ein Problem mit der Internetverbindung oder der Suchanfrage.

    +
    + } + @if (searchResponseWithQuery?.query) { + + + } +} - - - + diff --git a/src/app/views/data-view/data-outlets/search-panel/search-result-list/search-result-list.component.html b/src/app/views/data-view/data-outlets/search-panel/search-result-list/search-result-list.component.html index 4846007009..4ba5afc8b0 100644 --- a/src/app/views/data-view/data-outlets/search-panel/search-result-list/search-result-list.component.html +++ b/src/app/views/data-view/data-outlets/search-panel/search-result-list/search-result-list.component.html @@ -2,125 +2,133 @@ -
    - -
    -
    -
    - - - +@if (searchResponseWithQuery?.data) { +
    + +
    +
    - -
    - -
    -
    -
    - {{ searchResultText }} -
    -
    - - - - - - - -
    - - - - - - - - - - - - - - -
    TypRessource
    - - iconsrc -  {{ subject?.iconlabel }} - - - {{ subject!.valuelabel[0] }}:  -
    -
    - - -
    -
    -
    -
    - iconsrc -  {{ subject?.iconlabel }} -
    -
    -
    - -
    -
    - -
    +
    + {{ searchResultText }}
    - - - - - -
    + @if (searchResponseWithQuery?.data?.subjects && !isNoResults()) { + + + + + @if (!isGridView()) { +
    + + + + + + + + + + @for ( + subject of searchResponseWithQuery?.data?.subjects; + track trackById($index, subject) + ) { + + + + + } + +
    TypRessource
    + + iconsrc +  {{ subject?.iconlabel }} + + + {{ subject!.valuelabel[0] }}:  +
    +
    + } + + @if (isGridView()) { +
    + @for (subject of searchResponseWithQuery?.data?.subjects; track trackById($index, subject)) { +
    +
    +
    + iconsrc +  {{ subject?.iconlabel }} +
    +
    +
    + +
    +
    + +
    +
    + } +
    + } + + + + } +
    +} diff --git a/src/app/views/data-view/services/data-api.service.ts b/src/app/views/data-view/services/data-api.service.ts index 5547c79473..c03bd932d4 100644 --- a/src/app/views/data-view/services/data-api.service.ts +++ b/src/app/views/data-view/services/data-api.service.ts @@ -79,7 +79,10 @@ export class DataApiService extends ApiService { * @param {HttpClient} http Instance of the HttpClient. * @param {ConversionService} conversionService Instance of the ConversionService. */ - constructor(http: HttpClient, private conversionService: ConversionService) { + constructor( + http: HttpClient, + private conversionService: ConversionService + ) { super(http); this.serviceName = 'DataApiService'; } diff --git a/src/app/views/edition-view/edition-complex-card/edition-complex-card.component.html b/src/app/views/edition-view/edition-complex-card/edition-complex-card.component.html index 03871a7ece..81d2313489 100644 --- a/src/app/views/edition-view/edition-complex-card/edition-complex-card.component.html +++ b/src/app/views/edition-view/edition-complex-card/edition-complex-card.component.html @@ -1,38 +1,50 @@
    -
    -
    -
    -
    -  {{ complex.complex.complexId.short }} -
    -
    - + }
    diff --git a/src/app/views/edition-view/edition-outlets/edition-complex/edition-complex.component.ts b/src/app/views/edition-view/edition-outlets/edition-complex/edition-complex.component.ts index 72fc215cd0..9f1c568078 100644 --- a/src/app/views/edition-view/edition-outlets/edition-complex/edition-complex.component.ts +++ b/src/app/views/edition-view/edition-outlets/edition-complex/edition-complex.component.ts @@ -39,7 +39,11 @@ export class EditionComplexComponent implements OnDestroy, OnInit { * @param {EditionService} editionService Instance of the EditionService. * @param {UtilityService} utils Instance of the UtilityService. */ - constructor(private route: ActivatedRoute, private editionService: EditionService, public utils: UtilityService) {} + constructor( + private route: ActivatedRoute, + private editionService: EditionService, + public utils: UtilityService + ) {} /** * Getter variable: editionRouteConstants. diff --git a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-detail-nav/edition-detail-nav.component.html b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-detail-nav/edition-detail-nav.component.html index 91af71cca7..b8f1049edd 100644 --- a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-detail-nav/edition-detail-nav.component.html +++ b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-detail-nav/edition-detail-nav.component.html @@ -1,7 +1,6 @@ - - +@if (editionRouterLinkButtons) { + + +} diff --git a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/edition-graph.component.html b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/edition-graph.component.html index fd61ce7e3e..c2c01acf9a 100644 --- a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/edition-graph.component.html +++ b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/edition-graph.component.html @@ -4,101 +4,114 @@ -
    -
    - -
    -

    - So weit vorhanden, präsentiert die vorliegende Ansicht Graph-Visualisierungen des ausgewählten - Editionsbereichs: Der statische Graph vermittelt dabei einen von den jeweiligen Editoren - fixierten Überblick editionsrelevanter Zusammenhänge und Abhängigkeiten, der - dynamische Graph bietet zusätzlich einen interaktiven Zugang zu dem zugrundeliegenden - Datenbestand. - Diese Funktionalität ist noch nicht für alle Skizzenbestandteile verfügbar, wird aber - sukzessive ergänzt. -

    - -

    -
    - -

    - [Die Graph-Visualisierungen zum Editionskomplex - erscheinen im Zusammenhang der - vollständigen Edition von {{ editionComplex.complexId.short }} in - {{ editionRouteConstants.EDITION.short }} {{ editionComplex.series.short }}/{{ - editionComplex.section.short - }}.] - -

    -
    -
    - - -
    -

    - Dynamischer Graph - - - -

    -

    - Diese interaktive Visualisierung basiert auf Mads Holten's - SPARQL Visualizer. Sie modelliert den - aktuellen Skizzenkomplex in Form eines RDF-Graphen. Die - zugrundeliegenden RDF-Daten sind unter dem Punkt RDF Triples einsehbar und lokal - editierbar. Eine Filterung und Eingrenzung dieser RDF-Daten kann mithilfe der Abfragesprache - SPARQL - unter dem Punkt SPARQL Abfrage vorgenommen werden; in der Voreinstellung werden hierbei - alle vorhandenen Triples abgefragt. -

    - -
    - - -
    -

    Statischer Graph

    -
    -
    + @if (editionGraphData$ | async; as editionGraphData) { +
    + @for (graph of editionGraphData.graph; track graph) { +
    + +
    +

    + So weit vorhanden, präsentiert die vorliegende Ansicht Graph-Visualisierungen des + ausgewählten Editionsbereichs: Der statische Graph vermittelt dabei einen von den + jeweiligen Editoren fixierten Überblick editionsrelevanter Zusammenhänge und Abhängigkeiten, + der dynamische Graph bietet zusätzlich einen interaktiven Zugang zu dem + zugrundeliegenden Datenbestand. + Diese Funktionalität ist noch nicht für alle Skizzenbestandteile verfügbar, wird aber + sukzessive ergänzt. +

    + @if (utils.isNotEmptyArray(graph.description) || graph.rdfData?.triples) { + @for (description of graph.description; track description) { +

    + } + } @else { +

    + [Die Graph-Visualisierungen zum Editionskomplex + erscheinen im Zusammenhang + der vollständigen Edition von {{ editionComplex.complexId.short }} in + {{ editionRouteConstants.EDITION.short }} {{ editionComplex.series.short }}/{{ + editionComplex.section.short + }}.] + +

    + } +
    + + @if (graph.rdfData && graph.rdfData.triples && graph.rdfData.queryList) { +
    +

    + Dynamischer Graph + + @if (!isFullscreen) { + + } + @if (isFullscreen) { + + } +

    +

    + Diese interaktive Visualisierung basiert auf Mads Holten's + SPARQL Visualizer. Sie + modelliert den aktuellen Skizzenkomplex in Form eines + RDF-Graphen. Die zugrundeliegenden RDF-Daten sind + unter dem Punkt RDF Triples einsehbar und lokal editierbar. Eine Filterung und + Eingrenzung dieser RDF-Daten kann mithilfe der Abfragesprache + SPARQL + unter dem Punkt SPARQL Abfrage vorgenommen werden; in der Voreinstellung werden + hierbei alle vorhandenen Triples abgefragt. +

    + +
    + } + + @if (graph.staticImage && graph.staticImage !== '') { +
    +

    Statischer Graph

    +
    +
    + } +
    + }
    -
    + } @else { + @if (errorObject) { +
    +
    +
    {{ errorObject | json }}
    +
    +
    + } + } -
    -
    -
    {{ errorObject | json }}
    + @if (errorObject) { +
    +
    +
    {{ errorObject | json }}
    +
    -
    + }
    diff --git a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/construct-results/construct-results.component.html b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/construct-results/construct-results.component.html index 0e21e959f8..264d027537 100644 --- a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/construct-results/construct-results.component.html +++ b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/construct-results/construct-results.component.html @@ -1,26 +1,31 @@ - - - - - - - - - - - +
    +
    +
    + +
    +
    +
    + + @if (queryResult$ | async; as queryResult) { + @if (isQueryResultNotEmpty(queryResult)) { + + + + } @else { + + } + } @else { + + } - - - - - - - +
    +
    +
    +
    diff --git a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/construct-results/construct-results.component.spec.ts b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/construct-results/construct-results.component.spec.ts index b2400d2687..cf4f49698a 100644 --- a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/construct-results/construct-results.component.spec.ts +++ b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/construct-results/construct-results.component.spec.ts @@ -4,13 +4,16 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { EMPTY, Observable, of as observableOf } from 'rxjs'; import Spy = jasmine.Spy; -import { NgbAccordion, NgbAccordionModule, NgbConfig, NgbPanelChangeEvent } from '@ng-bootstrap/ng-bootstrap'; +import { NgbAccordionDirective, NgbAccordionModule, NgbConfig } from '@ng-bootstrap/ng-bootstrap'; import { click } from '@testing/click-helper'; import { detectChangesOnPush } from '@testing/detect-changes-on-push-helper'; import { expectSpyCall, + expectToBe, + expectToContain, + expectToEqual, getAndExpectDebugElementByCss, getAndExpectDebugElementByDirective, } from '@testing/expect-helper'; @@ -38,13 +41,14 @@ describe('ConstructResultsComponent (DONE)', () => { let compDe: DebugElement; let expectedHeight: number; - let expectedTriples: Triple[]; + let expectedQueryResult: Triple; let expectedQueryResult$: Observable; let expectedIsFullscreen: boolean; let emitClickedNodeRequestSpy: Spy; + let isAccordionItemDisabledSpy: Spy; + let isQueryResultNotEmptySpy: Spy; let nodeClickSpy: Spy; - let preventPanelCollapseOnFullscreenSpy: Spy; // Global NgbConfigModule @NgModule({ imports: [NgbAccordionModule], exports: [NgbAccordionModule] }) @@ -57,7 +61,7 @@ describe('ConstructResultsComponent (DONE)', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ - imports: [NgbAccordionWithConfigModule], + imports: [NgbAccordionWithConfigModule, NgbAccordionDirective], declarations: [ ConstructResultsComponent, ForceGraphStubComponent, @@ -75,21 +79,20 @@ describe('ConstructResultsComponent (DONE)', () => { // Test data expectedHeight = 500; expectedIsFullscreen = false; - expectedTriples = [ - { - subject: 'example:Test', - predicate: 'example:has', - object: 'example:Success', - }, - ]; - expectedQueryResult$ = observableOf(expectedTriples); + expectedQueryResult = { + subject: 'example:Test', + predicate: 'example:has', + object: 'example:Success', + }; + expectedQueryResult$ = observableOf([expectedQueryResult]); // Spies on component functions // `.and.callThrough` will track the spy down the nested describes, see // https://jasmine.github.io/2.0/introduction.html#section-Spies:_%3Ccode%3Eand.callThrough%3C/code%3E - nodeClickSpy = spyOn(component, 'onGraphNodeClick').and.callThrough(); - preventPanelCollapseOnFullscreenSpy = spyOn(component, 'preventPanelCollapseOnFullscreen').and.callThrough(); emitClickedNodeRequestSpy = spyOn(component.clickedNodeRequest, 'emit').and.callThrough(); + isAccordionItemDisabledSpy = spyOn(component, 'isAccordionItemDisabled').and.callThrough(); + isQueryResultNotEmptySpy = spyOn(component, 'isQueryResultNotEmpty').and.callThrough(); + nodeClickSpy = spyOn(component, 'onGraphNodeClick').and.callThrough(); }); it('... should create', () => { @@ -110,12 +113,9 @@ describe('ConstructResultsComponent (DONE)', () => { }); describe('VIEW', () => { - it('... should contain one ngb-accordion without panel (div.accordion-item) yet', () => { - // Ngb-accordion debug element - const accordionDes = getAndExpectDebugElementByCss(compDe, 'ngb-accordion', 1, 1); - - // Panel - getAndExpectDebugElementByCss(accordionDes[0], 'div.accordion-item', 0, 0, 'yet'); + it('... should contain no div.accordion yet', () => { + // Div.accordion debug element + getAndExpectDebugElementByCss(compDe, 'div.accordion', 0, 0); }); }); }); @@ -132,211 +132,909 @@ describe('ConstructResultsComponent (DONE)', () => { }); it('... should have `queryResult` input', () => { - expect(component.queryResult$).toBeDefined(); - expect(component.queryResult$) - .withContext(`should equal ${expectedQueryResult$}`) - .toEqual(expectedQueryResult$); + expectToEqual(component.queryResult$, expectedQueryResult$); }); it('... should have `defaultForceGraphHeight` input', () => { - expect(component.defaultForceGraphHeight).toBeDefined(); - expect(component.defaultForceGraphHeight).withContext(`should be ${expectedHeight}`).toBe(expectedHeight); + expectToBe(component.defaultForceGraphHeight, expectedHeight); }); it('... should have `isFullscreen` input', () => { - expect(component.isFullscreen).toBeDefined(); - expect(component.isFullscreen).withContext(`should be ${expectedIsFullscreen}`).toBe(expectedIsFullscreen); + expectToBe(component.isFullscreen, expectedIsFullscreen); }); describe('VIEW', () => { - it('... should contain one ngb-accordion with panel (div.accordion-item) header and body', () => { - // Ngb-accordion debug element - const accordionDes = getAndExpectDebugElementByCss(compDe, 'ngb-accordion', 1, 1); - - // Panel (div.card) - const panelDes = getAndExpectDebugElementByCss(accordionDes[0], 'div.accordion-item', 1, 1); // Panel (div.card) - // Header - getAndExpectDebugElementByCss( - panelDes[0], - 'div#awg-graph-visualizer-construct-results-header.accordion-header', - 1, - 1 - ); // Panel (div.card) - // Body - getAndExpectDebugElementByCss( - panelDes[0], - 'div#awg-graph-visualizer-construct-results > div.accordion-body', - 1, - 1 - ); + it('... should contain one div.accordion', () => { + // NgbAccordion debug element + getAndExpectDebugElementByCss(compDe, 'div.accordion', 1, 1); }); - it('... should display panel header button', () => { - // Panel header button - const btnDes = getAndExpectDebugElementByCss( - compDe, - 'div#awg-graph-visualizer-construct-results-header > button', - 1, - 1 - ); - - const btnEl = btnDes[0].nativeElement; + describe('not in fullscreen mode', () => { + it('... should contain div.accordion-item with header and open body in div.accordion', () => { + // NgbAccordion debug element + const accordionDes = getAndExpectDebugElementByCss(compDe, 'div.accordion', 1, 1); + + // Div.accordion-item + const itemDes = getAndExpectDebugElementByCss( + accordionDes[0], + 'div#awg-graph-visualizer-construct-results.accordion-item', + 1, + 1 + ); + // Header (div.accordion-header) + getAndExpectDebugElementByCss( + itemDes[0], + 'div#awg-graph-visualizer-construct-results > div.accordion-header', + 1, + 1 + ); + + // Body open (div.accordion-collapse) + const itemBodyDes = getAndExpectDebugElementByCss( + itemDes[0], + 'div#awg-graph-visualizer-construct-results-collapse', + 1, + 1 + ); + const itemBodyEl = itemBodyDes[0].nativeElement; + + expectToContain(itemBodyEl.classList, 'show'); + }); + + it('... should display item header button', () => { + // Header debug elements + const itemHeaderDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-construct-results > div.accordion-header', + 1, + 1 + ); + + // Item header button + const btnDes = getAndExpectDebugElementByCss(itemHeaderDes[0], 'button.accordion-button', 1, 1); + const btnEl = btnDes[0].nativeElement; + + // Check button content + expectToBe(btnEl.textContent, 'Resultat'); + }); + + it('... should toggle item body on click', () => { + // Header debug elements + const itemHeaderDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-construct-results > div.accordion-header', + 1, + 1 + ); + + // Button debug elements + const btnDes = getAndExpectDebugElementByCss(itemHeaderDes[0], 'button.accordion-button', 1, 1); + // Button native elements to click on + const btnEl = btnDes[0].nativeElement; + + // Item body is open + let itemBodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-construct-results > div.accordion-collapse', + 1, + 1, + 'open' + ); + let itemBodyEl = itemBodyDes[0].nativeElement; + + expectToContain(itemBodyEl.classList, 'show'); + + // Click header button + click(btnEl as HTMLElement); + detectChangesOnPush(fixture); + + // Item body is collapsed + itemBodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-construct-results > div.accordion-collapse', + 1, + 1, + 'collapsed' + ); + itemBodyEl = itemBodyDes[0].nativeElement; + + expectToContain(itemBodyEl.classList, 'collapse'); + + // Click header button + click(btnEl as HTMLElement); + detectChangesOnPush(fixture); + + // Item body is open again + itemBodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-construct-results > div.accordion-collapse', + 1, + 1, + 'open' + ); + itemBodyEl = itemBodyDes[0].nativeElement; + + expectToContain(itemBodyEl.classList, 'show'); + }); + + describe('... should contain TwelveToneSpinnerComponent (stubbed) in item body while loading if ... ', () => { + it('... queryResult$ is EMPTY', () => { + // Mock empty observable + component.queryResult$ = EMPTY; + detectChangesOnPush(fixture); + + // Item body + const bodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-construct-results-collapse > div.accordion-body', + 1, + 1 + ); + + getAndExpectDebugElementByDirective(bodyDes[0], TwelveToneSpinnerStubComponent, 1, 1); + }); + + it('... queryResult$ is undefined', () => { + // Mock undefined response + component.queryResult$ = observableOf(undefined); + detectChangesOnPush(fixture); + + // Item body + const bodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-construct-results-collapse > div.accordion-body', + 1, + 1 + ); + + getAndExpectDebugElementByDirective(bodyDes[0], TwelveToneSpinnerStubComponent, 1, 1); + }); + + it('... queryResult$ is null', () => { + // Mock null response + component.queryResult$ = observableOf(null); + detectChangesOnPush(fixture); + + // Item body + const bodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-construct-results-collapse > div.accordion-body', + 1, + 1 + ); + + getAndExpectDebugElementByDirective(bodyDes[0], TwelveToneSpinnerStubComponent, 1, 1); + }); + }); + + describe('... should contain item body with SparqlNoResultsStubComponent (stubbed) if ... ', () => { + it('... queryResult is empty array', () => { + // Mock empty array + component.queryResult$ = observableOf([]); + detectChangesOnPush(fixture); + + // Item body + const bodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-construct-results-collapse > div.accordion-body', + 1, + 1 + ); + + getAndExpectDebugElementByDirective(bodyDes[0], SparqlNoResultsStubComponent, 1, 1); + }); + + it('... queryResult is undefined or empty string', () => { + // Mock undefined response + component.queryResult$ = observableOf([ + { subject: undefined, predicate: undefined, object: undefined }, + ]); + detectChangesOnPush(fixture); + + // Item body + const bodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-construct-results-collapse > div.accordion-body', + 1, + 1 + ); + + getAndExpectDebugElementByDirective(bodyDes[0], SparqlNoResultsStubComponent, 1, 1); + + // Mock empty response + component.queryResult$ = observableOf([{ subject: '', predicate: '', object: '' }]); + detectChangesOnPush(fixture); + + // Item body + getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-construct-results-collapse > div.accordion-body', + 1, + 1 + ); + + getAndExpectDebugElementByDirective(bodyDes[0], SparqlNoResultsStubComponent, 1, 1); + }); + + it('... queryResult.subject is undefined or empty string', () => { + // Mock undefined response + component.queryResult$ = observableOf([ + { + subject: undefined, + predicate: 'example:has', + object: 'example:Success', + }, + ]); + detectChangesOnPush(fixture); + + // Item body + const bodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-construct-results-collapse > div.accordion-body', + 1, + 1 + ); + + getAndExpectDebugElementByDirective(bodyDes[0], SparqlNoResultsStubComponent, 1, 1); + + // Mock empty response + component.queryResult$ = observableOf([ + { + subject: '', + predicate: 'example:has', + object: 'example:Success', + }, + ]); + detectChangesOnPush(fixture); + + // Item body + getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-construct-results-collapse > div.accordion-body', + 1, + 1 + ); + + getAndExpectDebugElementByDirective(bodyDes[0], SparqlNoResultsStubComponent, 1, 1); + }); + + it('... queryResult.predicate is undefined or empty string', () => { + // Mock undefined response + component.queryResult$ = observableOf([ + { + subject: 'example:Test', + predicate: undefined, + object: 'example:Success', + }, + ]); + detectChangesOnPush(fixture); + + // Item body + const bodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-construct-results-collapse > div.accordion-body', + 1, + 1 + ); + + getAndExpectDebugElementByDirective(bodyDes[0], SparqlNoResultsStubComponent, 1, 1); + + // Mock empty response + component.queryResult$ = observableOf([ + { + subject: 'example:Test', + predicate: '', + object: 'example:Success', + }, + ]); + detectChangesOnPush(fixture); + + // Item body + getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-construct-results-collapse > div.accordion-body', + 1, + 1 + ); + + getAndExpectDebugElementByDirective(bodyDes[0], SparqlNoResultsStubComponent, 1, 1); + }); + + it('... queryResult.object is undefined or empty string', () => { + // Mock undefined response + component.queryResult$ = observableOf([ + { + subject: 'example:Test', + predicate: 'example:has', + object: undefined, + }, + ]); + detectChangesOnPush(fixture); + + // Item body + const bodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-construct-results-collapse > div.accordion-body', + 1, + 1 + ); + + getAndExpectDebugElementByDirective(bodyDes[0], SparqlNoResultsStubComponent, 1, 1); + + // Mock empty response + component.queryResult$ = observableOf([ + { + subject: 'example:Test', + predicate: 'example:has', + object: '', + }, + ]); + detectChangesOnPush(fixture); + + // Item body + getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-construct-results-collapse > div.accordion-body', + 1, + 1 + ); + + getAndExpectDebugElementByDirective(bodyDes[0], SparqlNoResultsStubComponent, 1, 1); + }); + }); + + it('... should contain item body with ForceGraphComponent (stubbed) if results are available', () => { + // Item body + const bodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-construct-results-collapse > div.accordion-body', + 1, + 1 + ); + + // ForceGraph + getAndExpectDebugElementByDirective(bodyDes[0], ForceGraphStubComponent, 1, 1); + }); + + it('... should pass down `queryResult` and `defaultForceGraphHeight` to forceGraph component', () => { + const forceGraphDes = getAndExpectDebugElementByDirective(compDe, ForceGraphStubComponent, 1, 1); + const forceGraphCmp = forceGraphDes[0].injector.get( + ForceGraphStubComponent + ) as ForceGraphStubComponent; + + expectToEqual(forceGraphCmp.queryResultTriples, [expectedQueryResult]); + expectToBe(forceGraphCmp.height, expectedHeight); + }); + }); - // Check button content - expect(btnEl.textContent).toBeTruthy(); - expect(btnEl.textContent).withContext('should be Resultat').toContain('Resultat'); + describe('in fullscreen mode', () => { + beforeEach(() => { + // Set fullscreen mode + component.isFullscreen = true; + detectChangesOnPush(fixture); + }); + + it('... should contain div.accordion-item with header and open body in div.accordion', () => { + // NgbAccordion debug element + const accordionDes = getAndExpectDebugElementByCss(compDe, 'div.accordion', 1, 1); + + // Panel (div.accordion-item) + const itemDes = getAndExpectDebugElementByCss( + accordionDes[0], + 'div#awg-graph-visualizer-construct-results.accordion-item', + 1, + 1 + ); + // Header (div.accordion-header) + getAndExpectDebugElementByCss( + itemDes[0], + 'div#awg-graph-visualizer-construct-results > div.accordion-header', + 1, + 1 + ); + + // Body open (div.accordion-collapse) + const itemBodyDes = getAndExpectDebugElementByCss( + itemDes[0], + 'div#awg-graph-visualizer-construct-results-collapse', + 1, + 1 + ); + const itemBodyEl = itemBodyDes[0].nativeElement; + + expectToContain(itemBodyEl.classList, 'show'); + }); + + it('... should display item header button', () => { + // Header debug elements + const itemHeaderDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-construct-results > div.accordion-header', + 1, + 1 + ); + + // Panel header button + const btnDes = getAndExpectDebugElementByCss(itemHeaderDes[0], 'button.accordion-button', 1, 1); + const btnEl = btnDes[0].nativeElement; + + // Check button content + expectToBe(btnEl.textContent, 'Resultat'); + }); + + it('... should not toggle item body on click', () => { + // Header debug elements + const itemHeaderDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-construct-results > div.accordion-header', + 1, + 1 + ); + + // Button debug elements + const btnDes = getAndExpectDebugElementByCss(itemHeaderDes[0], 'button.accordion-button', 1, 1); + // Button native elements to click on + const btnEl = btnDes[0].nativeElement; + + expect(btnEl.disabled).toBeTruthy(); + + // Item body is open + let itemBodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-construct-results > div.accordion-collapse', + 1, + 1, + 'open' + ); + let itemBodyEl = itemBodyDes[0].nativeElement; + + expectToContain(itemBodyEl.classList, 'show'); + + // Click header button + click(btnEl as HTMLElement); + detectChangesOnPush(fixture); + + // Item body does not close again + itemBodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-construct-results > div.accordion-collapse', + 1, + 1, + 'open' + ); + itemBodyEl = itemBodyDes[0].nativeElement; + + expectToContain(itemBodyEl.classList, 'show'); + }); + + describe('... should contain TwelveToneSpinnerComponent (stubbed) in item body while loading if ... ', () => { + it('... queryResult$ is EMPTY', () => { + // Mock empty observable + component.queryResult$ = EMPTY; + detectChangesOnPush(fixture); + + // Item body + const bodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-construct-results-collapse > div.accordion-body', + 1, + 1 + ); + + getAndExpectDebugElementByDirective(bodyDes[0], TwelveToneSpinnerStubComponent, 1, 1); + }); + + it('... queryResult$ is undefined', () => { + // Mock undefined response + component.queryResult$ = observableOf(undefined); + detectChangesOnPush(fixture); + + // Item body + const bodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-construct-results-collapse > div.accordion-body', + 1, + 1 + ); + + getAndExpectDebugElementByDirective(bodyDes[0], TwelveToneSpinnerStubComponent, 1, 1); + }); + + it('... queryResult$ is null', () => { + // Mock null response + component.queryResult$ = observableOf(null); + detectChangesOnPush(fixture); + + // Item body + const bodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-construct-results-collapse > div.accordion-body', + 1, + 1 + ); + + getAndExpectDebugElementByDirective(bodyDes[0], TwelveToneSpinnerStubComponent, 1, 1); + }); + }); + + describe('... should contain item body with SparqlNoResultsStubComponent (stubbed) if ... ', () => { + it('... queryResult is empty array', () => { + // Mock empty array + component.queryResult$ = observableOf([]); + detectChangesOnPush(fixture); + + // Item body + const bodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-construct-results-collapse > div.accordion-body', + 1, + 1 + ); + + getAndExpectDebugElementByDirective(bodyDes[0], SparqlNoResultsStubComponent, 1, 1); + }); + + it('... queryResult is undefined or empty string', () => { + // Mock undefined response + component.queryResult$ = observableOf([ + { subject: undefined, predicate: undefined, object: undefined }, + ]); + detectChangesOnPush(fixture); + + // Item body + const bodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-construct-results-collapse > div.accordion-body', + 1, + 1 + ); + + getAndExpectDebugElementByDirective(bodyDes[0], SparqlNoResultsStubComponent, 1, 1); + + // Mock empty response + component.queryResult$ = observableOf([{ subject: '', predicate: '', object: '' }]); + detectChangesOnPush(fixture); + + // Item body + getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-construct-results-collapse > div.accordion-body', + 1, + 1 + ); + + getAndExpectDebugElementByDirective(bodyDes[0], SparqlNoResultsStubComponent, 1, 1); + }); + + it('... queryResult.subject is undefined or empty string', () => { + // Mock undefined response + component.queryResult$ = observableOf([ + { + subject: undefined, + predicate: 'example:has', + object: 'example:Success', + }, + ]); + detectChangesOnPush(fixture); + + // Item body + const bodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-construct-results-collapse > div.accordion-body', + 1, + 1 + ); + + getAndExpectDebugElementByDirective(bodyDes[0], SparqlNoResultsStubComponent, 1, 1); + + // Mock empty response + component.queryResult$ = observableOf([ + { + subject: '', + predicate: 'example:has', + object: 'example:Success', + }, + ]); + detectChangesOnPush(fixture); + + // Item body + getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-construct-results-collapse > div.accordion-body', + 1, + 1 + ); + + getAndExpectDebugElementByDirective(bodyDes[0], SparqlNoResultsStubComponent, 1, 1); + }); + + it('... queryResult.predicate is undefined or empty string', () => { + // Mock undefined response + component.queryResult$ = observableOf([ + { + subject: 'example:Test', + predicate: undefined, + object: 'example:Success', + }, + ]); + detectChangesOnPush(fixture); + + // Item body + const bodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-construct-results-collapse > div.accordion-body', + 1, + 1 + ); + + getAndExpectDebugElementByDirective(bodyDes[0], SparqlNoResultsStubComponent, 1, 1); + + // Mock empty response + component.queryResult$ = observableOf([ + { + subject: 'example:Test', + predicate: '', + object: 'example:Success', + }, + ]); + detectChangesOnPush(fixture); + + // Item body + getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-construct-results-collapse > div.accordion-body', + 1, + 1 + ); + + getAndExpectDebugElementByDirective(bodyDes[0], SparqlNoResultsStubComponent, 1, 1); + }); + + it('... queryResult.object is undefined or empty string', () => { + // Mock undefined response + component.queryResult$ = observableOf([ + { + subject: 'example:Test', + predicate: 'example:has', + object: undefined, + }, + ]); + detectChangesOnPush(fixture); + + // Item body + const bodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-construct-results-collapse > div.accordion-body', + 1, + 1 + ); + + getAndExpectDebugElementByDirective(bodyDes[0], SparqlNoResultsStubComponent, 1, 1); + + // Mock empty response + component.queryResult$ = observableOf([ + { + subject: 'example:Test', + predicate: 'example:has', + object: '', + }, + ]); + detectChangesOnPush(fixture); + + // Item body + getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-construct-results-collapse > div.accordion-body', + 1, + 1 + ); + + getAndExpectDebugElementByDirective(bodyDes[0], SparqlNoResultsStubComponent, 1, 1); + }); + }); + + it('... should contain item body with ForceGraphComponent (stubbed) if results are available', () => { + // Item body + const bodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-construct-results-collapse > div.accordion-body', + 1, + 1 + ); + + // ForceGraph + getAndExpectDebugElementByDirective(bodyDes[0], ForceGraphStubComponent, 1, 1); + }); + + it('... should pass down `queryResult` and `defaultForceGraphHeight` to forceGraph component', () => { + const forceGraphDes = getAndExpectDebugElementByDirective(compDe, ForceGraphStubComponent, 1, 1); + const forceGraphCmp = forceGraphDes[0].injector.get( + ForceGraphStubComponent + ) as ForceGraphStubComponent; + + expectToEqual(forceGraphCmp.queryResultTriples, [expectedQueryResult]); + expectToBe(forceGraphCmp.height, expectedHeight); + }); }); + }); - it('... should toggle panel body on click when not in fullscreen mode', () => { - component.isFullscreen = false; - fixture.detectChanges(); - - // Header debug elements - const panelHeaderDes = getAndExpectDebugElementByCss( - compDe, - 'div#awg-graph-visualizer-construct-results-header.accordion-header', - 1, - 1 - ); - - // Button debug elements - const btnDes = getAndExpectDebugElementByCss(panelHeaderDes[0], 'button.accordion-button', 1, 1); - // Button native elements to click on - const btnEl = btnDes[0].nativeElement; - - // Panel body is open - getAndExpectDebugElementByCss( - compDe, - 'div#awg-graph-visualizer-construct-results > div.accordion-body', - 1, - 1, - 'open' - ); - - // Click header button - click(btnEl as HTMLElement); - detectChangesOnPush(fixture); + describe('#isAccordionItemDisabled()', () => { + it('... should have a method `isAccordionItemDisabled`', () => { + expect(component.isAccordionItemDisabled).toBeDefined(); + }); - // Panel is open - getAndExpectDebugElementByCss( - compDe, - 'div#awg-graph-visualizer-construct-results > div.accordion-body', - 0, - 0, - 'collapsed' - ); - - // Click header button - click(btnEl as HTMLElement); - detectChangesOnPush(fixture); + it('... should be triggered from ngbAccordionItem', () => { + expectSpyCall(isAccordionItemDisabledSpy, 2); + }); - getAndExpectDebugElementByCss( - // Panel body is closed again - compDe, - 'div#awg-graph-visualizer-construct-results > div.accordion-body', - 1, - 1, - 'open' - ); + it('... should return false if isFullscreen is false', () => { + expectToBe(component.isAccordionItemDisabled(), false); }); - it('... should not toggle panel body on click in fullscreen mode', () => { + it('... should return true if isFullscreen is true', () => { + // Set fullscreen flag to true component.isFullscreen = true; - fixture.detectChanges(); - - // Header debug elements - const panelHeaderDes = getAndExpectDebugElementByCss( - compDe, - 'div#awg-graph-visualizer-construct-results-header.accordion-header', - 1, - 1 - ); - - // Button debug elements - const btnDes = getAndExpectDebugElementByCss(panelHeaderDes[0], 'button.accordion-button', 1, 1); - // Button native elements to click on - const btnEl = btnDes[0].nativeElement; - - // Panel body does not close - getAndExpectDebugElementByCss( - compDe, - 'div#awg-graph-visualizer-construct-results > div.accordion-body', - 1, - 1, - 'open' - ); - - // Click header button - click(btnEl as HTMLElement); - detectChangesOnPush(fixture); - // Panel body does not close again - getAndExpectDebugElementByCss( - compDe, - 'div#awg-graph-visualizer-construct-results > div.accordion-body', - 1, - 1, - 'open' - ); + expectToBe(component.isAccordionItemDisabled(), true); }); + }); - it('... should contain panel body with TwelveToneSpinnerComponent (stubbed) while loading', () => { - // Mock empty observable - component.queryResult$ = EMPTY; - detectChangesOnPush(fixture); - - // Panel body - const bodyDes = getAndExpectDebugElementByCss( - compDe, - 'div#awg-graph-visualizer-construct-results > div.accordion-body', - 1, - 1 - ); + describe('#isQueryResultNotEmpty()', () => { + it('... should have a method `isQueryResultNotEmpty`', () => { + expect(component.isQueryResultNotEmpty).toBeDefined(); + }); - getAndExpectDebugElementByDirective(bodyDes[0], TwelveToneSpinnerStubComponent, 1, 1); + it('... should be triggered from ngbAccordionBody', () => { + expectSpyCall(isQueryResultNotEmptySpy, 2, expectedQueryResult[0]); }); - it('... should contain panel body with SparqlNoResultsStubComponent (stubbed) if no results are available', () => { - // Mock empty response - component.queryResult$ = observableOf([]); - detectChangesOnPush(fixture); + it('... should be triggered by change of queryResult', () => { + expectSpyCall(isQueryResultNotEmptySpy, 2, expectedQueryResult[0]); - // Panel body - const bodyDes = getAndExpectDebugElementByCss( - compDe, - 'div#awg-graph-visualizer-construct-results > div.accordion-body', - 1, - 1 - ); + // Mock another queryResult + const anotherQueryResult = { + subject: 'example:AnotherTest', + predicate: 'example:has', + object: 'example:AnotherSuccess', + }; + component.queryResult$ = observableOf([anotherQueryResult]); + detectChangesOnPush(fixture); - // SparqlNoResultsStubComponent - getAndExpectDebugElementByDirective(bodyDes[0], SparqlNoResultsStubComponent, 1, 1); + expectSpyCall(isQueryResultNotEmptySpy, 3, anotherQueryResult[0]); }); - it('... should contain panel body with ForceGraphComponent (stubbed) if results are available', () => { - // Panel body - const bodyDes = getAndExpectDebugElementByCss( - compDe, - 'div#awg-graph-visualizer-construct-results > div.accordion-body', - 1, - 1 - ); - - // ForceGraph - getAndExpectDebugElementByDirective(bodyDes[0], ForceGraphStubComponent, 1, 1); + describe('... should return false if ...', () => { + it('... queryResult is empty array', () => { + expectSpyCall(isQueryResultNotEmptySpy, 2, expectedQueryResult[0]); + + // Mock empty response + const emptyQueryResult = []; + component.queryResult$ = observableOf(emptyQueryResult); + detectChangesOnPush(fixture); + + expectSpyCall(isQueryResultNotEmptySpy, 3, emptyQueryResult); + expectToBe(component.isQueryResultNotEmpty(emptyQueryResult), false); + }); + + it('... queryResult.subject is undefined or empty string', () => { + expectSpyCall(isQueryResultNotEmptySpy, 2, expectedQueryResult[0]); + + // Mock undefined response + const undefinedQueryResult = { + subject: undefined, + predicate: 'example:has', + object: 'example:Success', + }; + component.queryResult$ = observableOf([undefinedQueryResult]); + detectChangesOnPush(fixture); + + expectSpyCall(isQueryResultNotEmptySpy, 3, undefinedQueryResult[0]); + expectToBe(component.isQueryResultNotEmpty([undefinedQueryResult]), false); + + // Mock empty response + const emptyQueryResult = { + subject: '', + predicate: 'example:has', + object: 'example:Success', + }; + component.queryResult$ = observableOf([emptyQueryResult]); + detectChangesOnPush(fixture); + + expectSpyCall(isQueryResultNotEmptySpy, 5, emptyQueryResult[0]); + expectToBe(component.isQueryResultNotEmpty([emptyQueryResult]), false); + }); + + it('... queryResult.predicate is undefined or empty string', () => { + expectSpyCall(isQueryResultNotEmptySpy, 2, expectedQueryResult[0]); + + // Mock undefined response + const undefinedQueryResult = { + subject: 'example:Test', + predicate: undefined, + object: 'example:Success', + }; + component.queryResult$ = observableOf([undefinedQueryResult]); + detectChangesOnPush(fixture); + + expectSpyCall(isQueryResultNotEmptySpy, 3, undefinedQueryResult[0]); + expectToBe(component.isQueryResultNotEmpty([undefinedQueryResult]), false); + + // Mock empty response + const emptyQueryResult = { + subject: 'example:Test', + predicate: '', + object: 'example:Success', + }; + component.queryResult$ = observableOf([emptyQueryResult]); + detectChangesOnPush(fixture); + + expectSpyCall(isQueryResultNotEmptySpy, 5, emptyQueryResult[0]); + expectToBe(component.isQueryResultNotEmpty([emptyQueryResult]), false); + }); + + it('... queryResult.object is undefined or empty string', () => { + expectSpyCall(isQueryResultNotEmptySpy, 2, expectedQueryResult[0]); + + // Mock undefined response + const undefinedQueryResult = { + subject: 'example:Test', + predicate: 'example:has', + object: undefined, + }; + component.queryResult$ = observableOf([undefinedQueryResult]); + detectChangesOnPush(fixture); + + expectSpyCall(isQueryResultNotEmptySpy, 3, undefinedQueryResult[0]); + expectToBe(component.isQueryResultNotEmpty([undefinedQueryResult]), false); + + // Mock empty response + const emptyQueryResult = { + subject: 'example:Test', + predicate: 'example:has', + object: '', + }; + component.queryResult$ = observableOf([emptyQueryResult]); + detectChangesOnPush(fixture); + + expectSpyCall(isQueryResultNotEmptySpy, 5, emptyQueryResult[0]); + expectToBe(component.isQueryResultNotEmpty([emptyQueryResult]), false); + }); + + it('... queryResult.subject, queryResult.predicate and queryResult.object are undefined or empty string', () => { + expectSpyCall(isQueryResultNotEmptySpy, 2, expectedQueryResult[0]); + + // Mock undefined response + const undefinedQueryResult = { subject: undefined, predicate: undefined, object: undefined }; + component.queryResult$ = observableOf([undefinedQueryResult]); + detectChangesOnPush(fixture); + + expectSpyCall(isQueryResultNotEmptySpy, 3, undefinedQueryResult[0]); + expectToBe(component.isQueryResultNotEmpty([undefinedQueryResult]), false); + + // Mock empty response + const emptyQueryResult = { subject: '', predicate: '', object: '' }; + component.queryResult$ = observableOf([emptyQueryResult]); + detectChangesOnPush(fixture); + + expectSpyCall(isQueryResultNotEmptySpy, 5, emptyQueryResult[0]); + expectToBe(component.isQueryResultNotEmpty([emptyQueryResult]), false); + }); }); - it('... should pass down `queryResult` and `defaultForceGraphHeight` to forceGraph component', () => { - const forceGraphDes = getAndExpectDebugElementByDirective(compDe, ForceGraphStubComponent, 1, 1); - const forceGraphCmp = forceGraphDes[0].injector.get(ForceGraphStubComponent) as ForceGraphStubComponent; + it('... should return true if queryResult is not empty', () => { + expectSpyCall(isQueryResultNotEmptySpy, 2, expectedQueryResult[0]); - expect(forceGraphCmp.queryResultTriples).toBeDefined(); - expect(forceGraphCmp.queryResultTriples) - .withContext(`should equal ${expectedTriples}`) - .toEqual(expectedTriples); + // Mock non-empty response + const nonEmptyQueryResult = { + subject: 'example:Test', + predicate: 'example:has', + object: 'example:Success', + }; + component.queryResult$ = observableOf([nonEmptyQueryResult]); + detectChangesOnPush(fixture); - expect(forceGraphCmp.height).toBeDefined(); - expect(forceGraphCmp.height).withContext(`should be: ${expectedHeight}`).toBe(expectedHeight); + expectSpyCall(isQueryResultNotEmptySpy, 3, nonEmptyQueryResult[0]); + expectToBe(component.isQueryResultNotEmpty([nonEmptyQueryResult]), true); }); }); @@ -377,108 +1075,5 @@ describe('ConstructResultsComponent (DONE)', () => { expectSpyCall(emitClickedNodeRequestSpy, 1, node); }); }); - - describe('#preventPanelCollapseOnFullscreen()', () => { - it('... should have a method `preventPanelCollapseOnFullscreen`', () => { - expect(component.preventPanelCollapseOnFullscreen).toBeDefined(); - }); - - it('... should trigger on event from ngb-accordion', () => { - const accordionDes = getAndExpectDebugElementByDirective(compDe, NgbAccordion, 1, 1); - const accordionCmp = accordionDes[0].injector.get(NgbAccordion) as NgbAccordion; - - const panelChangeEvent: NgbPanelChangeEvent = { - panelId: 'panelChangeId', - nextState: true, - preventDefault: () => { - // Intentional empty test override - }, - }; - - // Emit change event from accordion - accordionCmp.panelChange.emit(panelChangeEvent); - - expectSpyCall(preventPanelCollapseOnFullscreenSpy, 1, panelChangeEvent); - }); - - it('... should not do anything if no $event is provided', () => { - const accordionDes = getAndExpectDebugElementByDirective(compDe, NgbAccordion, 1, 1); - const accordionCmp = accordionDes[0].injector.get(NgbAccordion) as NgbAccordion; - - // Emit undefined change event from accordion - accordionCmp.panelChange.emit(undefined); - - expectSpyCall(preventPanelCollapseOnFullscreenSpy, 1, undefined); - }); - - it('... should trigger $event.preventDefault() if isFullscreen == true && $event.nextState == false', () => { - const accordionDes = getAndExpectDebugElementByDirective(compDe, NgbAccordion, 1, 1); - const accordionCmp = accordionDes[0].injector.get(NgbAccordion) as NgbAccordion; - - const panelChangeEvent: NgbPanelChangeEvent = { - panelId: 'panelChangeId', - nextState: false, - preventDefault: () => { - // Intentional empty test override - }, - }; - const preventDefaultSpy: Spy = spyOn(panelChangeEvent, 'preventDefault').and.callThrough(); - - // Set fullscreen mode - component.isFullscreen = true; - - // Emit change event from accordion - accordionCmp.panelChange.emit(panelChangeEvent); - - expectSpyCall(preventPanelCollapseOnFullscreenSpy, 1, panelChangeEvent); - expectSpyCall(preventDefaultSpy, 1, undefined); - }); - - it('... should not trigger $event.preventDefault() if $event.nextState == true', () => { - const accordionDes = getAndExpectDebugElementByDirective(compDe, NgbAccordion, 1, 1); - const accordionCmp = accordionDes[0].injector.get(NgbAccordion) as NgbAccordion; - - const panelChangeEvent: NgbPanelChangeEvent = { - panelId: 'panelChangeId', - nextState: true, - preventDefault: () => { - // Intentional empty test override - }, - }; - const preventDefaultSpy: Spy = spyOn(panelChangeEvent, 'preventDefault').and.callThrough(); - - // Set fullscreen mode - component.isFullscreen = true; - - // Emit change event from accordion - accordionCmp.panelChange.emit(panelChangeEvent); - - expectSpyCall(preventPanelCollapseOnFullscreenSpy, 1, panelChangeEvent); - expectSpyCall(preventDefaultSpy, 0, undefined); - }); - - it('... should not trigger $event.preventDefault() if isFullscreen == false', () => { - const accordionDes = getAndExpectDebugElementByDirective(compDe, NgbAccordion, 1, 1); - const accordionCmp = accordionDes[0].injector.get(NgbAccordion) as NgbAccordion; - - const panelChangeEvent: NgbPanelChangeEvent = { - panelId: 'panelChangeId', - nextState: false, - preventDefault: () => { - // Intentional empty test override - }, - }; - const preventDefaultSpy: Spy = spyOn(panelChangeEvent, 'preventDefault').and.callThrough(); - - // Unset fullscreen mode - component.isFullscreen = false; - - // Emit change event from accordion - accordionCmp.panelChange.emit(panelChangeEvent); - - expectSpyCall(preventPanelCollapseOnFullscreenSpy, 1, panelChangeEvent); - expectSpyCall(preventDefaultSpy, 0, undefined); - }); - }); }); }); diff --git a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/construct-results/construct-results.component.ts b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/construct-results/construct-results.component.ts index 25fbcac309..562f138a3e 100644 --- a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/construct-results/construct-results.component.ts +++ b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/construct-results/construct-results.component.ts @@ -1,6 +1,5 @@ import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; -import { NgbPanelChangeEvent } from '@ng-bootstrap/ng-bootstrap'; import { Observable } from 'rxjs'; import { D3SimulationNode, Triple } from '../models'; @@ -50,6 +49,37 @@ export class ConstructResultsComponent { @Output() clickedNodeRequest: EventEmitter = new EventEmitter(); + /** + * Public method: isQueryResultNotEmpty. + * + * It checks if a given queryResult triple is not empty. + * + * @param {Triple[]} queryResult The given queryResult triples. + * + * @returns {boolean} The boolean value of the comparison result. + */ + isQueryResultNotEmpty(queryResult: Triple[]): boolean { + if (queryResult.length === 0) { + return false; + } + return queryResult.every(triple => { + const { subject, predicate, object } = triple; + return Boolean(subject) && Boolean(predicate) && Boolean(object); + }); + } + + /** + * Public method: isAccordionItemDisabled. + * + * It returns a boolean flag if the accordion item should be disabled. + * It returns true if fullscreenMode is set, otherwise false. + * + * @returns {boolean} The boolean value of the comparison. + */ + isAccordionItemDisabled(): boolean { + return this.isFullscreen ? true : false; + } + /** * Public method: onGraphNodeClick. * @@ -66,20 +96,4 @@ export class ConstructResultsComponent { } this.clickedNodeRequest.emit(node); } - - /** - * Public method: preventPanelCollapseOnFullscreen. - * - * It prevents the given panel event from being collapsed in fullscreen mode. - * - * @returns {void} Prevents the panel collapse. - */ - preventPanelCollapseOnFullscreen($event: NgbPanelChangeEvent): void { - if (!$event) { - return; - } - if (this.isFullscreen && $event.nextState === false) { - $event.preventDefault(); - } - } } diff --git a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/force-graph/d3/d3.service.ts b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/force-graph/d3/d3.service.ts index 9d107f6963..6c65a20cb4 100644 --- a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/force-graph/d3/d3.service.ts +++ b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/force-graph/d3/d3.service.ts @@ -10,9 +10,9 @@ import { D3SimulationNode, } from '../../models'; -import * as d3_drag from 'd3-drag'; -import * as d3_selection from 'd3-selection'; -import * as d3_zoom from 'd3-zoom'; +import * as D3_DRAG from 'd3-drag'; +import * as D3_SELECTION from 'd3-selection'; +import * as D3_ZOOM from 'd3-zoom'; /** * The D3 service. @@ -37,7 +37,7 @@ export class D3Service { * @returns {void} Sets the drag behaviour. */ applyDragBehaviour(dragContext: D3Selection, simulation: D3Simulation): void { - // Const dragElement: D3Selection = d3_selection.select(element); + // Const dragElement: D3Selection = D3_SELECTION.select(element); const dragStart = (event: any, d: D3SimulationNode) => { // Prevent propagation of dragstart to parent elements @@ -66,8 +66,7 @@ export class D3Service { }; // Create drag behaviour - const dragBehaviour: D3DragBehaviour = d3_drag - .drag() + const dragBehaviour: D3DragBehaviour = D3_DRAG.drag() .on('start', dragStart) .on('drag', dragged) .on('end', dragEnd); @@ -87,8 +86,8 @@ export class D3Service { */ applyZoomBehaviour(zoomContainerElement: any, svgElement: any): void { // Select the elements - const svg: D3Selection = d3_selection.select(svgElement); - const zoomContainer: D3Selection = d3_selection.select(zoomContainerElement); + const svg: D3Selection = D3_SELECTION.select(svgElement); + const zoomContainer: D3Selection = D3_SELECTION.select(zoomContainerElement); // Perform the zooming const zoomed = (event: any) => { @@ -98,7 +97,7 @@ export class D3Service { }; // Create zoom behaviour that calls the zoomActions on zoom - const zoomBehaviour = d3_zoom.zoom().on('zoom', zoomed); + const zoomBehaviour = D3_ZOOM.zoom().on('zoom', zoomed); // Apply zoom behaviour svg.call(zoomBehaviour); diff --git a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/force-graph/force-graph.component.html b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/force-graph/force-graph.component.html index e9d6c20cfe..8c6386209a 100644 --- a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/force-graph/force-graph.component.html +++ b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/force-graph/force-graph.component.html @@ -26,36 +26,39 @@
    -
    - -
    - - -
    + }
    diff --git a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/force-graph/force-graph.component.scss b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/force-graph/force-graph.component.scss index 444fb6093c..5aaeb11702 100644 --- a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/force-graph/force-graph.component.scss +++ b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/force-graph/force-graph.component.scss @@ -1,4 +1,4 @@ -@import 'src/assets/themes/scss/shared'; +@import '/src/assets/themes/scss/shared'; #awg-force-graph-container { position: relative; @@ -167,11 +167,15 @@ input[type='range'].awg-force-graph-zoom-slider { } :host ::ng-deep .node-text { - font: 15px 'Roboto', sans-serif; + font: + 15px 'Roboto', + sans-serif; fill: black; } :host ::ng-deep .link-text { - font: 13px 'Roboto', sans-serif; + font: + 13px 'Roboto', + sans-serif; fill: grey; } diff --git a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/force-graph/force-graph.component.ts b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/force-graph/force-graph.component.ts index 830ea6ff50..d405315ad8 100644 --- a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/force-graph/force-graph.component.ts +++ b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/force-graph/force-graph.component.ts @@ -38,10 +38,10 @@ import { import { PrefixPipe } from '../prefix-pipe/prefix.pipe'; import { GraphVisualizerService } from '../services/graph-visualizer.service'; -import * as d3_drag from 'd3-drag'; -import * as d3_force from 'd3-force'; -import * as d3_selection from 'd3-selection'; -import * as d3_zoom from 'd3-zoom'; +import * as D3_DRAG from 'd3-drag'; +import * as D3_FORCE from 'd3-force'; +import * as D3_SELECTION from 'd3-selection'; +import * as D3_ZOOM from 'd3-zoom'; /** * Object constant with a set of forces. @@ -212,7 +212,10 @@ export class ForceGraphComponent implements OnInit, OnChanges, OnDestroy { * @param {GraphVisualizerService} graphVisualizerService Instance of the GraphVisualizerService. * @param {PrefixPipe} prefixPipe Instance of the PrefixPipe. */ - constructor(private graphVisualizerService: GraphVisualizerService, private prefixPipe: PrefixPipe) {} + constructor( + private graphVisualizerService: GraphVisualizerService, + private prefixPipe: PrefixPipe + ) {} /** * HostListener: onResize. @@ -365,7 +368,7 @@ export class ForceGraphComponent implements OnInit, OnChanges, OnDestroy { */ private _cleanSVG(): void { // Remove everything below the SVG element - d3_selection.selectAll('svg.force-graph > *').remove(); + D3_SELECTION.selectAll('svg.force-graph > *').remove(); } /** @@ -406,8 +409,7 @@ export class ForceGraphComponent implements OnInit, OnChanges, OnDestroy { // ==================== Add SVG ===================== if (!this._svg) { - this._svg = d3_selection - .select(this._graphContainer.nativeElement) + this._svg = D3_SELECTION.select(this._graphContainer.nativeElement) .append('svg') .attr('class', 'force-graph'); } @@ -457,24 +459,22 @@ export class ForceGraphComponent implements OnInit, OnChanges, OnDestroy { */ private _setupForceSimulation(): void { // Set up the simulation - this._forceSimulation = d3_force.forceSimulation(); + this._forceSimulation = D3_FORCE.forceSimulation(); // Create forces - const chargeForce = d3_force - .forceManyBody() - .strength((d: D3SimulationNode) => this._nodeRadius(d) * FORCES.CHARGE_STRENGTH); + const chargeForce = D3_FORCE.forceManyBody().strength( + (d: D3SimulationNode) => this._nodeRadius(d) * FORCES.CHARGE_STRENGTH + ); - const centerForce = d3_force.forceCenter(this._divWidth / 2, this._divHeight / 2); + const centerForce = D3_FORCE.forceCenter(this._divWidth / 2, this._divHeight / 2); - const collideForce = d3_force - .forceCollide() + const collideForce = D3_FORCE.forceCollide() .strength(FORCES.COLLISION_STRENGTH) .radius(FORCES.COLLISION_RADIUS) .iterations(2); // Create a custom link force with id accessor to use named sources and targets - const linkForce = d3_force - .forceLink() + const linkForce = D3_FORCE.forceLink() .links(this._simulationData.links) .id((d: D3SimulationLink) => d.predicate) .distance(FORCES.LINK_DISTANCE); @@ -705,8 +705,7 @@ export class ForceGraphComponent implements OnInit, OnChanges, OnDestroy { }; // Create drag behaviour - const dragBehaviour: D3DragBehaviour = d3_drag - .drag() + const dragBehaviour: D3DragBehaviour = D3_DRAG.drag() .on('start', dragStart) .on('drag', dragged) .on('end', dragEnd); @@ -742,8 +741,7 @@ export class ForceGraphComponent implements OnInit, OnChanges, OnDestroy { }; // Create zoom behaviour - this._zoomBehaviour = d3_zoom - .zoom() + this._zoomBehaviour = D3_ZOOM.zoom() .scaleExtent([this.sliderConfig.min, this.sliderConfig.max]) .on('zoom', zoomed); diff --git a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/graph-visualizer.component.html b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/graph-visualizer.component.html index 5e7f9037c6..0c4d0d76f8 100644 --- a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/graph-visualizer.component.html +++ b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/graph-visualizer.component.html @@ -1,61 +1,61 @@ -
    -
    -
    -
    - - +@if (graphRDFInputData) { +
    +
    +
    +
    + + +
    +
    + + +
    - -
    - - + +
    + + + @if (query.queryType === 'construct') { + -
    + (clickedNodeRequest)="onGraphNodeClick($event)"> + } @else { + @if (query.queryType === 'select') { + + } @else { + + } + } + + + +
    - - -
    - - - - - - - - - - - - - - - -
    -
    +} diff --git a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/graph-visualizer.component.spec.ts b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/graph-visualizer.component.spec.ts index 6d3884b3e4..795c430acf 100644 --- a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/graph-visualizer.component.spec.ts +++ b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/graph-visualizer.component.spec.ts @@ -86,6 +86,8 @@ class TriplesEditorStubComponent { class UnsupportedTypeResultsStubComponent { @Input() queryType: string; // Query.queryType ? + @Input() + isFullscreen: boolean; } describe('GraphVisualizerComponent (DONE)', () => { diff --git a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/graph-visualizer.component.ts b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/graph-visualizer.component.ts index ff9aa9d0bb..3be62f7e0f 100644 --- a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/graph-visualizer.component.ts +++ b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/graph-visualizer.component.ts @@ -98,7 +98,10 @@ export class GraphVisualizerComponent implements OnInit { * @param {GraphVisualizerService} graphVisualizerService Instance of the GraphVisualizerService. * @param {ToastService} toastService Instance of the ToastService. */ - constructor(private graphVisualizerService: GraphVisualizerService, private toastService: ToastService) {} + constructor( + private graphVisualizerService: GraphVisualizerService, + private toastService: ToastService + ) {} /** * Angular life cycle hook: ngOnInit. diff --git a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/models/d3-force-simulation.model.ts b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/models/d3-force-simulation.model.ts index 58522d9a9e..9606c18971 100644 --- a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/models/d3-force-simulation.model.ts +++ b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/models/d3-force-simulation.model.ts @@ -3,7 +3,7 @@ import { EventEmitter } from '@angular/core'; import { D3SimulationLink } from './d3-simulation-link.model'; import { D3SimulationNode } from './d3-simulation-node.model'; -import * as d3_force from 'd3-force'; +import * as D3_FORCE from 'd3-force'; /** * Object constant with a set of forces. @@ -23,7 +23,7 @@ const FORCES = { * * It represents a d3 force simulation. */ -export interface D3Simulation extends d3_force.Simulation {} +export interface D3Simulation extends D3_FORCE.Simulation {} /** * The D3ForceSimulationOptions interface. @@ -140,7 +140,7 @@ export class D3ForceSimulation { const ticker = this.ticker; // Set up the simulation - this.forceSimulation = d3_force.forceSimulation(); + this.forceSimulation = D3_FORCE.forceSimulation(); this._createForces(options); @@ -175,19 +175,17 @@ export class D3ForceSimulation { */ private _createForces(options: D3ForceSimulationOptions): void { // Create forces - this._chargeForce = d3_force.forceManyBody().strength((d: D3SimulationNode) => d['r'] * FORCES.CHARGE_STRENGTH); + this._chargeForce = D3_FORCE.forceManyBody().strength((d: D3SimulationNode) => d['r'] * FORCES.CHARGE_STRENGTH); - this._centerForce = d3_force.forceCenter(options.width / 2, options.height / 2); + this._centerForce = D3_FORCE.forceCenter(options.width / 2, options.height / 2); - this._collideForce = d3_force - .forceCollide() + this._collideForce = D3_FORCE.forceCollide() .strength(FORCES.COLLISION_STRENGTH) .radius(d => d['r'] + 5) .iterations(2); // Create a custom link force with id accessor to use named sources and targets - this._linkForce = d3_force - .forceLink() + this._linkForce = D3_FORCE.forceLink() .links(this.links) .id((d: D3SimulationLink) => d.predicate) .distance(FORCES.LINK_DISTANCE); // FORCES.LINKS diff --git a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/models/d3-simulation-link.model.ts b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/models/d3-simulation-link.model.ts index 839a909828..33357ea396 100644 --- a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/models/d3-simulation-link.model.ts +++ b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/models/d3-simulation-link.model.ts @@ -1,6 +1,6 @@ import { D3SimulationNode } from './d3-simulation-node.model'; -import * as d3_force from 'd3-force'; +import * as D3_FORCE from 'd3-force'; /** * Type alias: D3SimulationNodeOrStringOrNumber. @@ -15,7 +15,7 @@ type D3SimulationNodeOrStringOrNumber = D3SimulationNode | string | number; * It is used in the context of the graph visualizer * to store the data of a d3 simulation link. */ -export class D3SimulationLink implements d3_force.SimulationLinkDatum { +export class D3SimulationLink implements D3_FORCE.SimulationLinkDatum { /** * The source of the simulation link. */ diff --git a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/models/d3-simulation-node.model.ts b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/models/d3-simulation-node.model.ts index 2ebe498db2..1bd583e098 100644 --- a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/models/d3-simulation-node.model.ts +++ b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/models/d3-simulation-node.model.ts @@ -1,4 +1,4 @@ -import * as d3_force from 'd3-force'; +import * as D3_FORCE from 'd3-force'; /** * The D3SimulationNodeType enumeration. @@ -16,7 +16,7 @@ export enum D3SimulationNodeType { * It is used in the context of the graph visualizer * to store the data of a d3 simulation node. */ -export class D3SimulationNode implements d3_force.SimulationNodeDatum { +export class D3SimulationNode implements D3_FORCE.SimulationNodeDatum { /** * The id of the simulation node. */ diff --git a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/select-results/select-results.component.html b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/select-results/select-results.component.html index d563f71b1e..08ce82b446 100644 --- a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/select-results/select-results.component.html +++ b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/select-results/select-results.component.html @@ -1,25 +1,30 @@ - - - - - - - - - - - +
    +
    +
    + +
    +
    +
    + + @if (queryResult$ | async; as queryResult) { + @if (isQueryResultNotEmpty(queryResult)) { + + + + } @else { + + } + } @else { + + } - - - - - - - +
    +
    +
    +
    diff --git a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/select-results/select-results.component.spec.ts b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/select-results/select-results.component.spec.ts index b4d99c1004..7972014d49 100644 --- a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/select-results/select-results.component.spec.ts +++ b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/select-results/select-results.component.spec.ts @@ -1,16 +1,18 @@ import { Component, DebugElement, EventEmitter, Input, NgModule, Output } from '@angular/core'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; -import { EMPTY, Observable, of as observableOf } from 'rxjs'; +import { EMPTY, Observable, lastValueFrom, of as observableOf } from 'rxjs'; import Spy = jasmine.Spy; -import { NgbAccordion, NgbAccordionModule, NgbConfig, NgbPanelChangeEvent } from '@ng-bootstrap/ng-bootstrap'; +import { NgbAccordionDirective, NgbAccordionModule, NgbConfig } from '@ng-bootstrap/ng-bootstrap'; import { click } from '@testing/click-helper'; import { detectChangesOnPush } from '@testing/detect-changes-on-push-helper'; - import { expectSpyCall, + expectToBe, + expectToContain, + expectToEqual, getAndExpectDebugElementByCss, getAndExpectDebugElementByDirective, } from '@testing/expect-helper'; @@ -42,10 +44,10 @@ describe('SelectResultsComponent (DONE)', () => { let expectedQueryTime: number; let expectedIsFullscreen: boolean; - let tableClickSpy: Spy; let emitClickedTableRequestSpy: Spy; - let isNotEmptySpy: Spy; - let preventPanelCollapseOnFullscreenSpy: Spy; + let isAccordionItemDisabledSpy: Spy; + let isQueryResultNotEmptySpy: Spy; + let tableClickSpy: Spy; // Global NgbConfigModule @NgModule({ imports: [NgbAccordionModule], exports: [NgbAccordionModule] }) @@ -58,7 +60,7 @@ describe('SelectResultsComponent (DONE)', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ - imports: [NgbAccordionWithConfigModule], + imports: [NgbAccordionWithConfigModule, NgbAccordionDirective], declarations: [ SelectResultsComponent, SparqlNoResultsStubComponent, @@ -89,10 +91,10 @@ describe('SelectResultsComponent (DONE)', () => { // Spies on component functions // `.and.callThrough` will track the spy down the nested describes, see // https://jasmine.github.io/2.0/introduction.html#section-Spies:_%3Ccode%3Eand.callThrough%3C/code%3E - tableClickSpy = spyOn(component, 'onTableNodeClick').and.callThrough(); emitClickedTableRequestSpy = spyOn(component.clickedTableRequest, 'emit').and.callThrough(); - isNotEmptySpy = spyOn(component, 'isNotEmpty').and.callThrough(); - preventPanelCollapseOnFullscreenSpy = spyOn(component, 'preventPanelCollapseOnFullscreen').and.callThrough(); + isAccordionItemDisabledSpy = spyOn(component, 'isAccordionItemDisabled').and.callThrough(); + isQueryResultNotEmptySpy = spyOn(component, 'isQueryResultNotEmpty').and.callThrough(); + tableClickSpy = spyOn(component, 'onTableNodeClick').and.callThrough(); }); it('... should create', () => { @@ -113,12 +115,9 @@ describe('SelectResultsComponent (DONE)', () => { }); describe('VIEW', () => { - it('... should contain one ngb-accordion without panel (div.accordion-item) yet', () => { - // Ngb-accordion debug element - const accordionDes = getAndExpectDebugElementByCss(compDe, 'ngb-accordion', 1, 1); - - // Panel - getAndExpectDebugElementByCss(accordionDes[0], 'div.accordion-item', 0, 0, 'yet'); + it('... should contain no div.accordion yet', () => { + // Div.accordion debug element + getAndExpectDebugElementByCss(compDe, 'div.accordion', 0, 0); }); }); }); @@ -134,218 +133,666 @@ describe('SelectResultsComponent (DONE)', () => { fixture.detectChanges(); }); - it('... should have `queryResult` input', () => { - expect(component.queryResult$).toBeDefined(); - expect(component.queryResult$) - .withContext(`should equal ${expectedQueryResult$}`) - .toEqual(expectedQueryResult$); - }); + it('... should have `queryResult` input', waitForAsync(() => { + expectToEqual(component.queryResult$, expectedQueryResult$); + expectAsync(lastValueFrom(component.queryResult$)).toBeResolved(); + expectAsync(lastValueFrom(component.queryResult$)) + .withContext(`should be resolved to ${expectedQueryResult}}`) + .toBeResolvedTo(expectedQueryResult); + })); it('... should have `queryTime` input', () => { - expect(component.queryTime).toBeDefined(); - expect(component.queryTime).withContext(`should equal ${expectedQueryTime}`).toEqual(expectedQueryTime); + expectToBe(component.queryTime, expectedQueryTime); }); it('... should have `isFullscreen` input', () => { - expect(component.isFullscreen).toBeDefined(); - expect(component.isFullscreen) - .withContext(`should equal ${expectedIsFullscreen}`) - .toEqual(expectedIsFullscreen); + expectToBe(component.isFullscreen, expectedIsFullscreen); }); describe('VIEW', () => { - it('... should contain one ngb-accordion with panel (div.accordion-item) header and body', () => { - // Ngb-accordion debug element - const accordionDes = getAndExpectDebugElementByCss(compDe, 'ngb-accordion', 1, 1); - - // Panel (div.card) - const panelDes = getAndExpectDebugElementByCss(accordionDes[0], 'div.accordion-item', 1, 1); // Panel (div.card) - // Header - getAndExpectDebugElementByCss( - panelDes[0], - 'div#awg-graph-visualizer-select-results-header.accordion-header', - 1, - 1 - ); // Panel (div.card) - // Body - getAndExpectDebugElementByCss( - panelDes[0], - 'div#awg-graph-visualizer-select-results > div.accordion-body', - 1, - 1 - ); + it('... should contain one div.accordion', () => { + // NgbAccordion debug element + getAndExpectDebugElementByCss(compDe, 'div.accordion', 1, 1); }); - it('... should display panel header button', () => { - // Panel header button - const btnDes = getAndExpectDebugElementByCss( - compDe, - 'div#awg-graph-visualizer-select-results-header > button.accordion-button', - 1, - 1 - ); - - const btnEl = btnDes[0].nativeElement; + describe('not in fullscreen mode', () => { + it('... should contain one div.accordion-item with header and open body in div.accordion', () => { + // NgbAccordion debug element + const accordionDes = getAndExpectDebugElementByCss(compDe, 'div.accordion', 1, 1); + + // Div.accordion-item + const itemDes = getAndExpectDebugElementByCss( + accordionDes[0], + 'div#awg-graph-visualizer-select-results.accordion-item', + 1, + 1 + ); + // Header (div.accordion-header) + getAndExpectDebugElementByCss( + itemDes[0], + 'div#awg-graph-visualizer-select-results > div.accordion-header', + 1, + 1 + ); + + // Body open (div.accordion-collapse) + const itemBodyDes = getAndExpectDebugElementByCss( + itemDes[0], + 'div#awg-graph-visualizer-select-results-collapse', + 1, + 1 + ); + const itemBodyEl = itemBodyDes[0].nativeElement; + + expectToContain(itemBodyEl.classList, 'show'); + }); + + it('... should display item header button', () => { + // Header debug elements + const itemHeaderDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-select-results > div.accordion-header', + 1, + 1 + ); + + // Item header button + const btnDes = getAndExpectDebugElementByCss(itemHeaderDes[0], 'button.accordion-button', 1, 1); + const btnEl = btnDes[0].nativeElement; + + // Check button content + expectToBe(btnEl.textContent, 'Resultat'); + }); + + it('... should toggle item body on click', () => { + // Header debug elements + const itemHeaderDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-select-results > div.accordion-header', + 1, + 1 + ); + + // Button debug elements + const btnDes = getAndExpectDebugElementByCss(itemHeaderDes[0], 'button.accordion-button', 1, 1); + // Button native elements to click on + const btnEl = btnDes[0].nativeElement; + + // Item body is open + let itemBodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-select-results > div.accordion-collapse', + 1, + 1, + 'open' + ); + let itemBodyEl = itemBodyDes[0].nativeElement; + + expectToContain(itemBodyEl.classList, 'show'); + + // Click header button + click(btnEl as HTMLElement); + detectChangesOnPush(fixture); + + // Item body is collapsed + itemBodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-select-results > div.accordion-collapse', + 1, + 1, + 'collapsed' + ); + itemBodyEl = itemBodyDes[0].nativeElement; + + expectToContain(itemBodyEl.classList, 'collapse'); + + // Click header button + click(btnEl as HTMLElement); + detectChangesOnPush(fixture); + + // Item body is open again + itemBodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-select-results > div.accordion-collapse', + 1, + 1, + 'open' + ); + itemBodyEl = itemBodyDes[0].nativeElement; + + expectToContain(itemBodyEl.classList, 'show'); + }); + + describe('... should contain TwelveToneSpinnerComponent (stubbed) in item body while loading if ... ', () => { + it('... queryResult$ is EMPTY', () => { + // Mock empty observable + component.queryResult$ = EMPTY; + detectChangesOnPush(fixture); + + // Item body + const bodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-select-results-collapse > div.accordion-body', + 1, + 1 + ); + + getAndExpectDebugElementByDirective(bodyDes[0], TwelveToneSpinnerStubComponent, 1, 1); + }); + + it('... queryResult$ is undefined', () => { + // Mock undefined response + component.queryResult$ = undefined; + detectChangesOnPush(fixture); + + // Item body + const bodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-select-results-collapse > div.accordion-body', + 1, + 1 + ); + + getAndExpectDebugElementByDirective(bodyDes[0], TwelveToneSpinnerStubComponent, 1, 1); + }); + + it('... queryResult$ is null', () => { + // Mock null response + component.queryResult$ = null; + detectChangesOnPush(fixture); + + // Item body + const bodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-select-results-collapse > div.accordion-body', + 1, + 1 + ); + + getAndExpectDebugElementByDirective(bodyDes[0], TwelveToneSpinnerStubComponent, 1, 1); + }); + }); + + describe('... should contain item body with SparqlNoResultsStubComponent (stubbed) if ... ', () => { + it('... no results are available', () => { + // Mock empty response + component.queryResult$ = observableOf({ head: { vars: [] }, body: { bindings: [] } }); + detectChangesOnPush(fixture); + + // Item body + const bodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-select-results-collapse > div.accordion-body', + 1, + 1 + ); + + // SparqlNoResultsStubComponent + getAndExpectDebugElementByDirective(bodyDes[0], SparqlNoResultsStubComponent, 1, 1); + }); + + it('... queryResult.head is undefined', () => { + // Mock undefined response + const undefinedQueryResult = { head: undefined, body: { bindings: [{ test: 'Test' }] } }; + component.queryResult$ = observableOf(undefinedQueryResult); + detectChangesOnPush(fixture); + + // Item body + const bodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-select-results-collapse > div.accordion-body', + 1, + 1 + ); + + // SparqlNoResultsStubComponent + getAndExpectDebugElementByDirective(bodyDes[0], SparqlNoResultsStubComponent, 1, 1); + }); + + it('... queryResult.body is undefined', () => { + // Mock undefined response + const undefinedQueryResult = { head: { vars: ['Test'] }, body: undefined }; + component.queryResult$ = observableOf(undefinedQueryResult); + detectChangesOnPush(fixture); + + // Item body + const bodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-select-results-collapse > div.accordion-body', + 1, + 1 + ); + + // SparqlNoResultsStubComponent + getAndExpectDebugElementByDirective(bodyDes[0], SparqlNoResultsStubComponent, 1, 1); + }); + + it('... queryResult.head and queryResult.body are undefined', () => { + // Mock undefined response + const undefinedQueryResult = { head: undefined, body: undefined }; + component.queryResult$ = observableOf(undefinedQueryResult); + detectChangesOnPush(fixture); + + // Item body + const body = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-select-results-collapse > div.accordion-body', + 1, + 1 + ); + + // SparqlNoResultsStubComponent + getAndExpectDebugElementByDirective(body[0], SparqlNoResultsStubComponent, 1, 1); + }); + }); + + it('... should contain item body with SparqlTableComponent (stubbed) if results are available', () => { + // Item body + const bodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-select-results-collapse > div.accordion-body', + 1, + 1 + ); + + // SparqlTable + getAndExpectDebugElementByDirective(bodyDes[0], SparqlTableStubComponent, 1, 1); + }); + + it('... should pass down `queryResult` and `queryTime` to sparqlTable component', () => { + const sparqlTableDes = getAndExpectDebugElementByDirective(compDe, SparqlTableStubComponent, 1, 1); + const sparqlTableCmp = sparqlTableDes[0].injector.get( + SparqlTableStubComponent + ) as SparqlTableStubComponent; + + expectToEqual(sparqlTableCmp.queryResult, expectedQueryResult); + expectToBe(sparqlTableCmp.queryTime, expectedQueryTime); + }); + }); - // Check button content - expect(btnEl.textContent).toBeTruthy(); - expect(btnEl.textContent).withContext('should contain Resultat').toContain('Resultat'); + describe('in fullscreen mode', () => { + beforeEach(() => { + // Set fullscreen mode + component.isFullscreen = true; + detectChangesOnPush(fixture); + }); + + it('... should contain one div.accordion-item with header and open body in div.accordion', () => { + // NgbAccordion debug element + const accordionDes = getAndExpectDebugElementByCss(compDe, 'div.accordion', 1, 1); + + // Div.accordion-item + const itemDes = getAndExpectDebugElementByCss( + accordionDes[0], + 'div#awg-graph-visualizer-select-results.accordion-item', + 1, + 1 + ); + // Header (div.accordion-header) + getAndExpectDebugElementByCss( + itemDes[0], + 'div#awg-graph-visualizer-select-results > div.accordion-header', + 1, + 1 + ); + + // Body open (div.accordion-collapse) + const itemBodyDes = getAndExpectDebugElementByCss( + itemDes[0], + 'div#awg-graph-visualizer-select-results-collapse', + 1, + 1 + ); + const itemBodyEl = itemBodyDes[0].nativeElement; + + expectToContain(itemBodyEl.classList, 'show'); + }); + + it('... should display item header button', () => { + // Header debug elements + const itemHeaderDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-select-results > div.accordion-header', + 1, + 1 + ); + + // Item header button + const btnDes = getAndExpectDebugElementByCss(itemHeaderDes[0], 'button.accordion-button', 1, 1); + const btnEl = btnDes[0].nativeElement; + + // Check button content + expectToBe(btnEl.textContent, 'Resultat'); + }); + + it('... should not toggle item body on click', () => { + // Header debug elements + const itemHeaderDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-select-results > div.accordion-header', + 1, + 1 + ); + + // Button debug elements + const btnDes = getAndExpectDebugElementByCss(itemHeaderDes[0], 'button.accordion-button', 1, 1); + // Button native elements to click on + const btnEl = btnDes[0].nativeElement; + + expect(btnEl.disabled).toBeTruthy(); + + // Item body is open + let itemBodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-select-results > div.accordion-collapse', + 1, + 1, + 'open' + ); + let itemBodyEl = itemBodyDes[0].nativeElement; + + expectToContain(itemBodyEl.classList, 'show'); + + // Click header button + click(btnEl as HTMLElement); + detectChangesOnPush(fixture); + + // Item body does not close again + itemBodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-select-results > div.accordion-collapse', + 1, + 1, + 'open' + ); + itemBodyEl = itemBodyDes[0].nativeElement; + + expectToContain(itemBodyEl.classList, 'show'); + }); + + describe('... should contain TwelveToneSpinnerComponent (stubbed) in item body while loading if ... ', () => { + it('... queryResult$ is EMPTY', () => { + // Mock empty observable + component.queryResult$ = EMPTY; + detectChangesOnPush(fixture); + + // Item body + const bodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-select-results-collapse > div.accordion-body', + 1, + 1 + ); + + getAndExpectDebugElementByDirective(bodyDes[0], TwelveToneSpinnerStubComponent, 1, 1); + }); + + it('... queryResult$ is undefined', () => { + // Mock undefined response + component.queryResult$ = undefined; + detectChangesOnPush(fixture); + + // Item body + const bodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-select-results-collapse > div.accordion-body', + 1, + 1 + ); + + getAndExpectDebugElementByDirective(bodyDes[0], TwelveToneSpinnerStubComponent, 1, 1); + }); + + it('... queryResult$ is null', () => { + // Mock null response + component.queryResult$ = null; + detectChangesOnPush(fixture); + + // Item body + const bodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-select-results-collapse > div.accordion-body', + 1, + 1 + ); + + getAndExpectDebugElementByDirective(bodyDes[0], TwelveToneSpinnerStubComponent, 1, 1); + }); + }); + + describe('... should contain item body with SparqlNoResultsStubComponent (stubbed) if ... ', () => { + it('... no results are available', () => { + // Mock empty response + component.queryResult$ = observableOf({ head: { vars: [] }, body: { bindings: [] } }); + detectChangesOnPush(fixture); + + // Item body + const bodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-select-results-collapse > div.accordion-body', + 1, + 1 + ); + + // SparqlNoResultsStubComponent + getAndExpectDebugElementByDirective(bodyDes[0], SparqlNoResultsStubComponent, 1, 1); + }); + + it('... queryResult.head is undefined', () => { + // Mock undefined response + const undefinedQueryResult = { head: undefined, body: { bindings: [{ test: 'Test' }] } }; + component.queryResult$ = observableOf(undefinedQueryResult); + detectChangesOnPush(fixture); + + // Item body + const bodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-select-results-collapse > div.accordion-body', + 1, + 1 + ); + + // SparqlNoResultsStubComponent + getAndExpectDebugElementByDirective(bodyDes[0], SparqlNoResultsStubComponent, 1, 1); + }); + + it('... queryResult.body is undefined', () => { + // Mock undefined response + const undefinedQueryResult = { head: { vars: ['Test'] }, body: undefined }; + component.queryResult$ = observableOf(undefinedQueryResult); + detectChangesOnPush(fixture); + + // Item body + const bodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-select-results-collapse > div.accordion-body', + 1, + 1 + ); + + // SparqlNoResultsStubComponent + getAndExpectDebugElementByDirective(bodyDes[0], SparqlNoResultsStubComponent, 1, 1); + }); + + it('... queryResult.head and queryResult.body are undefined', () => { + // Mock undefined response + const undefinedQueryResult = { head: undefined, body: undefined }; + component.queryResult$ = observableOf(undefinedQueryResult); + detectChangesOnPush(fixture); + + // Item body + const body = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-select-results-collapse > div.accordion-body', + 1, + 1 + ); + + // SparqlNoResultsStubComponent + getAndExpectDebugElementByDirective(body[0], SparqlNoResultsStubComponent, 1, 1); + }); + }); + + it('... should contain item body with SparqlTableComponent (stubbed) if results are available', () => { + // Item body + const bodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-select-results-collapse > div.accordion-body', + 1, + 1 + ); + + // SparqlTable + getAndExpectDebugElementByDirective(bodyDes[0], SparqlTableStubComponent, 1, 1); + }); + + it('... should pass down `queryResult` and `queryTime` to sparqlTable component', () => { + const sparqlTableDes = getAndExpectDebugElementByDirective(compDe, SparqlTableStubComponent, 1, 1); + const sparqlTableCmp = sparqlTableDes[0].injector.get( + SparqlTableStubComponent + ) as SparqlTableStubComponent; + + expectToEqual(sparqlTableCmp.queryResult, expectedQueryResult); + expectToBe(sparqlTableCmp.queryTime, expectedQueryTime); + }); }); + }); - it('... should toggle panel body on click when not in fullscreen mode', () => { - component.isFullscreen = false; - fixture.detectChanges(); - - // Header debug elements - const panelHeaderDes = getAndExpectDebugElementByCss( - compDe, - 'div#awg-graph-visualizer-select-results-header.accordion-header', - 1, - 1 - ); - - // Button debug elements - const btnDes = getAndExpectDebugElementByCss(panelHeaderDes[0], 'button.accordion-button', 1, 1); - // Button native elements to click on - const btnEl = btnDes[0].nativeElement; - - // Panel body is open - getAndExpectDebugElementByCss( - compDe, - 'div#awg-graph-visualizer-select-results > div.accordion-body', - 1, - 1, - 'open' - ); - - // Click header button - click(btnEl as HTMLElement); - detectChangesOnPush(fixture); + describe('#isAccordionItemDisabled()', () => { + it('... should have a method `isAccordionItemDisabled`', () => { + expect(component.isAccordionItemDisabled).toBeDefined(); + }); - // Panel is open - getAndExpectDebugElementByCss( - compDe, - 'div#awg-graph-visualizer-select-results > div.accordion-body', - 0, - 0, - 'collapsed' - ); - - // Click header button - click(btnEl as HTMLElement); - detectChangesOnPush(fixture); + it('... should be triggered from ngbAccordionItem', () => { + expectSpyCall(isAccordionItemDisabledSpy, 2); + }); - getAndExpectDebugElementByCss( - // Panel body is closed again - compDe, - 'div#awg-graph-visualizer-select-results > div.accordion-body', - 1, - 1, - 'open' - ); + it('... should return false if isFullscreen is false', () => { + expectToBe(component.isAccordionItemDisabled(), false); }); - it('... should not toggle panel body on click in fullscreen mode', () => { + it('... should return true if isFullscreen is true', () => { + // Set fullscreen flag to true component.isFullscreen = true; - fixture.detectChanges(); - - // Header debug elements - const panelHeaderDes = getAndExpectDebugElementByCss( - compDe, - 'div#awg-graph-visualizer-select-results-header.accordion-header', - 1, - 1 - ); - - // Button debug elements - const btnDes = getAndExpectDebugElementByCss(panelHeaderDes[0], 'button.accordion-button', 1, 1); - // Button native elements to click on - const btnEl = btnDes[0].nativeElement; - - // Panel body does not close - getAndExpectDebugElementByCss( - compDe, - 'div#awg-graph-visualizer-select-results > div.accordion-body', - 1, - 1, - 'open' - ); - - // Click header button - click(btnEl as HTMLElement); - detectChangesOnPush(fixture); - // Panel body does not close again - getAndExpectDebugElementByCss( - compDe, - 'div#awg-graph-visualizer-select-results > div.accordion-body', - 1, - 1, - 'open' - ); + expectToBe(component.isAccordionItemDisabled(), true); }); + }); - it('... should contain panel body with TwelveToneSpinnerComponent (stubbed) while loading', () => { - // Mock empty observable - component.queryResult$ = EMPTY; - detectChangesOnPush(fixture); - - // Panel body - const bodyDes = getAndExpectDebugElementByCss( - compDe, - 'div#awg-graph-visualizer-select-results > div.accordion-body', - 1, - 1 - ); + describe('#isQueryResultNotEmpty()', () => { + it('... should have a method `isQueryResultNotEmpty`', () => { + expect(component.isQueryResultNotEmpty).toBeDefined(); + }); - getAndExpectDebugElementByDirective(bodyDes[0], TwelveToneSpinnerStubComponent, 1, 1); + it('... should be triggered from ngbAccordionBody', () => { + expectSpyCall(isQueryResultNotEmptySpy, 2, expectedQueryResult[0]); }); - it('... should contain panel body with SparqlNoResultsStubComponent (stubbed) if no results are available', () => { - // Mock empty response - component.queryResult$ = observableOf({ head: { vars: [] }, body: { bindings: [] } }); - detectChangesOnPush(fixture); + it('... should be triggered by change of queryResult', () => { + expectSpyCall(isQueryResultNotEmptySpy, 2, expectedQueryResult); - // Panel body - const bodyDes = getAndExpectDebugElementByCss( - compDe, - 'div#awg-graph-visualizer-select-results > div.accordion-body', - 1, - 1 - ); + // Mock another queryResult + const queryResult = { + head: { vars: ['AnotherTestHeader'] }, + body: { bindings: [{ testKey: 'AnotherTestValue' }] }, + }; + component.queryResult$ = observableOf(queryResult); + + detectChangesOnPush(fixture); - // SparqlNoResultsStubComponent - getAndExpectDebugElementByDirective(bodyDes[0], SparqlNoResultsStubComponent, 1, 1); + expectSpyCall(isQueryResultNotEmptySpy, 3, queryResult); }); - it('... should contain panel body with SparqlTableComponent (stubbed) if results are available', () => { - // Panel body - const bodyDes = getAndExpectDebugElementByCss( - compDe, - 'div#awg-graph-visualizer-select-results > div.accordion-body', - 1, - 1 - ); - - // SparqlTable - getAndExpectDebugElementByDirective(bodyDes[0], SparqlTableStubComponent, 1, 1); + describe('... should return false if ...', () => { + it('... queryResult.head is undefined', () => { + expectSpyCall(isQueryResultNotEmptySpy, 2, expectedQueryResult); + + // Mock undefined response + const undefinedQueryResult = { head: undefined, body: { bindings: [{ test: 'Test' }] } }; + component.queryResult$ = observableOf(undefinedQueryResult); + detectChangesOnPush(fixture); + + expectSpyCall(isQueryResultNotEmptySpy, 3, undefinedQueryResult); + expectToBe(component.isQueryResultNotEmpty(undefinedQueryResult), false); + }); + + it('... queryResult.body is undefined', () => { + expectSpyCall(isQueryResultNotEmptySpy, 2, expectedQueryResult); + + // Mock undefined response + const undefinedQueryResult = { head: { vars: ['Test'] }, body: undefined }; + component.queryResult$ = observableOf(undefinedQueryResult); + detectChangesOnPush(fixture); + + expectSpyCall(isQueryResultNotEmptySpy, 3, undefinedQueryResult); + expectToBe(component.isQueryResultNotEmpty(undefinedQueryResult), false); + }); + + it('... queryResult.head and queryResult.body are undefined', () => { + expectSpyCall(isQueryResultNotEmptySpy, 2, expectedQueryResult); + + // Mock undefined response + const undefinedQueryResult = { head: undefined, body: undefined }; + component.queryResult$ = observableOf(undefinedQueryResult); + detectChangesOnPush(fixture); + + expectSpyCall(isQueryResultNotEmptySpy, 3, undefinedQueryResult); + expectToBe(component.isQueryResultNotEmpty(undefinedQueryResult), false); + }); + + it('... queryResult.head.vars is empty array', () => { + expectSpyCall(isQueryResultNotEmptySpy, 2, expectedQueryResult); + + // Mock empty response + const emptyQueryResult = { head: { vars: [] }, body: { bindings: [{ testKey: 'TestValue' }] } }; + component.queryResult$ = observableOf(emptyQueryResult); + + detectChangesOnPush(fixture); + + expectSpyCall(isQueryResultNotEmptySpy, 3, emptyQueryResult); + expectToBe(component.isQueryResultNotEmpty(emptyQueryResult), false); + }); + + it('... queryResult.body.bindings is empty array', () => { + expectSpyCall(isQueryResultNotEmptySpy, 2, expectedQueryResult); + + // Mock empty response + const emptyQueryResult = { head: { vars: ['TestHeader'] }, body: { bindings: [] } }; + component.queryResult$ = observableOf(emptyQueryResult); + + detectChangesOnPush(fixture); + + expectSpyCall(isQueryResultNotEmptySpy, 3, emptyQueryResult); + expectToBe(component.isQueryResultNotEmpty(emptyQueryResult), false); + }); + + it('... queryResult.head.vars & queryResult.body.bindings are empty arrays', () => { + expectSpyCall(isQueryResultNotEmptySpy, 2, expectedQueryResult); + + // Mock empty response + const emptyQueryResult = { head: { vars: [] }, body: { bindings: [] } }; + component.queryResult$ = observableOf(emptyQueryResult); + + detectChangesOnPush(fixture); + + expectSpyCall(isQueryResultNotEmptySpy, 3, emptyQueryResult); + expectToBe(component.isQueryResultNotEmpty(emptyQueryResult), false); + }); }); - it('... should pass down `queryResult` and `queryTime` to sparqlTable component', () => { - const sparqlTableDes = getAndExpectDebugElementByDirective(compDe, SparqlTableStubComponent, 1, 1); - const sparqlTableCmp = sparqlTableDes[0].injector.get( - SparqlTableStubComponent - ) as SparqlTableStubComponent; + it('... should return true if queryResult.head.vars && queryResult.body.bindings is not empty (length > 0)', () => { + expectSpyCall(isQueryResultNotEmptySpy, 2, expectedQueryResult); - expect(sparqlTableCmp.queryResult).toBeDefined(); - expect(sparqlTableCmp.queryResult) - .withContext(`should equal ${expectedQueryResult}`) - .toEqual(expectedQueryResult); + // Mock non-empty response + const nonEmptyQueryResult = { + head: { vars: ['AnotherTestHeader'] }, + body: { bindings: [{ testKey: 'AnotherTestValue' }] }, + }; + component.queryResult$ = observableOf(nonEmptyQueryResult); + detectChangesOnPush(fixture); - expect(sparqlTableCmp.queryTime).toBeDefined(); - expect(sparqlTableCmp.queryTime) - .withContext(`should have data: ${expectedQueryTime}`) - .toEqual(expectedQueryTime); + expectSpyCall(isQueryResultNotEmptySpy, 3, nonEmptyQueryResult); + expectToBe(component.isQueryResultNotEmpty(nonEmptyQueryResult), true); }); }); @@ -392,192 +839,5 @@ describe('SelectResultsComponent (DONE)', () => { expectSpyCall(emitClickedTableRequestSpy, 1, expectedUri); }); }); - - describe('#isNotEmpty()', () => { - it('... should have a method `isNotEmpty`', () => { - expect(component.isNotEmpty).toBeDefined(); - }); - - it('... should not do anything if no queryResult.head is provided', () => { - expectSpyCall(isNotEmptySpy, 1, expectedQueryResult); - - // Mock empty response - const emptyQueryResult = { head: undefined, body: { bindings: [{ test: 'Test' }] } }; - component.queryResult$ = observableOf(emptyQueryResult); - detectChangesOnPush(fixture); - - expectSpyCall(isNotEmptySpy, 2, emptyQueryResult); - // SparqlNoResultsStubComponent - getAndExpectDebugElementByDirective(compDe, SparqlNoResultsStubComponent, 1, 1); - }); - - it('... should not do anything if no queryResult.body is provided', () => { - expectSpyCall(isNotEmptySpy, 1, expectedQueryResult); - - // Mock empty response - const emptyQueryResult = { head: { vars: ['Test'] }, body: undefined }; - component.queryResult$ = observableOf(emptyQueryResult); - detectChangesOnPush(fixture); - - expectSpyCall(isNotEmptySpy, 2, emptyQueryResult); - // SparqlNoResultsStubComponent - getAndExpectDebugElementByDirective(compDe, SparqlNoResultsStubComponent, 1, 1); - }); - - it('... should return false if queryResult.head.vars length = 0', () => { - expectSpyCall(isNotEmptySpy, 1, expectedQueryResult); - - // Mock empty response - const emptyQueryResult = { head: { vars: [] }, body: { bindings: [{ testKey: 'TestValue' }] } }; - component.queryResult$ = observableOf(emptyQueryResult); - detectChangesOnPush(fixture); - - expectSpyCall(isNotEmptySpy, 2, emptyQueryResult); - expect(component.isNotEmpty(emptyQueryResult)).toBeFalse(); - }); - - it('... should return false if queryResult.body.bindings length = 0', () => { - expectSpyCall(isNotEmptySpy, 1, expectedQueryResult); - - // Mock empty response - const emptyQueryResult = { head: { vars: ['TestHeader'] }, body: { bindings: [] } }; - component.queryResult$ = observableOf(emptyQueryResult); - detectChangesOnPush(fixture); - - expectSpyCall(isNotEmptySpy, 2, emptyQueryResult); - expect(component.isNotEmpty(emptyQueryResult)).toBeFalse(); - }); - - it('... should return false if queryResult.head.vars & queryResult.body.bindings length = 0', () => { - expectSpyCall(isNotEmptySpy, 1, expectedQueryResult); - - // Mock empty response - const emptyQueryResult = { head: { vars: [] }, body: { bindings: [] } }; - component.queryResult$ = observableOf(emptyQueryResult); - detectChangesOnPush(fixture); - - expectSpyCall(isNotEmptySpy, 2, emptyQueryResult); - expect(component.isNotEmpty(emptyQueryResult)).toBeFalse(); - }); - - it('... should return true if queryResult.head.vars & queryResult.body.bindings length > 0', () => { - component.queryResult$ = expectedQueryResult$; - expectSpyCall(isNotEmptySpy, 1, expectedQueryResult); - - expect(component.isNotEmpty(expectedQueryResult)).toBeTrue(); - expectSpyCall(isNotEmptySpy, 2, expectedQueryResult); - - // Mock another non-empty response - const queryResult = { head: { vars: ['TestHeader'] }, body: { bindings: [{ testKey: 'TestValue' }] } }; - component.queryResult$ = observableOf(queryResult); - detectChangesOnPush(fixture); - - expectSpyCall(isNotEmptySpy, 3, queryResult); - expect(component.isNotEmpty(queryResult)).toBeTrue(); - }); - }); - - describe('#preventPanelCollapseOnFullscreen()', () => { - it('... should have a method `preventPanelCollapseOnFullscreen`', () => { - expect(component.preventPanelCollapseOnFullscreen).toBeDefined(); - }); - - it('... should trigger on event from ngb-accordion', () => { - const accordionDes = getAndExpectDebugElementByDirective(compDe, NgbAccordion, 1, 1); - const accordionCmp = accordionDes[0].injector.get(NgbAccordion) as NgbAccordion; - - const panelChangeEvent: NgbPanelChangeEvent = { - panelId: 'panelChangeId', - nextState: true, - preventDefault: () => { - // Intentional empty test override - }, - }; - - // Emit change event from accordion - accordionCmp.panelChange.emit(panelChangeEvent); - - expectSpyCall(preventPanelCollapseOnFullscreenSpy, 1, panelChangeEvent); - }); - - it('... should not do anything if no $event is provided', () => { - const accordionDes = getAndExpectDebugElementByDirective(compDe, NgbAccordion, 1, 1); - const accordionCmp = accordionDes[0].injector.get(NgbAccordion) as NgbAccordion; - - // Emit undefined change event from accordion - accordionCmp.panelChange.emit(undefined); - - expectSpyCall(preventPanelCollapseOnFullscreenSpy, 1, undefined); - }); - - it('... should trigger $event.preventDefault() if isFullscreen == true && $event.nextState == false', () => { - const accordionDes = getAndExpectDebugElementByDirective(compDe, NgbAccordion, 1, 1); - const accordionCmp = accordionDes[0].injector.get(NgbAccordion) as NgbAccordion; - - const panelChangeEvent: NgbPanelChangeEvent = { - panelId: 'panelChangeId', - nextState: false, - preventDefault: () => { - // Intentional empty test override - }, - }; - const preventDefaultSpy: Spy = spyOn(panelChangeEvent, 'preventDefault').and.callThrough(); - - // Set fullscreen mode - component.isFullscreen = true; - - // Emit change event from accordion - accordionCmp.panelChange.emit(panelChangeEvent); - - expectSpyCall(preventPanelCollapseOnFullscreenSpy, 1, panelChangeEvent); - expectSpyCall(preventDefaultSpy, 1, undefined); - }); - - it('... should not trigger $event.preventDefault() if $event.nextState == true', () => { - const accordionDes = getAndExpectDebugElementByDirective(compDe, NgbAccordion, 1, 1); - const accordionCmp = accordionDes[0].injector.get(NgbAccordion) as NgbAccordion; - - const panelChangeEvent: NgbPanelChangeEvent = { - panelId: 'panelChangeId', - nextState: true, - preventDefault: () => { - // Intentional empty test override - }, - }; - const preventDefaultSpy: Spy = spyOn(panelChangeEvent, 'preventDefault').and.callThrough(); - - // Set fullscreen mode - component.isFullscreen = true; - - // Emit change event from accordion - accordionCmp.panelChange.emit(panelChangeEvent); - - expectSpyCall(preventPanelCollapseOnFullscreenSpy, 1, panelChangeEvent); - expectSpyCall(preventDefaultSpy, 0, undefined); - }); - - it('... should not trigger $event.preventDefault() if isFullscreen == false', () => { - const accordionDes = getAndExpectDebugElementByDirective(compDe, NgbAccordion, 1, 1); - const accordionCmp = accordionDes[0].injector.get(NgbAccordion) as NgbAccordion; - - const panelChangeEvent: NgbPanelChangeEvent = { - panelId: 'panelChangeId', - nextState: false, - preventDefault: () => { - // Intentional empty test override - }, - }; - const preventDefaultSpy: Spy = spyOn(panelChangeEvent, 'preventDefault').and.callThrough(); - - // Unset fullscreen mode - component.isFullscreen = false; - - // Emit change event from accordion - accordionCmp.panelChange.emit(panelChangeEvent); - - expectSpyCall(preventPanelCollapseOnFullscreenSpy, 1, panelChangeEvent); - expectSpyCall(preventDefaultSpy, 0, undefined); - }); - }); }); }); diff --git a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/select-results/select-results.component.ts b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/select-results/select-results.component.ts index f8ec3e952c..8b9eccfd9f 100644 --- a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/select-results/select-results.component.ts +++ b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/select-results/select-results.component.ts @@ -1,8 +1,8 @@ import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; -import { NgbPanelChangeEvent } from '@ng-bootstrap/ng-bootstrap'; import { Observable } from 'rxjs'; +import { UtilityService } from '@awg-app/core/services'; import { QueryResult } from '../models'; /** @@ -51,19 +51,38 @@ export class SelectResultsComponent { clickedTableRequest: EventEmitter = new EventEmitter(); /** - * Public method: isNotEmpty. + * Constructor of the SelectResultsComponent. + * + * It declares a public instance of the UtilityService. + * + * @param {UtilityService} utils Instance of the UtilityService. + */ + constructor(public utils: UtilityService) {} + + /** + * Public method: isAccordionItemDisabled. + * + * It returns a boolean flag if the accordion item should be disabled. + * It returns true if fullscreenMode is set, otherwise false. + * + * @returns {boolean} The boolean value of the comparison. + */ + isAccordionItemDisabled(): boolean { + return this.isFullscreen ? true : false; + } + + /** + * Public method: isQueryResultNotEmpty. * * It checks if a given queryResult is not empty. * - * @param {string} queryResult The given queryResult. + * @param {QueryResult} queryResult The given queryResult. * * @returns {boolean} The boolean value of the comparison result. */ - isNotEmpty(queryResult: QueryResult): boolean { - if (!queryResult.head || !queryResult.body) { - return undefined; - } - return queryResult.head.vars.length > 0 && queryResult.body.bindings.length > 0; + isQueryResultNotEmpty(queryResult: QueryResult): boolean { + const { head, body } = queryResult; + return this.utils.isNotEmptyArray(head?.vars) && this.utils.isNotEmptyArray(body?.bindings); } /** @@ -81,20 +100,4 @@ export class SelectResultsComponent { } this.clickedTableRequest.emit(uri); } - - /** - * Public method: preventPanelCollapseOnFullscreen. - * - * It prevents the given panel event from being collapsed in fullscreen mode. - * - * @returns {void} Prevents the panel collapse. - */ - preventPanelCollapseOnFullscreen($event: NgbPanelChangeEvent): void { - if (!$event) { - return; - } - if (this.isFullscreen && $event.nextState === false) { - $event.preventDefault(); - } - } } diff --git a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/sparql-editor/sparql-editor.component.html b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/sparql-editor/sparql-editor.component.html index 551d6cc23a..5394cab335 100644 --- a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/sparql-editor/sparql-editor.component.html +++ b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/sparql-editor/sparql-editor.component.html @@ -1,23 +1,21 @@ - - - -
    - - - +
    +
    +
    + + + @if (!isFullscreen) { + } - -
    + + @if (isExampleQueriesEnabled()) { +
    @@ -31,41 +29,45 @@
    - - + @for (q of queryList; track q) { + @if (q.queryType === 'construct') { {{ q.queryLabel }} - - + } + } - - + @for (q of queryList; track q) { + @if (q.queryType === 'select') { {{ q.queryLabel }} - - + } + }
    -
    - - - - + } +
    +
    +
    + + + - -
    - - - + +
    + + + +
    +
    -
    - - +
    +
    +
    diff --git a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/sparql-editor/sparql-editor.component.scss b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/sparql-editor/sparql-editor.component.scss index e69de29bb2..d1ba38e0ba 100644 --- a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/sparql-editor/sparql-editor.component.scss +++ b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/sparql-editor/sparql-editor.component.scss @@ -0,0 +1,13 @@ +.custom-header { + &::after { + content: none; + } + + &.accordion-button { + padding: 0.8rem !important; + + > #awg-graph-visualizer-sparql-query-toggle { + text-decoration: none; + } + } +} diff --git a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/sparql-editor/sparql-editor.component.spec.ts b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/sparql-editor/sparql-editor.component.spec.ts index 12fdff1f43..f7a671549c 100644 --- a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/sparql-editor/sparql-editor.component.spec.ts +++ b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/sparql-editor/sparql-editor.component.spec.ts @@ -1,14 +1,13 @@ import { Component, DebugElement, EventEmitter, Input, NgModule, Output, SimpleChange } from '@angular/core'; -import { ComponentFixture, fakeAsync, TestBed, tick, waitForAsync } from '@angular/core/testing'; +import { ComponentFixture, TestBed, fakeAsync, tick, waitForAsync } from '@angular/core/testing'; import { sparql } from '@codemirror/legacy-modes/mode/sparql'; import { - NgbAccordion, + NgbAccordionDirective, NgbAccordionModule, NgbConfig, NgbDropdown, NgbDropdownModule, - NgbPanelChangeEvent, } from '@ng-bootstrap/ng-bootstrap'; import { EditorView } from 'codemirror'; import Spy = jasmine.Spy; @@ -17,6 +16,9 @@ import { click } from '@testing/click-helper'; import { detectChangesOnPush } from '@testing/detect-changes-on-push-helper'; import { expectSpyCall, + expectToBe, + expectToContain, + expectToEqual, getAndExpectDebugElementByCss, getAndExpectDebugElementByDirective, } from '@testing/expect-helper'; @@ -65,7 +67,8 @@ describe('SparqlEditorComponent (DONE)', () => { let onQueryListChangeSpy: Spy; let onViewChangeSpy: Spy; let performQuerySpy: Spy; - let preventPanelCollapseOnFullscreenSpy: Spy; + let isAccordionItemDisabledSpy: Spy; + let isAccordionItemCollapsedSpy: Spy; let resetQuerySpy: Spy; let setViewTypeSpy: Spy; let switchQueryTypeSpy: Spy; @@ -85,7 +88,7 @@ describe('SparqlEditorComponent (DONE)', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ - imports: [NgbAnimationConfigModule, NgbAccordion, NgbDropdown], + imports: [NgbAnimationConfigModule, NgbAccordionDirective, NgbDropdown], declarations: [SparqlEditorComponent, CodeMirrorStubComponent, ViewHandleButtongGroupStubComponent], }).compileComponents(); })); @@ -96,6 +99,9 @@ describe('SparqlEditorComponent (DONE)', () => { compDe = fixture.debugElement; // Test data + expectedIsFullscreen = false; + expectedCmSparqlMode = sparql; + expectedConstructQuery1 = new GraphSparqlQuery(); expectedConstructQuery1.queryType = 'construct'; expectedConstructQuery1.queryLabel = 'Test Query 3'; @@ -123,10 +129,6 @@ describe('SparqlEditorComponent (DONE)', () => { expectedSelectQuery2, ]; - expectedIsFullscreen = false; - - expectedCmSparqlMode = sparql; - expectedViewHandles = [ new ViewHandle('Graph view', ViewHandleTypes.GRAPH, component.faDiagramProject), new ViewHandle('Table view', ViewHandleTypes.TABLE, component.faTable), @@ -140,7 +142,8 @@ describe('SparqlEditorComponent (DONE)', () => { onQueryListChangeSpy = spyOn(component, 'onQueryListChange').and.callThrough(); onViewChangeSpy = spyOn(component, 'onViewChange').and.callThrough(); performQuerySpy = spyOn(component, 'performQuery').and.callThrough(); - preventPanelCollapseOnFullscreenSpy = spyOn(component, 'preventPanelCollapseOnFullscreen').and.callThrough(); + isAccordionItemCollapsedSpy = spyOn(component, 'isAccordionItemCollapsed').and.callThrough(); + isAccordionItemDisabledSpy = spyOn(component, 'isAccordionItemDisabled').and.callThrough(); resetQuerySpy = spyOn(component, 'resetQuery').and.callThrough(); setViewTypeSpy = spyOn(component, 'setViewType').and.callThrough(); switchQueryTypeSpy = spyOn(component, 'switchQueryType').and.callThrough(); @@ -168,33 +171,21 @@ describe('SparqlEditorComponent (DONE)', () => { }); it('... should have cmSparqlMode', () => { - expect(component.cmSparqlMode).toBeDefined(); - expect(component.cmSparqlMode) - .withContext(`should equal ${expectedCmSparqlMode}`) - .toEqual(expectedCmSparqlMode); + expectToEqual(component.cmSparqlMode, expectedCmSparqlMode); }); it('... should have selectedViewType', () => { - expect(component.selectedViewType).toBeDefined(); - expect(component.selectedViewType) - .withContext(`should equal ${ViewHandleTypes.GRAPH}`) - .toEqual(ViewHandleTypes.GRAPH); + expectToEqual(component.selectedViewType, ViewHandleTypes.GRAPH); }); it('... should have viewHandles', () => { - expect(component.viewHandles).toBeDefined(); - expect(component.viewHandles) - .withContext(`should equal ${expectedViewHandles}`) - .toEqual(expectedViewHandles); + expectToEqual(component.viewHandles, expectedViewHandles); }); describe('VIEW', () => { - it('... should contain one ngb-accordion without panel (div.accordion-item) yet', () => { - // Ngb-accordion debug element - const accordionDes = getAndExpectDebugElementByCss(compDe, 'ngb-accordion', 1, 1); - - // Panel - getAndExpectDebugElementByCss(accordionDes[0], 'div.accordion-item', 0, 0, 'yet'); + it('... should contain no ngbAccordion yet', () => { + // NgbAccordion debug element + getAndExpectDebugElementByCss(compDe, 'div.accordion', 0, 0); }); }); }); @@ -211,54 +202,72 @@ describe('SparqlEditorComponent (DONE)', () => { }); it('... should have `queryList` input', () => { - expect(component.queryList).toBeDefined(); - expect(component.queryList).withContext(`should equal ${expectedQueryList}`).toEqual(expectedQueryList); + expectToEqual(component.queryList, expectedQueryList); }); it('... should have `query` input', () => { - expect(component.query).toBeDefined(); - expect(component.query) - .withContext(`should equal ${expectedConstructQuery1}`) - .toEqual(expectedConstructQuery1); + expectToEqual(component.query, expectedConstructQuery1); }); it('... should have `isFullScreen` input', () => { - expect(component.isFullscreen).toBeDefined(); - expect(component.isFullscreen) - .withContext(`should equal ${expectedIsFullscreen}`) - .toBe(expectedIsFullscreen); + expectToBe(component.isFullscreen, expectedIsFullscreen); }); describe('VIEW', () => { + it('... should contain one div.accordion', () => { + // NgbAccordion debug element + getAndExpectDebugElementByCss(compDe, 'div.accordion', 1, 1); + }); + describe('not in fullscreen mode', () => { describe('with closed panel', () => { - it('... should contain one ngb-accordion with panel (div.accordion-item) header and collapsed body', () => { - // Ngb-accordion debug element - const accordionDes = getAndExpectDebugElementByCss(compDe, 'ngb-accordion', 1, 1); - - // Panel (div.card) - const panelDes = getAndExpectDebugElementByCss(accordionDes[0], 'div.accordion-item', 1, 1); // Panel (div.card) - // Header - getAndExpectDebugElementByCss( + it('... should contain one div.accordion with panel (div.accordion-item) header and collapsed body', () => { + // NgbAccordion debug element + const accordionDes = getAndExpectDebugElementByCss(compDe, 'div.accordion', 1, 1); + + // Panel (div.accordion-item) + const panelDes = getAndExpectDebugElementByCss( + accordionDes[0], + 'div#awg-graph-visualizer-sparql-query.accordion-item', + 1, + 1 + ); + // Header (div.accordion-header) + const panelHeaderDes = getAndExpectDebugElementByCss( panelDes[0], - 'div#awg-graph-visualizer-sparql-query-header.accordion-header', + 'div#awg-graph-visualizer-sparql-query > div.accordion-header', 1, 1 - ); // Panel (div.card) - // No body - getAndExpectDebugElementByCss( + ); + const panelHeaderEl = panelHeaderDes[0].nativeElement; + + expectToContain(panelHeaderEl.classList, 'collapsed'); + + // Body (div.accordion-collapse) + const panelBodyDes = getAndExpectDebugElementByCss( panelDes[0], - 'div#awg-graph-visualizer-sparql-query > div.accordion-body', - 0, - 0 + 'div#awg-graph-visualizer-sparql-query > div.accordion-collapse', + 1, + 1 ); + const panelBodyEl = panelBodyDes[0].nativeElement; + + expect(panelBodyEl.classList).not.toContain('show'); }); it('... should display panel header button', () => { - // Panel header button - const btnDes = getAndExpectDebugElementByCss( + // Header debug elements + const panelHeaderDes = getAndExpectDebugElementByCss( compDe, - 'div#awg-graph-visualizer-sparql-query-header > div.accordion-button > button.btn-link', + 'div#awg-graph-visualizer-sparql-query > div.accordion-header', + 1, + 1 + ); + + // Custom panel header button + const btnDes = getAndExpectDebugElementByCss( + panelHeaderDes[0], + 'button#awg-graph-visualizer-sparql-query-toggle', 1, 1 ); @@ -266,15 +275,14 @@ describe('SparqlEditorComponent (DONE)', () => { const btnEl = btnDes[0].nativeElement; // Check button content - expect(btnEl.textContent).toBeTruthy(); - expect(btnEl.textContent).withContext('should be SPARQL').toContain('SPARQL'); + expectToBe(btnEl.textContent, 'SPARQL'); }); - it('... should toggle panel body by click on panel header button', async () => { + it('... should toggle panel body on click', () => { // Header debug elements const panelHeaderDes = getAndExpectDebugElementByCss( compDe, - 'div#awg-graph-visualizer-sparql-query-header.accordion-header', + 'div#awg-graph-visualizer-sparql-query > div.accordion-header', 1, 1 ); @@ -282,7 +290,7 @@ describe('SparqlEditorComponent (DONE)', () => { // Button debug elements const btnDes = getAndExpectDebugElementByCss( panelHeaderDes[0], - 'div.accordion-button > button.btn-link', + 'button#awg-graph-visualizer-sparql-query-toggle', 1, 1 ); @@ -290,39 +298,45 @@ describe('SparqlEditorComponent (DONE)', () => { const btnEl = btnDes[0].nativeElement; // Panel body is closed - getAndExpectDebugElementByCss( + let panelBodyDes = getAndExpectDebugElementByCss( compDe, - 'div#awg-graph-visualizer-sparql-query > div.accordion-body', - 0, - 0, - 'collapsed' + 'div#awg-graph-visualizer-sparql-query > div.accordion-collapse', + 1, + 1 ); + let panelBodyEl = panelBodyDes[0].nativeElement; + + expect(panelBodyEl.classList).not.toContain('show'); // Click header button click(btnEl as HTMLElement); - await detectChangesOnPush(fixture); + detectChangesOnPush(fixture); // Panel is open - getAndExpectDebugElementByCss( + panelBodyDes = getAndExpectDebugElementByCss( compDe, - 'div#awg-graph-visualizer-sparql-query > div.accordion-body', - 1, + 'div#awg-graph-visualizer-sparql-query > div.accordion-collapse', 1, - 'open' + 1 ); + panelBodyEl = panelBodyDes[0].nativeElement; + + expectToContain(panelBodyEl.classList, 'show'); // Click header button click(btnEl as HTMLElement); - await detectChangesOnPush(fixture); + detectChangesOnPush(fixture); // Panel body is closed again - getAndExpectDebugElementByCss( + panelBodyDes = getAndExpectDebugElementByCss( compDe, - 'div#awg-graph-visualizer-sparql-query > div.accordion-body', - 0, - 0, - 'collapsed' + 'div#awg-graph-visualizer-sparql-query > div.accordion-collapse', + 1, + 1 ); + panelBodyEl = panelBodyDes[0].nativeElement; + + expect(panelBodyEl.classList).not.toContain('show'); }); describe('View handle button group', () => { @@ -330,7 +344,7 @@ describe('SparqlEditorComponent (DONE)', () => { // Header debug elements const panelHeaderDes = getAndExpectDebugElementByCss( compDe, - 'div#awg-graph-visualizer-sparql-query-header.accordion-header', + 'div#awg-graph-visualizer-sparql-query > div.accordion-header', 1, 1 ); @@ -347,7 +361,7 @@ describe('SparqlEditorComponent (DONE)', () => { // Header debug elements const panelHeaderDes = getAndExpectDebugElementByCss( compDe, - 'div#awg-graph-visualizer-sparql-query-header.accordion-header', + 'div#awg-graph-visualizer-sparql-query > div.accordion-header', 1, 1 ); @@ -362,21 +376,20 @@ describe('SparqlEditorComponent (DONE)', () => { ViewHandleButtongGroupStubComponent ) as ViewHandleButtongGroupStubComponent; - expect(viewHandleButtongGroupCmp.selectedViewType).toBeTruthy(); - expect(viewHandleButtongGroupCmp.selectedViewType).toBe(ViewHandleTypes.GRAPH); + expectToBe(viewHandleButtongGroupCmp.selectedViewType, ViewHandleTypes.GRAPH); }); }); describe('Example query button group', () => { - it('... should contain an example query btn-group in panel header if isExampleQueriesEnabled = true', async () => { + it('... should contain an example query btn-group in panel header if isExampleQueriesEnabled = true', () => { isExampleQueriesEnabledSpy.and.returnValue(true); - await detectChangesOnPush(fixture); + detectChangesOnPush(fixture); // Header debug elements const panelHeaderDes = getAndExpectDebugElementByCss( compDe, - 'div#awg-graph-visualizer-sparql-query-header.accordion-header', + 'div#awg-graph-visualizer-sparql-query > div.accordion-header', 1, 1 ); @@ -384,21 +397,21 @@ describe('SparqlEditorComponent (DONE)', () => { // Panel header div.awg-example-query-btn-group getAndExpectDebugElementByCss( panelHeaderDes[0], - 'div.accordion-button > div.awg-example-query-btn-group', + 'div.accordion-header > div.awg-example-query-btn-group', 1, 1 ); }); - it('... should not contain an example query btn-group in panel header if isExampleQueriesEnabled = false', async () => { + it('... should not contain an example query btn-group in panel header if isExampleQueriesEnabled = false', () => { isExampleQueriesEnabledSpy.and.returnValue(false); - await detectChangesOnPush(fixture); + detectChangesOnPush(fixture); // Header debug elements const panelHeaderDes = getAndExpectDebugElementByCss( compDe, - 'div#awg-graph-visualizer-sparql-query-header.accordion-header', + 'div#awg-graph-visualizer-sparql-query > div.accordion-header', 1, 1 ); @@ -406,7 +419,7 @@ describe('SparqlEditorComponent (DONE)', () => { // Panel header div.awg-example-query-btn-group getAndExpectDebugElementByCss( panelHeaderDes[0], - 'div.accordion-button > div.awg-example-query-btn-group', + 'div.accordion-header > div.awg-example-query-btn-group', 0, 0 ); @@ -416,38 +429,36 @@ describe('SparqlEditorComponent (DONE)', () => { // Header debug elements const panelHeaderDes = getAndExpectDebugElementByCss( compDe, - 'div#awg-graph-visualizer-sparql-query-header.accordion-header', + 'div#awg-graph-visualizer-sparql-query > div.accordion-header', 1, 1 ); const btnDes = getAndExpectDebugElementByCss( panelHeaderDes[0], - 'div.accordion-button > div.awg-example-query-btn-group > button.btn', + 'div.accordion-header > div.awg-example-query-btn-group > button.btn', 1, 1 ); const btnEl = btnDes[0].nativeElement; expect(btnEl.disabled).toBeTruthy(); - expect(btnEl.getAttribute('aria-disabled')).toBe('true'); - - expect(btnEl.textContent).toBeTruthy(); - expect(btnEl.textContent).toContain('Beispielabfragen'); + expectToBe(btnEl.getAttribute('aria-disabled'), 'true'); + expectToBe(btnEl.textContent.trim(), 'Beispielabfragen'); }); it('... should contain another btn-group dropdown in example query btn-group', () => { // Header debug elements const panelHeaderDes = getAndExpectDebugElementByCss( compDe, - 'div#awg-graph-visualizer-sparql-query-header.accordion-header', + 'div#awg-graph-visualizer-sparql-query > div.accordion-header', 1, 1 ); const outerButtonGroupDes = getAndExpectDebugElementByCss( panelHeaderDes[0], - 'div.accordion-button > div.awg-example-query-btn-group', + 'div.accordion-header > div.awg-example-query-btn-group', 1, 1 ); @@ -459,14 +470,14 @@ describe('SparqlEditorComponent (DONE)', () => { // Header debug elements const panelHeaderDes = getAndExpectDebugElementByCss( compDe, - 'div#awg-graph-visualizer-sparql-query-header.accordion-header', + 'div#awg-graph-visualizer-sparql-query > div.accordion-header', 1, 1 ); const dropdownButtonGroupDes = getAndExpectDebugElementByCss( panelHeaderDes[0], - 'div.accordion-button > div.awg-example-query-btn-group > div.btn-group.dropdown', + 'div.accordion-header > div.awg-example-query-btn-group > div.btn-group.dropdown', 1, 1 ); @@ -484,14 +495,14 @@ describe('SparqlEditorComponent (DONE)', () => { // Header debug elements const panelHeaderDes = getAndExpectDebugElementByCss( compDe, - 'div#awg-graph-visualizer-sparql-query-header.accordion-header', + 'div#awg-graph-visualizer-sparql-query > div.accordion-header', 1, 1 ); const dropdownButtonGroupDes = getAndExpectDebugElementByCss( panelHeaderDes[0], - 'div.accordion-button > div.awg-example-query-btn-group > div.btn-group.dropdown', + 'div.accordion-header > div.awg-example-query-btn-group > div.btn-group.dropdown', 1, 1 ); @@ -514,7 +525,7 @@ describe('SparqlEditorComponent (DONE)', () => { it('... should display label on dropdown items in example query btn-group', () => { const dropdownButtonGroupDes = getAndExpectDebugElementByCss( compDe, - 'div.accordion-button > div.awg-example-query-btn-group > div.btn-group.dropdown', + 'div.accordion-header > div.awg-example-query-btn-group > div.btn-group.dropdown', 1, 1 ); @@ -526,25 +537,16 @@ describe('SparqlEditorComponent (DONE)', () => { expectedQueryList.length ); - expect(itemDes.length) - .withContext(`should be ${expectedQueryList.length}`) - .toBe(expectedQueryList.length); - - const itemEl0 = itemDes[0].nativeElement; - const itemEl1 = itemDes[1].nativeElement; + expectToBe(itemDes.length, expectedQueryList.length); - expect(itemEl0.textContent).toBeTruthy(); - expect(itemEl0.textContent) - .withContext(`should contain ${expectedQueryList[0].queryLabel}`) - .toContain(expectedQueryList[0].queryLabel); + itemDes.forEach((itemDe: DebugElement, index: number) => { + const itemEl = itemDe.nativeElement; - expect(itemEl1.textContent).toBeTruthy(); - expect(itemEl1.textContent) - .withContext(`should contain ${expectedQueryList[1].queryLabel}`) - .toContain(expectedQueryList[1].queryLabel); + expectToBe(itemEl.textContent, expectedQueryList[index].queryLabel); + }); }); - it('... should disable current query in dropdown items', async () => { + it('... should disable current query in dropdown items', () => { const itemDes = getAndExpectDebugElementByCss( compDe, 'div.dropdown-menu > a.dropdown-item', @@ -552,9 +554,7 @@ describe('SparqlEditorComponent (DONE)', () => { expectedQueryList.length ); - expect(itemDes.length) - .withContext(`should be ${expectedQueryList.length}`) - .toBe(expectedQueryList.length); + expectToBe(itemDes.length, expectedQueryList.length); const itemEl0 = itemDes[0].nativeElement; const itemEl1 = itemDes[1].nativeElement; @@ -563,13 +563,13 @@ describe('SparqlEditorComponent (DONE)', () => { expect(itemEl1).not.toHaveClass('disabled'); component.query = expectedConstructQuery2; - await detectChangesOnPush(fixture); + detectChangesOnPush(fixture); expect(itemEl0).not.toHaveClass('disabled'); expect(itemEl1).toHaveClass('disabled'); }); - it('... should trigger `onQueryListChange()` by click on dropdown item', async () => { + it('... should trigger `onQueryListChange()` by click on dropdown item', () => { const itemDes = getAndExpectDebugElementByCss( compDe, 'div.dropdown-menu > a.dropdown-item', @@ -577,9 +577,7 @@ describe('SparqlEditorComponent (DONE)', () => { expectedQueryList.length ); - expect(itemDes.length) - .withContext(`should be ${expectedQueryList.length}`) - .toBe(expectedQueryList.length); + expectToBe(itemDes.length, expectedQueryList.length); const itemEl0 = itemDes[0].nativeElement; const itemEl1 = itemDes[1].nativeElement; @@ -588,19 +586,19 @@ describe('SparqlEditorComponent (DONE)', () => { // Click on second item (first disabled) click(itemEl1 as HTMLElement); - await detectChangesOnPush(fixture); + detectChangesOnPush(fixture); // Spy call with second query expectSpyCall(onQueryListChangeSpy, 1, expectedConstructQuery2); component.query = expectedConstructQuery2; - await detectChangesOnPush(fixture); + detectChangesOnPush(fixture); expect(itemEl0).not.toHaveClass('disabled'); // Click on first item (second disabled) click(itemEl0 as HTMLElement); - await detectChangesOnPush(fixture); + detectChangesOnPush(fixture); // Spy call with first query expectSpyCall(onQueryListChangeSpy, 2, expectedConstructQuery1); @@ -611,11 +609,11 @@ describe('SparqlEditorComponent (DONE)', () => { describe('with open panel', () => { let bodyDes: DebugElement[]; - beforeEach(async () => { + beforeEach(() => { // Open panel by click on header button const btnDes = getAndExpectDebugElementByCss( compDe, - 'div#awg-graph-visualizer-sparql-query-header.accordion-header > div.accordion-button > button.btn-link', + 'button#awg-graph-visualizer-sparql-query-toggle', 1, 1 ); @@ -623,15 +621,82 @@ describe('SparqlEditorComponent (DONE)', () => { // Click header button click(btnEl as HTMLElement); - await detectChangesOnPush(fixture); + detectChangesOnPush(fixture); + + // Panel body is open + const collapseDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-sparql-query > div.accordion-collapse', + 1, + 1 + ); + const collapseEl = collapseDes[0].nativeElement; + + expectToContain(collapseEl.classList, 'show'); // Panel body - bodyDes = getAndExpectDebugElementByCss( + bodyDes = getAndExpectDebugElementByCss(collapseDes[0], 'div.accordion-body', 1, 1); + }); + + it('... should toggle panel body on click', () => { + // Header debug elements + const panelHeaderDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-sparql-query > div.accordion-header', + 1, + 1 + ); + + // Button debug elements + const btnDes = getAndExpectDebugElementByCss( + panelHeaderDes[0], + 'button#awg-graph-visualizer-sparql-query-toggle', + 1, + 1 + ); + // Button native elements to click on + const btnEl = btnDes[0].nativeElement; + + // Panel body is open + let panelBodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-sparql-query > div.accordion-collapse', + 1, + 1 + ); + let panelBodyEl = panelBodyDes[0].nativeElement; + + expectToContain(panelBodyEl.classList, 'show'); + + // Click header button + click(btnEl as HTMLElement); + detectChangesOnPush(fixture); + + // Panel is closed + panelBodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-sparql-query > div.accordion-collapse', + 1, + 1 + ); + panelBodyEl = panelBodyDes[0].nativeElement; + + expect(panelBodyEl.classList).not.toContain('show'); + + // Click header button + click(btnEl as HTMLElement); + detectChangesOnPush(fixture); + + // Panel body is open again + panelBodyDes = getAndExpectDebugElementByCss( compDe, - 'div#awg-graph-visualizer-sparql-query > div.accordion-body', + 'div#awg-graph-visualizer-sparql-query > div.accordion-collapse', 1, 1 ); + panelBodyEl = panelBodyDes[0].nativeElement; + + expectToContain(panelBodyEl.classList, 'show'); }); it('... should contain CodeMirrorComponent (stubbed) in panel body', () => { @@ -652,17 +717,12 @@ describe('SparqlEditorComponent (DONE)', () => { const btnEl1 = btnDes[1].nativeElement; const btnEl2 = btnDes[2].nativeElement; - expect(btnEl0.textContent).toBeTruthy(); - expect(btnEl0.textContent).withContext(`should contain 'Query'`).toContain('Query'); - - expect(btnEl1.textContent).toBeTruthy(); - expect(btnEl1.textContent).withContext(`should contain 'Reset'`).toContain('Reset'); - - expect(btnEl2.textContent).toBeTruthy(); - expect(btnEl2.textContent).withContext(`should contain 'Clear'`).toContain('Clear'); + expectToBe(btnEl0.textContent, 'Query'); + expectToBe(btnEl1.textContent, 'Reset'); + expectToBe(btnEl2.textContent, 'Clear'); }); - it('... should trigger `performQuery()` by click on Query button', async () => { + it('... should trigger `performQuery()` by click on Query button', () => { const btnDes = getAndExpectDebugElementByCss( bodyDes[0], 'div.awg-graph-visualizer-sparql-query-handle-buttons > button.btn', @@ -671,18 +731,17 @@ describe('SparqlEditorComponent (DONE)', () => { ); const btnEl0 = btnDes[0].nativeElement; - expect(btnEl0.textContent).toBeTruthy(); - expect(btnEl0.textContent).withContext(`should contain 'Query'`).toContain('Query'); + expectToBe(btnEl0.textContent, 'Query'); // Click query button click(btnEl0 as HTMLElement); - await detectChangesOnPush(fixture); + detectChangesOnPush(fixture); expectSpyCall(performQuerySpy, 1); expectSpyCall(resetQuerySpy, 0); }); - it('... should trigger `resetQuery()` by click on Reset button', async () => { + it('... should trigger `resetQuery()` by click on Reset button', () => { const btnDes = getAndExpectDebugElementByCss( bodyDes[0], 'div.awg-graph-visualizer-sparql-query-handle-buttons > button.btn', @@ -691,18 +750,17 @@ describe('SparqlEditorComponent (DONE)', () => { ); const btnEl1 = btnDes[1].nativeElement; - expect(btnEl1.textContent).toBeTruthy(); - expect(btnEl1.textContent).withContext(`should contain 'Reset'`).toContain('Reset'); + expectToBe(btnEl1.textContent, 'Reset'); // Click reset button click(btnEl1 as HTMLElement); - await detectChangesOnPush(fixture); + detectChangesOnPush(fixture); expectSpyCall(performQuerySpy, 0); expectSpyCall(resetQuerySpy, 1); }); - it('... should trigger `onEditorInputChange()` with empty string by click on Clear button', async () => { + it('... should trigger `onEditorInputChange()` with empty string by click on Clear button', () => { const btnDes = getAndExpectDebugElementByCss( bodyDes[0], 'div.awg-graph-visualizer-sparql-query-handle-buttons > button.btn', @@ -711,12 +769,11 @@ describe('SparqlEditorComponent (DONE)', () => { ); const btnEl2 = btnDes[2].nativeElement; - expect(btnEl2.textContent).toBeTruthy(); - expect(btnEl2.textContent).withContext(`should contain 'Clear'`).toContain('Clear'); + expectToBe(btnEl2.textContent, 'Clear'); // Click clear button click(btnEl2 as HTMLElement); - await detectChangesOnPush(fixture); + detectChangesOnPush(fixture); expectSpyCall(onEditorInputChangeSpy, 1, ''); }); @@ -730,7 +787,7 @@ describe('SparqlEditorComponent (DONE)', () => { // Open panel by click on header button const btnDes = getAndExpectDebugElementByCss( compDe, - 'div#awg-graph-visualizer-sparql-query-header.accordion-header > div.accordion-button > button.btn-link', + 'button#awg-graph-visualizer-sparql-query-toggle', 1, 1 ); @@ -743,32 +800,39 @@ describe('SparqlEditorComponent (DONE)', () => { // Panel body bodyDes = getAndExpectDebugElementByCss( compDe, - 'div#awg-graph-visualizer-sparql-query > div.accordion-body', + 'div#awg-graph-visualizer-sparql-query-collapse > div.accordion-body', 1, 1 ); // Set fullscreen mode component.isFullscreen = true; + detectChangesOnPush(fixture); }); - it('... should contain one ngb-accordion with panel (div.accordion-item) header and open body', () => { - // Ngb-accordion debug element - const accordionDes = getAndExpectDebugElementByCss(compDe, 'ngb-accordion', 1, 1); + it('... should contain one div.accordion with panel (div.accordion-item) header and open body', () => { + // NgbAccordion debug element + const accordionDes = getAndExpectDebugElementByCss(compDe, 'div.accordion', 1, 1); - // Panel (div.card) - const panelDes = getAndExpectDebugElementByCss(accordionDes[0], 'div.accordion-item', 1, 1); // Panel (div.card) - // Header + // Panel (div.accordion-item) + const panelDes = getAndExpectDebugElementByCss( + accordionDes[0], + 'div#awg-graph-visualizer-sparql-query.accordion-item', + 1, + 1 + ); + // Header (div.accordion-header) getAndExpectDebugElementByCss( panelDes[0], - 'div#awg-graph-visualizer-sparql-query-header.accordion-header', + 'div#awg-graph-visualizer-sparql-query > div.accordion-header', 1, 1 - ); // Panel (div.card) - // Body open + ); + + // Body open (div.accordion-body) getAndExpectDebugElementByCss( panelDes[0], - 'div#awg-graph-visualizer-sparql-query > div.accordion-body', + 'div#awg-graph-visualizer-sparql-query-collapse > div.accordion-body', 1, 1 ); @@ -778,32 +842,29 @@ describe('SparqlEditorComponent (DONE)', () => { // Header debug elements const panelHeaderDes = getAndExpectDebugElementByCss( compDe, - 'div#awg-graph-visualizer-sparql-query-header.accordion-header', + 'div#awg-graph-visualizer-sparql-query > div.accordion-header', 1, 1 ); - // Panel header button + // Custom panel header button const btnDes = getAndExpectDebugElementByCss( panelHeaderDes[0], - 'div.accordion-button > button.btn-link', + 'button#awg-graph-visualizer-sparql-query-toggle', 1, 1 ); - - // Button native element const btnEl = btnDes[0].nativeElement; // Check button content - expect(btnEl.textContent).toBeTruthy(); - expect(btnEl.textContent).withContext(`should be 'SPARQL'`).toContain('SPARQL'); + expectToBe(btnEl.textContent, 'SPARQL'); }); - it('... should not toggle panel body by click on panel header button', async () => { + it('... should not toggle panel body on click', () => { // Header debug elements const panelHeaderDes = getAndExpectDebugElementByCss( compDe, - 'div#awg-graph-visualizer-sparql-query-header.accordion-header', + 'div#awg-graph-visualizer-sparql-query > div.accordion-header', 1, 1 ); @@ -811,17 +872,17 @@ describe('SparqlEditorComponent (DONE)', () => { // Button debug element const btnDes = getAndExpectDebugElementByCss( panelHeaderDes[0], - 'div.accordion-button > button.btn-link', + 'div.accordion-button > button#awg-graph-visualizer-sparql-query-toggle', 1, 1 ); // Button native element to click on const btnEl = btnDes[0].nativeElement; - // Panel body is open + // Panel body does not close getAndExpectDebugElementByCss( compDe, - 'div#awg-graph-visualizer-sparql-query > div.accordion-body', + 'div#awg-graph-visualizer-sparql-query-collapse > div.accordion-body', 1, 1, 'open' @@ -829,12 +890,12 @@ describe('SparqlEditorComponent (DONE)', () => { // Click header button click(btnEl as HTMLElement); - await detectChangesOnPush(fixture); + detectChangesOnPush(fixture); // Panel is open again getAndExpectDebugElementByCss( compDe, - 'div#awg-graph-visualizer-sparql-query > div.accordion-body', + 'div#awg-graph-visualizer-sparql-query-collapse > div.accordion-body', 1, 1, 'open' @@ -842,11 +903,11 @@ describe('SparqlEditorComponent (DONE)', () => { }); describe('View handle button group', () => { - it('... should contain ViewHandleButtongGroupComponent (stubbed) in panel header', () => { + it('... should contain no ViewHandleButtongGroupComponent (stubbed) in panel header', () => { // Header debug elements const panelHeaderDes = getAndExpectDebugElementByCss( compDe, - 'div#awg-graph-visualizer-sparql-query-header.accordion-header', + 'div#awg-graph-visualizer-sparql-query > div.accordion-header', 1, 1 ); @@ -854,45 +915,22 @@ describe('SparqlEditorComponent (DONE)', () => { getAndExpectDebugElementByDirective( panelHeaderDes[0], ViewHandleButtongGroupStubComponent, - 1, - 1 - ); - }); - - it('... should have selectedViewType===graph (according to querytype)', () => { - // Header debug elements - const panelHeaderDes = getAndExpectDebugElementByCss( - compDe, - 'div#awg-graph-visualizer-sparql-query-header.accordion-header', - 1, - 1 - ); - // ViewHandleButtongGroupComponent debug elements - const viewHandleButtongGroupDes = getAndExpectDebugElementByDirective( - panelHeaderDes[0], - ViewHandleButtongGroupStubComponent, - 1, - 1 + 0, + 0 ); - const viewHandleButtongGroupCmp = viewHandleButtongGroupDes[0].injector.get( - ViewHandleButtongGroupStubComponent - ) as ViewHandleButtongGroupStubComponent; - - expect(viewHandleButtongGroupCmp.selectedViewType).toBeTruthy(); - expect(viewHandleButtongGroupCmp.selectedViewType).toBe(ViewHandleTypes.GRAPH); }); }); describe('Example query button group', () => { - it('... should contain an example query btn-group in panel header if isExampleQueriesEnabled = true', async () => { + it('... should contain an example query btn-group in panel header if isExampleQueriesEnabled = true', () => { isExampleQueriesEnabledSpy.and.returnValue(true); - await detectChangesOnPush(fixture); + detectChangesOnPush(fixture); // Header debug elements const panelHeaderDes = getAndExpectDebugElementByCss( compDe, - 'div#awg-graph-visualizer-sparql-query-header.accordion-header', + 'div#awg-graph-visualizer-sparql-query > div.accordion-header', 1, 1 ); @@ -906,15 +944,15 @@ describe('SparqlEditorComponent (DONE)', () => { ); }); - it('... should not contain an example query btn-group in panel header if isExampleQueriesEnabled = false', async () => { + it('... should not contain an example query btn-group in panel header if isExampleQueriesEnabled = false', () => { isExampleQueriesEnabledSpy.and.returnValue(false); - await detectChangesOnPush(fixture); + detectChangesOnPush(fixture); // Header debug elements const panelHeaderDes = getAndExpectDebugElementByCss( compDe, - 'div#awg-graph-visualizer-sparql-query-header.accordion-header', + 'div#awg-graph-visualizer-sparql-query > div.accordion-header', 1, 1 ); @@ -932,7 +970,7 @@ describe('SparqlEditorComponent (DONE)', () => { // Header debug elements const panelHeaderDes = getAndExpectDebugElementByCss( compDe, - 'div#awg-graph-visualizer-sparql-query-header.accordion-header', + 'div#awg-graph-visualizer-sparql-query > div.accordion-header', 1, 1 ); @@ -946,17 +984,15 @@ describe('SparqlEditorComponent (DONE)', () => { const btnEl = btnDes[0].nativeElement; expect(btnEl.disabled).toBeTruthy(); - expect(btnEl.getAttribute('aria-disabled')).toBe('true'); - - expect(btnEl.textContent).toBeTruthy(); - expect(btnEl.textContent).toContain('Beispielabfragen'); + expectToBe(btnEl.getAttribute('aria-disabled'), 'true'); + expectToBe(btnEl.textContent.trim(), 'Beispielabfragen'); }); it('... should contain another btn-group dropdown in example query btn-group', () => { // Header debug elements const panelHeaderDes = getAndExpectDebugElementByCss( compDe, - 'div#awg-graph-visualizer-sparql-query-header.accordion-header', + 'div#awg-graph-visualizer-sparql-query > div.accordion-header', 1, 1 ); @@ -975,7 +1011,7 @@ describe('SparqlEditorComponent (DONE)', () => { // Header debug elements const panelHeaderDes = getAndExpectDebugElementByCss( compDe, - 'div#awg-graph-visualizer-sparql-query-header.accordion-header', + 'div#awg-graph-visualizer-sparql-query > div.accordion-header', 1, 1 ); @@ -994,7 +1030,7 @@ describe('SparqlEditorComponent (DONE)', () => { // Header debug elements const panelHeaderDes = getAndExpectDebugElementByCss( compDe, - 'div#awg-graph-visualizer-sparql-query-header.accordion-header', + 'div#awg-graph-visualizer-sparql-query > div.accordion-header', 1, 1 ); @@ -1036,25 +1072,16 @@ describe('SparqlEditorComponent (DONE)', () => { expectedQueryList.length ); - expect(itemDes.length) - .withContext(`should be ${expectedQueryList.length}`) - .toBe(expectedQueryList.length); - - const itemEl0 = itemDes[0].nativeElement; - const itemEl1 = itemDes[1].nativeElement; + expectToBe(itemDes.length, expectedQueryList.length); - expect(itemEl0.textContent).toBeTruthy(); - expect(itemEl0.textContent) - .withContext(`should contain ${expectedQueryList[0].queryLabel}`) - .toContain(expectedQueryList[0].queryLabel); + itemDes.forEach((itemDe: DebugElement, index: number) => { + const itemEl = itemDe.nativeElement; - expect(itemEl1.textContent).toBeTruthy(); - expect(itemEl1.textContent) - .withContext(`should contain ${expectedQueryList[1].queryLabel}`) - .toContain(expectedQueryList[1].queryLabel); + expectToBe(itemEl.textContent, expectedQueryList[index].queryLabel); + }); }); - it('... should disable current query in dropdown items in example query btn-group', async () => { + it('... should disable current query in dropdown items in example query btn-group', () => { const itemDes = getAndExpectDebugElementByCss( compDe, 'div.dropdown-menu > a.dropdown-item', @@ -1062,9 +1089,7 @@ describe('SparqlEditorComponent (DONE)', () => { expectedQueryList.length ); - expect(itemDes.length) - .withContext(`should be ${expectedQueryList.length}`) - .toBe(expectedQueryList.length); + expectToBe(itemDes.length, expectedQueryList.length); const itemEl0 = itemDes[0].nativeElement; const itemEl1 = itemDes[1].nativeElement; @@ -1073,13 +1098,13 @@ describe('SparqlEditorComponent (DONE)', () => { expect(itemEl1).not.toHaveClass('disabled'); component.query = expectedConstructQuery2; - await detectChangesOnPush(fixture); + detectChangesOnPush(fixture); expect(itemEl0).not.toHaveClass('disabled'); expect(itemEl1).toHaveClass('disabled'); }); - it('... should trigger `onQueryListChange()` by click on dropdown item', async () => { + it('... should trigger `onQueryListChange()` by click on dropdown item', () => { const itemDes = getAndExpectDebugElementByCss( compDe, 'div.dropdown-menu > a.dropdown-item', @@ -1087,9 +1112,7 @@ describe('SparqlEditorComponent (DONE)', () => { expectedQueryList.length ); - expect(itemDes.length) - .withContext(`should be ${expectedQueryList.length}`) - .toBe(expectedQueryList.length); + expectToBe(itemDes.length, expectedQueryList.length); const itemEl0 = itemDes[0].nativeElement; const itemEl1 = itemDes[1].nativeElement; @@ -1098,19 +1121,19 @@ describe('SparqlEditorComponent (DONE)', () => { // Click on second item (first disabled) click(itemEl1 as HTMLElement); - await detectChangesOnPush(fixture); + detectChangesOnPush(fixture); // Spy call with second query expectSpyCall(onQueryListChangeSpy, 1, expectedConstructQuery2); component.query = expectedConstructQuery2; - await detectChangesOnPush(fixture); + detectChangesOnPush(fixture); expect(itemEl0).not.toHaveClass('disabled'); // Click on first item (second disabled) click(itemEl0 as HTMLElement); - await detectChangesOnPush(fixture); + detectChangesOnPush(fixture); // Spy call with first query expectSpyCall(onQueryListChangeSpy, 2, expectedConstructQuery1); @@ -1123,83 +1146,55 @@ describe('SparqlEditorComponent (DONE)', () => { }); it('... should contain div with 3 buttons (Query, Reset, Clear) in panel body', () => { - const divDes = getAndExpectDebugElementByCss( - bodyDes[0], - 'div.awg-graph-visualizer-sparql-query-handle-buttons', - 1, - 1 - ); + const divDes = getAndExpectDebugElementByCss(bodyDes[0], 'div', 1, 1); const btnDes = getAndExpectDebugElementByCss(divDes[0], 'button.btn', 3, 3); const btnEl0 = btnDes[0].nativeElement; const btnEl1 = btnDes[1].nativeElement; const btnEl2 = btnDes[2].nativeElement; - expect(btnEl0.textContent).toBeTruthy(); - expect(btnEl0.textContent).withContext(`should contain 'Query'`).toContain('Query'); - - expect(btnEl1.textContent).toBeTruthy(); - expect(btnEl1.textContent).withContext(`should contain 'Reset'`).toContain('Reset'); - - expect(btnEl2.textContent).toBeTruthy(); - expect(btnEl2.textContent).withContext(`should contain 'Clear'`).toContain('Clear'); + expectToBe(btnEl0.textContent, 'Query'); + expectToBe(btnEl1.textContent, 'Reset'); + expectToBe(btnEl2.textContent, 'Clear'); }); - it('... should trigger `performQuery()` by click on Query button', async () => { - const btnDes = getAndExpectDebugElementByCss( - bodyDes[0], - 'div.awg-graph-visualizer-sparql-query-handle-buttons > button.btn', - 3, - 3 - ); + it('... should trigger `performQuery()` by click on Query button', () => { + const btnDes = getAndExpectDebugElementByCss(bodyDes[0], 'div > button.btn', 3, 3); const btnEl0 = btnDes[0].nativeElement; - expect(btnEl0.textContent).toBeTruthy(); - expect(btnEl0.textContent).withContext(`should contain 'Query'`).toContain('Query'); + expectToBe(btnEl0.textContent, 'Query'); // Click query button click(btnEl0 as HTMLElement); - await detectChangesOnPush(fixture); + detectChangesOnPush(fixture); expectSpyCall(performQuerySpy, 1); expectSpyCall(resetQuerySpy, 0); }); - it('... should trigger `resetQuery()` by click on Reset button', async () => { - const btnDes = getAndExpectDebugElementByCss( - bodyDes[0], - 'div.awg-graph-visualizer-sparql-query-handle-buttons > button.btn', - 3, - 3 - ); + it('... should trigger `resetTriples()` by click on Reset button', () => { + const btnDes = getAndExpectDebugElementByCss(bodyDes[0], 'div > button.btn', 3, 3); const btnEl1 = btnDes[1].nativeElement; - expect(btnEl1.textContent).toBeTruthy(); - expect(btnEl1.textContent).withContext(`should contain 'Reset'`).toContain('Reset'); + expectToBe(btnEl1.textContent, 'Reset'); // Click reset button click(btnEl1 as HTMLElement); - await detectChangesOnPush(fixture); + detectChangesOnPush(fixture); expectSpyCall(performQuerySpy, 0); expectSpyCall(resetQuerySpy, 1); }); - it('... should trigger `onEditorInputChange()` with empty string by click on Clear button', async () => { - const btnDes = getAndExpectDebugElementByCss( - bodyDes[0], - 'div.awg-graph-visualizer-sparql-query-handle-buttons > button.btn', - 3, - 3 - ); + it('... should trigger `onEditorInputChange()` with empty string by click on Clear button', () => { + const btnDes = getAndExpectDebugElementByCss(bodyDes[0], 'div > button.btn', 3, 3); const btnEl2 = btnDes[2].nativeElement; - expect(btnEl2.textContent).toBeTruthy(); - expect(btnEl2.textContent).withContext(`should contain 'Clear'`).toContain('Clear'); + expectToBe(btnEl2.textContent, 'Clear'); // Click clear button click(btnEl2 as HTMLElement); - await detectChangesOnPush(fixture); + detectChangesOnPush(fixture); expectSpyCall(onEditorInputChangeSpy, 1, ''); }); @@ -1276,7 +1271,7 @@ describe('SparqlEditorComponent (DONE)', () => { // Open panel by click on header button const btnDes = getAndExpectDebugElementByCss( compDe, - 'div#awg-graph-visualizer-sparql-query-header.accordion-header > div.accordion-button > button.btn-link', + 'div#awg-graph-visualizer-sparql-query > div.accordion-header > button.btn-link', 1, 1 ); @@ -1301,7 +1296,7 @@ describe('SparqlEditorComponent (DONE)', () => { expectSpyCall(onEditorInputChangeSpy, 1, changedQueryString); }); - it('... should trigger with empty string from click on Clear button', async () => { + it('... should trigger with empty string from click on Clear button', () => { const btnDes = getAndExpectDebugElementByCss( compDe, 'div.awg-graph-visualizer-sparql-query-handle-buttons > button.btn', @@ -1310,17 +1305,16 @@ describe('SparqlEditorComponent (DONE)', () => { ); const btnEl2 = btnDes[2].nativeElement; - expect(btnEl2.textContent).toBeTruthy(); - expect(btnEl2.textContent).withContext(`should contain 'Clear'`).toContain('Clear'); + expectToBe(btnEl2.textContent, 'Clear'); // Click clear button click(btnEl2 as HTMLElement); - await detectChangesOnPush(fixture); + detectChangesOnPush(fixture); expectSpyCall(onEditorInputChangeSpy, 1, ''); }); - it('... should emit updateQueryStringRequest on click', async () => { + it('... should emit updateQueryStringRequest on click', () => { const btnDes = getAndExpectDebugElementByCss( compDe, 'div.awg-graph-visualizer-sparql-query-handle-buttons > button.btn', @@ -1329,12 +1323,11 @@ describe('SparqlEditorComponent (DONE)', () => { ); const btnEl2 = btnDes[2].nativeElement; - expect(btnEl2.textContent).toBeTruthy(); - expect(btnEl2.textContent).withContext(`should contain 'Clear'`).toContain('Clear'); + expectToBe(btnEl2.textContent, 'Clear'); // Click clear button click(btnEl2 as HTMLElement); - await detectChangesOnPush(fixture); + detectChangesOnPush(fixture); expectSpyCall(onEditorInputChangeSpy, 1, ''); expectSpyCall(emitUpdateQueryStringRequestSpy, 1); @@ -1476,7 +1469,7 @@ describe('SparqlEditorComponent (DONE)', () => { // Header debug elements const panelHeaderDes = getAndExpectDebugElementByCss( compDe, - 'div#awg-graph-visualizer-sparql-query-header.accordion-header', + 'div#awg-graph-visualizer-sparql-query > div.accordion-header', 1, 1 ); @@ -1511,7 +1504,7 @@ describe('SparqlEditorComponent (DONE)', () => { // Open panel by click on header button const btnDes = getAndExpectDebugElementByCss( compDe, - 'div#awg-graph-visualizer-sparql-query-header.accordion-header > div.accordion-button > button.btn-link', + 'div#awg-graph-visualizer-sparql-query > div.accordion-header > button.btn-link', 1, 1 ); @@ -1526,7 +1519,7 @@ describe('SparqlEditorComponent (DONE)', () => { expect(component.performQuery).toBeDefined(); }); - it('... should trigger from click on Query button', async () => { + it('... should trigger from click on Query button', () => { const btnDes = getAndExpectDebugElementByCss( compDe, 'div.awg-graph-visualizer-sparql-query-handle-buttons > button.btn', @@ -1535,18 +1528,17 @@ describe('SparqlEditorComponent (DONE)', () => { ); const btnEl0 = btnDes[0].nativeElement; - expect(btnEl0.textContent).toBeTruthy(); - expect(btnEl0.textContent).withContext(`should contain 'Query'`).toContain('Query'); + expectToBe(btnEl0.textContent, 'Query'); // Click query button click(btnEl0 as HTMLElement); - await detectChangesOnPush(fixture); + detectChangesOnPush(fixture); expectSpyCall(performQuerySpy, 1); }); - describe('... should emit on click:', async () => { - it('`performQueryRequest` if querystring is given', async () => { + describe('... should emit on click:', () => { + it('`performQueryRequest` if querystring is given', () => { const btnDes = getAndExpectDebugElementByCss( compDe, 'div.awg-graph-visualizer-sparql-query-handle-buttons > button.btn', @@ -1555,12 +1547,11 @@ describe('SparqlEditorComponent (DONE)', () => { ); const btnEl0 = btnDes[0].nativeElement; - expect(btnEl0.textContent).toBeTruthy(); - expect(btnEl0.textContent).withContext(`should contain 'Query'`).toContain('Query'); + expectToBe(btnEl0.textContent, 'Query'); // Click query button click(btnEl0 as HTMLElement); - await detectChangesOnPush(fixture); + detectChangesOnPush(fixture); expectSpyCall(performQuerySpy, 1); expectSpyCall(emitPerformQueryRequestSpy, 1); @@ -1571,7 +1562,7 @@ describe('SparqlEditorComponent (DONE)', () => { const expectedErrorMessage = new ToastMessage('Empty query', 'Please enter a SPARQL query.'); component.query.queryString = ''; - await detectChangesOnPush(fixture); + detectChangesOnPush(fixture); const btnDes = getAndExpectDebugElementByCss( compDe, @@ -1581,12 +1572,11 @@ describe('SparqlEditorComponent (DONE)', () => { ); const btnEl0 = btnDes[0].nativeElement; - expect(btnEl0.textContent).toBeTruthy(); - expect(btnEl0.textContent).withContext(`should contain 'Query'`).toContain('Query'); + expectToBe(btnEl0.textContent, 'Query'); // Click query button click(btnEl0 as HTMLElement); - await detectChangesOnPush(fixture); + detectChangesOnPush(fixture); expectSpyCall(performQuerySpy, 1); expectSpyCall(emitPerformQueryRequestSpy, 0); @@ -1600,7 +1590,7 @@ describe('SparqlEditorComponent (DONE)', () => { // Open panel by click on header button const btnDes = getAndExpectDebugElementByCss( compDe, - 'div#awg-graph-visualizer-sparql-query-header.accordion-header > div.accordion-button > button.btn-link', + 'div#awg-graph-visualizer-sparql-query > div.accordion-header > button.btn-link', 1, 1 ); @@ -1615,7 +1605,7 @@ describe('SparqlEditorComponent (DONE)', () => { expect(component.resetQuery).toBeDefined(); }); - it('... should trigger from click on Reset button', async () => { + it('... should trigger on click on Reset button', () => { const btnDes = getAndExpectDebugElementByCss( compDe, 'div.awg-graph-visualizer-sparql-query-handle-buttons > button.btn', @@ -1624,26 +1614,25 @@ describe('SparqlEditorComponent (DONE)', () => { ); const btnEl1 = btnDes[1].nativeElement; - expect(btnEl1.textContent).toBeTruthy(); - expect(btnEl1.textContent).withContext(`should contain 'Reset'`).toContain('Reset'); + expectToBe(btnEl1.textContent, 'Reset'); // Click query button click(btnEl1 as HTMLElement); - await detectChangesOnPush(fixture); + detectChangesOnPush(fixture); expectSpyCall(resetQuerySpy, 1); }); - it('... should not emit anything if no query is provided', async () => { + it('... should not emit anything if no query is provided', () => { // Query is undefined component.resetQuery(undefined); - await detectChangesOnPush(fixture); + detectChangesOnPush(fixture); expectSpyCall(resetQuerySpy, 1, undefined); expectSpyCall(emitResestQueryRequestSpy, 0); }); - it('... should emit request on click', async () => { + it('... should emit request on click', () => { const btnDes = getAndExpectDebugElementByCss( compDe, 'div.awg-graph-visualizer-sparql-query-handle-buttons > button.btn', @@ -1652,12 +1641,11 @@ describe('SparqlEditorComponent (DONE)', () => { ); const btnEl1 = btnDes[1].nativeElement; - expect(btnEl1.textContent).toBeTruthy(); - expect(btnEl1.textContent).withContext(`should contain 'Reset'`).toContain('Reset'); + expectToBe(btnEl1.textContent, 'Reset'); // Click reset button click(btnEl1 as HTMLElement); - await detectChangesOnPush(fixture); + detectChangesOnPush(fixture); expectSpyCall(resetQuerySpy, 1); expectSpyCall(emitResestQueryRequestSpy, 1); @@ -1731,14 +1719,8 @@ describe('SparqlEditorComponent (DONE)', () => { component.switchQueryType(ViewHandleTypes.TABLE); - expect(component.query.queryType).toBeTruthy(); - expect(component.query.queryType) - .withContext(`should be ${expectedSelectQuery1.queryType}`) - .toBe(expectedSelectQuery1.queryType); - expect(component.query.queryString).toBeTruthy(); - expect(component.query.queryString) - .withContext(`should be ${expectedSelectQuery1.queryString}`) - .toBe(expectedSelectQuery1.queryString); + expectToBe(component.query.queryType, expectedSelectQuery1.queryType); + expectToBe(component.query.queryString, expectedSelectQuery1.queryString); }); it('... should switch querytype and string to `construct` if requested view is `graph`', () => { @@ -1751,14 +1733,8 @@ describe('SparqlEditorComponent (DONE)', () => { // Switch back to GRAPH view component.switchQueryType(ViewHandleTypes.GRAPH); - expect(component.query.queryType).toBeTruthy(); - expect(component.query.queryType) - .withContext(`should be ${expectedConstructQuery1.queryType}`) - .toBe(expectedConstructQuery1.queryType); - expect(component.query.queryString).toBeTruthy(); - expect(component.query.queryString) - .withContext(`should be ${expectedConstructQuery1.queryString}`) - .toBe(expectedConstructQuery1.queryString); + expectToBe(component.query.queryType, expectedConstructQuery1.queryType); + expectToBe(component.query.queryString, expectedConstructQuery1.queryString); }); it('... should do nothing if requested view is `grid`', () => { @@ -1767,14 +1743,8 @@ describe('SparqlEditorComponent (DONE)', () => { component.switchQueryType(ViewHandleTypes.GRID); - expect(component.query.queryType).toBeTruthy(); - expect(component.query.queryType) - .withContext(`should be ${expectedConstructQuery1.queryType}`) - .toBe(expectedConstructQuery1.queryType); - expect(component.query.queryString).toBeTruthy(); - expect(component.query.queryString) - .withContext(`should be ${expectedConstructQuery1.queryString}`) - .toBe(expectedConstructQuery1.queryString); + expectToBe(component.query.queryType, expectedConstructQuery1.queryType); + expectToBe(component.query.queryString, expectedConstructQuery1.queryString); }); it('... should throw error if requested view is not `table`, `graph` or `grid`', () => { @@ -1787,124 +1757,45 @@ describe('SparqlEditorComponent (DONE)', () => { }); }); - describe('#togglePanel()', () => { - it('... should have a method `togglePanel()`', () => { - expect(component.togglePanel).toBeDefined(); + describe('#isAccordionItemCollapsed()', () => { + it('... should have a method `isAccordionItemCollapsed`', () => { + expect(component.isAccordionItemCollapsed).toBeDefined(); }); - it('... should return empty string if isFullscreen = false', () => { - expect(component.togglePanel()).not.toBeTruthy(); + it('... should be triggered from ngbAccordionItem', () => { + expectSpyCall(isAccordionItemCollapsedSpy, 1); }); - it('... should return panel id if isFullscreen = true', () => { - const expectedId = 'awg-graph-visualizer-sparql-query'; + it('... should return true if isFullscreen is false', () => { + expectToBe(component.isAccordionItemCollapsed(), true); + }); + it('... should return false if isFullscreen is true', () => { // Set fullscreen flag to true component.isFullscreen = true; - expect(component.togglePanel()).withContext(`should be ${expectedId}`).toBe(expectedId); + + expectToBe(component.isAccordionItemCollapsed(), false); }); }); - describe('#preventPanelCollapseOnFullscreen()', () => { - it('... should have a method `preventPanelCollapseOnFullscreen`', () => { - expect(component.preventPanelCollapseOnFullscreen).toBeDefined(); + describe('#isAccordionItemDisabled()', () => { + it('... should have a method `isAccordionItemDisabled`', () => { + expect(component.isAccordionItemDisabled).toBeDefined(); }); - it('... should trigger on event from ngb-accordion', () => { - const accordionDes = getAndExpectDebugElementByDirective(compDe, NgbAccordion, 1, 1); - const accordionCmp = accordionDes[0].injector.get(NgbAccordion) as NgbAccordion; - - const panelChangeEvent: NgbPanelChangeEvent = { - panelId: 'panelChangeId', - nextState: true, - preventDefault: () => { - // Intentional empty test override - }, - }; - - // Emit change event from accordion - accordionCmp.panelChange.emit(panelChangeEvent); - - expectSpyCall(preventPanelCollapseOnFullscreenSpy, 1, panelChangeEvent); + it('... should be triggered from ngbAccordionItem', () => { + expectSpyCall(isAccordionItemDisabledSpy, 1); }); - it('... should not do anything if no $event is provided', () => { - const accordionDes = getAndExpectDebugElementByDirective(compDe, NgbAccordion, 1, 1); - const accordionCmp = accordionDes[0].injector.get(NgbAccordion) as NgbAccordion; - - // Emit undefined change event from accordion - accordionCmp.panelChange.emit(undefined); - - expectSpyCall(preventPanelCollapseOnFullscreenSpy, 1, undefined); + it('... should return false if isFullscreen is false', () => { + expectToBe(component.isAccordionItemDisabled(), false); }); - it('... should trigger $event.preventDefault() if isFullscreen == true && $event.nextState == false', () => { - const accordionDes = getAndExpectDebugElementByDirective(compDe, NgbAccordion, 1, 1); - const accordionCmp = accordionDes[0].injector.get(NgbAccordion) as NgbAccordion; - - const panelChangeEvent: NgbPanelChangeEvent = { - panelId: 'panelChangeId', - nextState: false, - preventDefault: () => { - // Intentional empty test override - }, - }; - const preventDefaultSpy: Spy = spyOn(panelChangeEvent, 'preventDefault').and.callThrough(); - - // Set fullscreen mode - component.isFullscreen = true; - - // Emit change event from accordion - accordionCmp.panelChange.emit(panelChangeEvent); - - expectSpyCall(preventPanelCollapseOnFullscreenSpy, 1, panelChangeEvent); - expectSpyCall(preventDefaultSpy, 1, undefined); - }); - - it('... should not trigger $event.preventDefault() if $event.nextState == true', () => { - const accordionDes = getAndExpectDebugElementByDirective(compDe, NgbAccordion, 1, 1); - const accordionCmp = accordionDes[0].injector.get(NgbAccordion) as NgbAccordion; - - const panelChangeEvent: NgbPanelChangeEvent = { - panelId: 'panelChangeId', - nextState: true, - preventDefault: () => { - // Intentional empty test override - }, - }; - const preventDefaultSpy: Spy = spyOn(panelChangeEvent, 'preventDefault').and.callThrough(); - - // Set fullscreen mode + it('... should return true if isFullscreen is true', () => { + // Set fullscreen flag to true component.isFullscreen = true; - // Emit change event from accordion - accordionCmp.panelChange.emit(panelChangeEvent); - - expectSpyCall(preventPanelCollapseOnFullscreenSpy, 1, panelChangeEvent); - expectSpyCall(preventDefaultSpy, 0, undefined); - }); - - it('... should not trigger $event.preventDefault() if isFullscreen == false', () => { - const accordionDes = getAndExpectDebugElementByDirective(compDe, NgbAccordion, 1, 1); - const accordionCmp = accordionDes[0].injector.get(NgbAccordion) as NgbAccordion; - - const panelChangeEvent: NgbPanelChangeEvent = { - panelId: 'panelChangeId', - nextState: false, - preventDefault: () => { - // Intentional empty test override - }, - }; - const preventDefaultSpy: Spy = spyOn(panelChangeEvent, 'preventDefault').and.callThrough(); - - // Unset fullscreen mode - component.isFullscreen = false; - - // Emit change event from accordion - accordionCmp.panelChange.emit(panelChangeEvent); - - expectSpyCall(preventPanelCollapseOnFullscreenSpy, 1, panelChangeEvent); - expectSpyCall(preventDefaultSpy, 0, undefined); + expectToBe(component.isAccordionItemDisabled(), true); }); }); }); diff --git a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/sparql-editor/sparql-editor.component.ts b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/sparql-editor/sparql-editor.component.ts index 67c87ed800..2e613fdc40 100644 --- a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/sparql-editor/sparql-editor.component.ts +++ b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/sparql-editor/sparql-editor.component.ts @@ -7,12 +7,10 @@ import { OnInit, Output, SimpleChanges, - ViewChild, } from '@angular/core'; import { sparql } from '@codemirror/legacy-modes/mode/sparql'; import { faDiagramProject, faTable } from '@fortawesome/free-solid-svg-icons'; -import { NgbAccordion, NgbPanelChangeEvent } from '@ng-bootstrap/ng-bootstrap'; import { CmMode } from '@awg-shared/codemirror/codemirror.component'; import { ToastMessage } from '@awg-shared/toast/toast.service'; @@ -32,13 +30,6 @@ import { GraphSparqlQuery } from '@awg-views/edition-view/models'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class SparqlEditorComponent implements OnInit, OnChanges { - /** - * ViewChild variable: sparqlAcc. - * - * It keeps the reference to the NgbAccordion. - */ - @ViewChild('sparqlAcc') sparqlAcc: NgbAccordion; - /** * Input variable: queryList. * @@ -164,12 +155,12 @@ export class SparqlEditorComponent implements OnInit, OnChanges { * @returns {void} Sets the selected view type. */ setViewType(): void { - if (this.query.queryType && this.query.queryType === 'construct') { - this.selectedViewType = ViewHandleTypes.GRAPH; - } - if (this.query.queryType && this.query.queryType === 'select') { - this.selectedViewType = ViewHandleTypes.TABLE; - } + const viewTypeMap = { + construct: ViewHandleTypes.GRAPH, + select: ViewHandleTypes.TABLE, + }; + + this.selectedViewType = viewTypeMap[this.query?.queryType] || ViewHandleTypes.GRAPH; } /** @@ -308,30 +299,26 @@ export class SparqlEditorComponent implements OnInit, OnChanges { } /** - * Public method: preventPanelCollapseOnFullscreen. + * Public method: isAccordionItemCollapsed. * - * It prevents the given panel event from being collapsed in fullscreen mode. + * It returns a boolean flag if the accordion item should be collapsed. + * It returns false if fullscreenMode is set, otherwise true. * - * @returns {void} Prevents the panel collapse. + * @returns {boolean} The boolean value of the comparison. */ - preventPanelCollapseOnFullscreen($event: NgbPanelChangeEvent): void { - if (!$event) { - return; - } - if (this.isFullscreen && $event.nextState === false) { - $event.preventDefault(); - } + isAccordionItemCollapsed(): boolean { + return this.isFullscreen ? false : true; } /** - * Public method: togglePanel. + * Public method: isAccordionItemDisabled. * - * It returns the id of the panel to be toggled if fullscreen mode is set, - * otherwise empty string. + * It returns a boolean flag if the accordion item should be disabled. + * It returns true if fullscreenMode is set, otherwise false. * - * @returns {string} The id of the panel to be toggled. + * @returns {boolean} The boolean value of the comparison. */ - togglePanel(): string { - return this.isFullscreen ? 'awg-graph-visualizer-sparql-query' : ''; + isAccordionItemDisabled(): boolean { + return this.isFullscreen ? true : false; } } diff --git a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/sparql-table/sparql-table.component.html b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/sparql-table/sparql-table.component.html index b2b550e0ae..02b2d4055c 100644 --- a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/sparql-table/sparql-table.component.html +++ b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/sparql-table/sparql-table.component.html @@ -1,7 +1,8 @@ - +@if (queryResult) { + +} diff --git a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/triples-editor/triples-editor.component.html b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/triples-editor/triples-editor.component.html index 54d73fb8f8..9a3b6e1d78 100644 --- a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/triples-editor/triples-editor.component.html +++ b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/triples-editor/triples-editor.component.html @@ -3,10 +3,10 @@ ngbAccordionItem="awg-graph-visualizer-triples" [collapsed]="isAccordionItemCollapsed()" [disabled]="isAccordionItemDisabled()"> -

    - -

    -
    +
    + +
    +
    diff --git a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/triples-editor/triples-editor.component.spec.ts b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/triples-editor/triples-editor.component.spec.ts index ddfd4124c6..72ba7cf665 100644 --- a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/triples-editor/triples-editor.component.spec.ts +++ b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/triples-editor/triples-editor.component.spec.ts @@ -72,9 +72,9 @@ describe('TriplesEditorComponent (DONE)', () => { // Test data expectedIsFullscreen = false; + expectedCmTurtleMode = turtle; expectedTriples = 'example:Test example:has example:Success'; - expectedCmTurtleMode = turtle; // Spies on component functions // `.and.callThrough` will track the spy down the nested describes, see @@ -108,8 +108,8 @@ describe('TriplesEditorComponent (DONE)', () => { }); describe('VIEW', () => { - it('... should contain no ngb-accordion yet', () => { - // Ngb-accordion debug element + it('... should contain no div.accordion yet', () => { + // Div.accordion debug element getAndExpectDebugElementByCss(compDe, 'div.accordion', 0, 0); }); }); @@ -135,14 +135,14 @@ describe('TriplesEditorComponent (DONE)', () => { describe('VIEW', () => { it('... should contain one div.accordion', () => { - // Ngb-accordion debug element + // NgbAccordion debug element getAndExpectDebugElementByCss(compDe, 'div.accordion', 1, 1); }); describe('not in fullscreen mode', () => { describe('with closed panel', () => { it('... should contain one div.accordion with panel (div.accordion-item) header and collapsed body', () => { - // Ngb-accordion debug element + // NgbAccordion debug element const accordionDes = getAndExpectDebugElementByCss(compDe, 'div.accordion', 1, 1); // Panel (div.accordion-item) @@ -152,10 +152,10 @@ describe('TriplesEditorComponent (DONE)', () => { 1, 1 ); - // Header (h2.accordion-header) + // Header (div.accordion-header) const panelHeaderDes = getAndExpectDebugElementByCss( panelDes[0], - 'div#awg-graph-visualizer-triples > h2.accordion-header', + 'div#awg-graph-visualizer-triples > div.accordion-header', 1, 1 ); @@ -179,7 +179,7 @@ describe('TriplesEditorComponent (DONE)', () => { // Header debug elements const panelHeaderDes = getAndExpectDebugElementByCss( compDe, - 'div#awg-graph-visualizer-triples > h2.accordion-header', + 'div#awg-graph-visualizer-triples > div.accordion-header', 1, 1 ); @@ -202,7 +202,7 @@ describe('TriplesEditorComponent (DONE)', () => { // Header debug elements const panelHeaderDes = getAndExpectDebugElementByCss( compDe, - 'div#awg-graph-visualizer-triples > h2.accordion-header', + 'div#awg-graph-visualizer-triples > div.accordion-header', 1, 1 ); @@ -210,7 +210,7 @@ describe('TriplesEditorComponent (DONE)', () => { // Button debug elements const btnDes = getAndExpectDebugElementByCss( panelHeaderDes[0], - 'button.accordion-button', + 'button#awg-graph-visualizer-triples-toggle', 1, 1 ); @@ -241,7 +241,7 @@ describe('TriplesEditorComponent (DONE)', () => { ); panelBodyEl = panelBodyDes[0].nativeElement; - expect(panelBodyEl.classList).toContain('show'); + expectToContain(panelBodyEl.classList, 'show'); // Click header button click(btnEl as HTMLElement); @@ -285,7 +285,7 @@ describe('TriplesEditorComponent (DONE)', () => { ); const collapseEl = collapseDes[0].nativeElement; - expect(collapseEl.classList).toContain('show'); + expectToContain(collapseEl.classList, 'show'); // Panel body bodyDes = getAndExpectDebugElementByCss(collapseDes[0], 'div.accordion-body', 1, 1); @@ -295,7 +295,7 @@ describe('TriplesEditorComponent (DONE)', () => { // Header debug elements const panelHeaderDes = getAndExpectDebugElementByCss( compDe, - 'div#awg-graph-visualizer-triples > h2.accordion-header', + 'div#awg-graph-visualizer-triples > div.accordion-header', 1, 1 ); @@ -303,7 +303,7 @@ describe('TriplesEditorComponent (DONE)', () => { // Button debug elements const btnDes = getAndExpectDebugElementByCss( panelHeaderDes[0], - 'button.accordion-button', + 'button#awg-graph-visualizer-triples-toggle', 1, 1 ); @@ -319,7 +319,7 @@ describe('TriplesEditorComponent (DONE)', () => { ); let panelBodyEl = panelBodyDes[0].nativeElement; - expect(panelBodyEl.classList).toContain('show'); + expectToContain(panelBodyEl.classList, 'show'); // Click header button click(btnEl as HTMLElement); @@ -349,7 +349,7 @@ describe('TriplesEditorComponent (DONE)', () => { ); panelBodyEl = panelBodyDes[0].nativeElement; - expect(panelBodyEl.classList).toContain('show'); + expectToContain(panelBodyEl.classList, 'show'); }); it('... should contain CodeMirrorComponent (stubbed) in panel body', () => { @@ -358,7 +358,12 @@ describe('TriplesEditorComponent (DONE)', () => { }); it('... should contain div with 3 buttons (Query, Reset, Clear) in panel body', () => { - const divDes = getAndExpectDebugElementByCss(bodyDes[0], 'div', 1, 1); + const divDes = getAndExpectDebugElementByCss( + bodyDes[0], + 'div.awg-graph-visualizer-triples-handle-buttons', + 1, + 1 + ); const btnDes = getAndExpectDebugElementByCss(divDes[0], 'button.btn', 3, 3); const btnEl0 = btnDes[0].nativeElement; @@ -371,7 +376,12 @@ describe('TriplesEditorComponent (DONE)', () => { }); it('... should trigger `performQuery()` by click on Query button', () => { - const btnDes = getAndExpectDebugElementByCss(bodyDes[0], 'div > button.btn', 3, 3); + const btnDes = getAndExpectDebugElementByCss( + bodyDes[0], + 'div.awg-graph-visualizer-triples-handle-buttons > button.btn', + 3, + 3 + ); const btnEl0 = btnDes[0].nativeElement; expectToBe(btnEl0.textContent, 'Query'); @@ -385,7 +395,12 @@ describe('TriplesEditorComponent (DONE)', () => { }); it('... should trigger `resetTriples()` by click on Reset button', () => { - const btnDes = getAndExpectDebugElementByCss(bodyDes[0], 'div > button.btn', 3, 3); + const btnDes = getAndExpectDebugElementByCss( + bodyDes[0], + 'div.awg-graph-visualizer-triples-handle-buttons > button.btn', + 3, + 3 + ); const btnEl1 = btnDes[1].nativeElement; expectToBe(btnEl1.textContent, 'Reset'); @@ -399,7 +414,12 @@ describe('TriplesEditorComponent (DONE)', () => { }); it('... should trigger `onEditorInputChange()` with empty string by click on Clear button', () => { - const btnDes = getAndExpectDebugElementByCss(bodyDes[0], 'div > button.btn', 3, 3); + const btnDes = getAndExpectDebugElementByCss( + bodyDes[0], + 'div.awg-graph-visualizer-triples-handle-buttons > button.btn', + 3, + 3 + ); const btnEl2 = btnDes[2].nativeElement; expectToBe(btnEl2.textContent, 'Clear'); @@ -408,7 +428,6 @@ describe('TriplesEditorComponent (DONE)', () => { click(btnEl2 as HTMLElement); detectChangesOnPush(fixture); - expectSpyCall(onEditorInputChangeSpy, 1); expectSpyCall(onEditorInputChangeSpy, 1, ''); }); }); @@ -444,7 +463,7 @@ describe('TriplesEditorComponent (DONE)', () => { }); it('... should contain one div.accordion with panel (div.accordion-item) header and open body', () => { - // Ngb-accordion debug element + // NgbAccordion debug element const accordionDes = getAndExpectDebugElementByCss(compDe, 'div.accordion', 1, 1); // Panel (div.accordion-item) @@ -454,10 +473,10 @@ describe('TriplesEditorComponent (DONE)', () => { 1, 1 ); - // Header (h2.accordion-header) + // Header (div.accordion-header) getAndExpectDebugElementByCss( panelDes[0], - 'div#awg-graph-visualizer-triples > h2.accordion-header', + 'div#awg-graph-visualizer-triples > div.accordion-header', 1, 1 ); @@ -472,14 +491,16 @@ describe('TriplesEditorComponent (DONE)', () => { }); it('... should display panel header button', () => { - // Panel header button - const btnDes = getAndExpectDebugElementByCss( + // Header debug elements + const panelHeaderDes = getAndExpectDebugElementByCss( compDe, - 'div#awg-graph-visualizer-triples > h2.accordion-header > button.accordion-button', + 'div#awg-graph-visualizer-triples > div.accordion-header', 1, 1 ); + // Panel header button + const btnDes = getAndExpectDebugElementByCss(panelHeaderDes[0], 'button.accordion-button', 1, 1); const btnEl = btnDes[0].nativeElement; // Check button content @@ -490,7 +511,7 @@ describe('TriplesEditorComponent (DONE)', () => { // Header debug elements const panelHeaderDes = getAndExpectDebugElementByCss( compDe, - 'div#awg-graph-visualizer-triples > h2.accordion-header', + 'div#awg-graph-visualizer-triples > div.accordion-header', 1, 1 ); @@ -501,26 +522,32 @@ describe('TriplesEditorComponent (DONE)', () => { const btnEl = btnDes[0].nativeElement; // Panel body does not close - getAndExpectDebugElementByCss( + let panelBodyDes = getAndExpectDebugElementByCss( compDe, - 'div#awg-graph-visualizer-triples > div#awg-graph-visualizer-triples-collapse > div.accordion-body', + 'div#awg-graph-visualizer-triples > div.accordion-collapse', 1, 1, 'open' ); + let panelBodyEl = panelBodyDes[0].nativeElement; + + expectToContain(panelBodyEl.classList, 'show'); // Click header button click(btnEl as HTMLElement); detectChangesOnPush(fixture); // Panel body does not close again - getAndExpectDebugElementByCss( + panelBodyDes = getAndExpectDebugElementByCss( compDe, - 'div#awg-graph-visualizer-triples > div#awg-graph-visualizer-triples-collapse > div.accordion-body', + 'div#awg-graph-visualizer-triples > div.accordion-collapse', 1, 1, 'open' ); + panelBodyEl = panelBodyDes[0].nativeElement; + + expectToContain(panelBodyEl.classList, 'show'); }); it('... should contain CodeMirrorComponent (stubbed) in panel body', () => { @@ -579,7 +606,6 @@ describe('TriplesEditorComponent (DONE)', () => { click(btnEl2 as HTMLElement); detectChangesOnPush(fixture); - expectSpyCall(onEditorInputChangeSpy, 1); expectSpyCall(onEditorInputChangeSpy, 1, ''); }); }); @@ -615,7 +641,7 @@ describe('TriplesEditorComponent (DONE)', () => { expectSpyCall(onEditorInputChangeSpy, 1, changedTriples); }); - it('... should trigger with empty string from click on Clear button', async () => { + it('... should trigger with empty string from click on Clear button', () => { const btnDes = getAndExpectDebugElementByCss( compDe, 'div.awg-graph-visualizer-triples-handle-buttons > button.btn', @@ -633,7 +659,7 @@ describe('TriplesEditorComponent (DONE)', () => { expectSpyCall(onEditorInputChangeSpy, 1, ''); }); - it('... should emit updateTriplesRequest on click', async () => { + it('... should emit updateTriplesRequest on click', () => { const btnDes = getAndExpectDebugElementByCss( compDe, 'div.awg-graph-visualizer-triples-handle-buttons > button.btn', @@ -700,7 +726,7 @@ describe('TriplesEditorComponent (DONE)', () => { expect(component.performQuery).toBeDefined(); }); - it('... should trigger on click on Query button', async () => { + it('... should trigger on click on Query button', () => { const btnDes = getAndExpectDebugElementByCss( compDe, 'div.awg-graph-visualizer-triples-handle-buttons > button.btn', @@ -713,13 +739,13 @@ describe('TriplesEditorComponent (DONE)', () => { // Click query button click(btnEl0 as HTMLElement); - await detectChangesOnPush(fixture); + detectChangesOnPush(fixture); expectSpyCall(performQuerySpy, 1); }); describe('... should emit on click', () => { - it('`performQueryRequest` if querystring is given', async () => { + it('`performQueryRequest` if querystring is given', () => { const btnDes = getAndExpectDebugElementByCss( compDe, 'div.awg-graph-visualizer-triples-handle-buttons > button.btn', @@ -732,18 +758,18 @@ describe('TriplesEditorComponent (DONE)', () => { // Click query button click(btnEl0 as HTMLElement); - await detectChangesOnPush(fixture); + detectChangesOnPush(fixture); expectSpyCall(performQuerySpy, 1); expectSpyCall(emitPerformQueryRequestSpy, 1); expectSpyCall(emitErrorMessageSpy, 0); }); - it('`errorMessageRequest` with errorMessage if querystring is not given', async () => { + it('`errorMessageRequest` with errorMessage if querystring is not given', () => { const expectedErrorMessage = new ToastMessage('Empty triples', 'Please enter triple content.'); component.triples = ''; - await detectChangesOnPush(fixture); + detectChangesOnPush(fixture); const btnDes = getAndExpectDebugElementByCss( compDe, @@ -757,7 +783,7 @@ describe('TriplesEditorComponent (DONE)', () => { // Click query button click(btnEl0 as HTMLElement); - await detectChangesOnPush(fixture); + detectChangesOnPush(fixture); expectSpyCall(performQuerySpy, 1); expectSpyCall(emitPerformQueryRequestSpy, 0); @@ -786,7 +812,7 @@ describe('TriplesEditorComponent (DONE)', () => { expect(component.resetTriples).toBeDefined(); }); - it('... should trigger on click on Reset button', async () => { + it('... should trigger on click on Reset button', () => { const btnDes = getAndExpectDebugElementByCss( compDe, 'div.awg-graph-visualizer-triples-handle-buttons > button.btn', @@ -799,12 +825,12 @@ describe('TriplesEditorComponent (DONE)', () => { // Click query button click(btnEl1 as HTMLElement); - await detectChangesOnPush(fixture); + detectChangesOnPush(fixture); expectSpyCall(resetTriplesSpy, 1); }); - it('... should emit request on click', async () => { + it('... should emit request on click', () => { const btnDes = getAndExpectDebugElementByCss( compDe, 'div.awg-graph-visualizer-triples-handle-buttons > button.btn', @@ -817,7 +843,7 @@ describe('TriplesEditorComponent (DONE)', () => { // Click reset button click(btnEl1 as HTMLElement); - await detectChangesOnPush(fixture); + detectChangesOnPush(fixture); expectSpyCall(resetTriplesSpy, 1); expectSpyCall(emitResetTriplesRequestSpy, 1); diff --git a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/unsupported-type-results/unsupported-type-results.component.html b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/unsupported-type-results/unsupported-type-results.component.html index 2fc83ed3df..9eb1e1dac5 100644 --- a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/unsupported-type-results/unsupported-type-results.component.html +++ b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/unsupported-type-results/unsupported-type-results.component.html @@ -1,10 +1,19 @@ - - - -

    - Sorry, but the requested SPARQL query type {{ queryType | uppercase }} is not supported yet. -

    -

    Please try a CONSTRUCT or SELECT query instead.

    -
    -
    -
    +
    +
    +
    + +
    +
    +
    +

    + Sorry, but the requested SPARQL query type {{ queryType | uppercase }} is not supported + yet. +

    +

    Please try a CONSTRUCT or SELECT query instead.

    +
    +
    +
    +
    diff --git a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/unsupported-type-results/unsupported-type-results.component.spec.ts b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/unsupported-type-results/unsupported-type-results.component.spec.ts index be3b9b5be8..0941eaf292 100644 --- a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/unsupported-type-results/unsupported-type-results.component.spec.ts +++ b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/unsupported-type-results/unsupported-type-results.component.spec.ts @@ -2,10 +2,12 @@ import { DebugElement, NgModule } from '@angular/core'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { NgbAccordionModule, NgbConfig } from '@ng-bootstrap/ng-bootstrap'; +import Spy = jasmine.Spy; import { detectChangesOnPush } from '@testing/detect-changes-on-push-helper'; -import { getAndExpectDebugElementByCss } from '@testing/expect-helper'; +import { expectSpyCall, expectToBe, expectToContain, getAndExpectDebugElementByCss } from '@testing/expect-helper'; +import { click } from '@testing/click-helper'; import { UnsupportedTypeResultsComponent } from './unsupported-type-results.component'; describe('UnsupportedTypeResultsComponent (DONE)', () => { @@ -14,6 +16,9 @@ describe('UnsupportedTypeResultsComponent (DONE)', () => { let compDe: DebugElement; let expectedQueryType: string; + let expectedIsFullscreen: boolean; + + let isAccordionItemDisabledSpy: Spy; // Global NgbConfigModule @NgModule({ imports: [NgbAccordionModule], exports: [NgbAccordionModule] }) @@ -38,6 +43,12 @@ describe('UnsupportedTypeResultsComponent (DONE)', () => { // Test data expectedQueryType = 'ask'; + expectedIsFullscreen = false; + + // Spies on component functions + // `.and.callThrough` will track the spy down the nested describes, see + // https://jasmine.github.io/2.0/introduction.html#section-Spies:_%3Ccode%3Eand.callThrough%3C/code%3E + isAccordionItemDisabledSpy = spyOn(component, 'isAccordionItemDisabled').and.callThrough(); }); it('... should create', () => { @@ -49,13 +60,14 @@ describe('UnsupportedTypeResultsComponent (DONE)', () => { expect(component.queryType).toBeUndefined(); }); - describe('VIEW', () => { - it('... should contain one ngb-accordion without panel (div.accordion-item) yet', () => { - // Ngb-accordion debug element - const accordionDes = getAndExpectDebugElementByCss(compDe, 'ngb-accordion', 1, 1); + it('... should not have isFullscreen', () => { + expect(component.isFullscreen).toBeUndefined(); + }); - // Panel - getAndExpectDebugElementByCss(accordionDes[0], 'div.accordion-item', 0, 0, 'yet'); + describe('VIEW', () => { + it('... should contain no div.accordion yet', () => { + // Div.accordion debug element + getAndExpectDebugElementByCss(compDe, 'div.accordion', 0, 0); }); }); }); @@ -64,131 +76,405 @@ describe('UnsupportedTypeResultsComponent (DONE)', () => { beforeEach(() => { // Simulate the parent setting the input properties component.queryType = expectedQueryType; + component.isFullscreen = expectedIsFullscreen; // Trigger initial data binding fixture.detectChanges(); }); it('... should have `queryType` input', () => { - expect(component.queryType).toBeDefined(); - expect(component.queryType).withContext(`should be ${expectedQueryType}`).toBe(expectedQueryType); + expectToBe(component.queryType, expectedQueryType); + }); + + it('... should have `isFullScreen` input', () => { + expectToBe(component.isFullscreen, expectedIsFullscreen); }); describe('VIEW', () => { - it('... should contain one ngb-accordion with panel (div.accordion-item) header and body', () => { - // Ngb-accordion debug element - const accordionDes = getAndExpectDebugElementByCss(compDe, 'ngb-accordion', 1, 1); - - // Panel (div.card) - const panelDes = getAndExpectDebugElementByCss(accordionDes[0], 'div.accordion-item', 1, 1); // Panel (div.card) - // Header - getAndExpectDebugElementByCss( - panelDes[0], - 'div#awg-graph-visualizer-unsupported-query-type-result-header.accordion-header', - 1, - 1 - ); // Panel (div.card) - // Body - getAndExpectDebugElementByCss( - panelDes[0], - 'div#awg-graph-visualizer-unsupported-query-type-result > div.accordion-body', - 1, - 1 - ); - }); + describe('not in fullscreen mode', () => { + it('... should contain one div.accordion with panel (div.accordion-item) header and open body', () => { + // Div.accordion debug element + const accordionDes = getAndExpectDebugElementByCss(compDe, 'div.accordion', 1, 1); + + // Panel (div.accordion-item) + const panelDes = getAndExpectDebugElementByCss( + accordionDes[0], + 'div#awg-graph-visualizer-unsupported-query-type-result.accordion-item', + 1, + 1 + ); + // Header (div.accordion-header) + const panelHeaderDes = getAndExpectDebugElementByCss( + panelDes[0], + 'div#awg-graph-visualizer-unsupported-query-type-result > div.accordion-header', + 1, + 1 + ); + const panelHeaderEl = panelHeaderDes[0].nativeElement; - it('... should display panel header button', () => { - // Panel header button - const btnDes = getAndExpectDebugElementByCss( - compDe, - 'div#awg-graph-visualizer-unsupported-query-type-result-header > button', - 1, - 1 - ); + expect(panelHeaderEl.classList).not.toContain('collapsed'); - const btnEl = btnDes[0].nativeElement; + // Body (div.accordion-collapse) + const panelBodyDes = getAndExpectDebugElementByCss( + panelDes[0], + 'div#awg-graph-visualizer-unsupported-query-type-result > div.accordion-collapse', + 1, + 1 + ); + const panelBodyEl = panelBodyDes[0].nativeElement; + + expectToContain(panelBodyEl.classList, 'show'); + }); + + it('... should display panel header button', () => { + // Header debug elements + const panelHeaderDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-unsupported-query-type-result > div.accordion-header', + 1, + 1 + ); - // Check button content - expect(btnEl.textContent).toBeTruthy(); - expect(btnEl.textContent).withContext('should contain Resultat').toContain('Resultat'); - }); + // Panel header button + const btnDes = getAndExpectDebugElementByCss(panelHeaderDes[0], 'button.accordion-button', 1, 1); + + const btnEl = btnDes[0].nativeElement; + + // Check button content + expectToBe(btnEl.textContent, 'Resultat'); + }); + + it('... should toggle panel body on click', () => { + // Header debug elements + const panelHeaderDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-unsupported-query-type-result > div.accordion-header', + 1, + 1 + ); + + // Button debug elements + const btnDes = getAndExpectDebugElementByCss( + panelHeaderDes[0], + 'button#awg-graph-visualizer-unsupported-query-type-result-toggle', + 1, + 1 + ); + // Button native elements to click on + const btnEl = btnDes[0].nativeElement; + + // Panel body is closed + let panelBodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-unsupported-query-type-result > div.accordion-collapse', + 1, + 1 + ); + let panelBodyEl = panelBodyDes[0].nativeElement; + + expect(panelBodyEl.classList).toContain('show'); + + // Click header button + click(btnEl as HTMLElement); + detectChangesOnPush(fixture); + + // Panel is open + panelBodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-unsupported-query-type-result > div.accordion-collapse', + 1, + 1 + ); + panelBodyEl = panelBodyDes[0].nativeElement; + + expect(panelBodyEl.classList).not.toContain('show'); + + // Click header button + click(btnEl as HTMLElement); + detectChangesOnPush(fixture); + + panelBodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-unsupported-query-type-result > div.accordion-collapse', + 1, + 1 + ); + panelBodyEl = panelBodyDes[0].nativeElement; + + expect(panelBodyEl.classList).toContain('show'); + }); + + it('... should contain panel body with two centered paragraphs', () => { + // Body debug elements + const panelBodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-unsupported-query-type-result > div.accordion-collapse', + 1, + 1 + ); + + // Panel body paragraphs + const pDes = getAndExpectDebugElementByCss(panelBodyDes[0], 'p', 2, 2); + + pDes.forEach((pDe: DebugElement) => { + const pEl = pDe.nativeElement; + expect(pEl).toBeTruthy(); + expect(pEl).toHaveClass('text-center'); + }); + }); + + it('... should display messages in panel body paragraphs', () => { + // Body debug elements + const panelBodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-unsupported-query-type-result > div.accordion-collapse', + 1, + 1 + ); + + // Panel body paragraphs + const pDes = getAndExpectDebugElementByCss(panelBodyDes[0], 'p', 2, 2); + + const pEl0 = pDes[0].nativeElement; + const pEl1 = pDes[1].nativeElement; + + expectToContain( + pEl0.textContent.trim(), + `Sorry, but the requested SPARQL query type ${expectedQueryType.toUpperCase()} is not supported yet` + ); + expectToContain(pEl1.textContent.trim(), 'Please try a CONSTRUCT or SELECT query instead.'); + }); + + it('... should display correct queryType in first paragraph if input changes', () => { + // Body debug elements + const panelBodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-unsupported-query-type-result > div.accordion-collapse', + 1, + 1 + ); + + // Panel body paragraphs + const pDes = getAndExpectDebugElementByCss(panelBodyDes[0], 'p', 2, 2); + + const pEl0 = pDes[0].nativeElement; + + expectToContain(pEl0.textContent, expectedQueryType.toUpperCase()); - it('... should contain panel body with two centered paragraphs', () => { - // Panel body paragraphs - const pDes = getAndExpectDebugElementByCss( - compDe, - 'div#awg-graph-visualizer-unsupported-query-type-result > div.accordion-body > p', - 2, - 2 - ); + // DESCRIBE + let newQueryType = 'describe'; + component.queryType = newQueryType; + detectChangesOnPush(fixture); - const pEl0 = pDes[0].nativeElement; - const pEl1 = pDes[1].nativeElement; + expectToContain(pEl0.textContent, newQueryType.toUpperCase()); - expect(pEl0).toHaveClass('text-center'); - expect(pEl1).toHaveClass('text-center'); + // COUNT + newQueryType = 'count'; + component.queryType = newQueryType; + detectChangesOnPush(fixture); + + expectToContain(pEl0.textContent, newQueryType.toUpperCase()); + }); }); - it('... should display messages in panel body paragraphs', () => { - // Panel body paragraphs - const pDes = getAndExpectDebugElementByCss( - compDe, - 'div#awg-graph-visualizer-unsupported-query-type-result > div.accordion-body > p', - 2, - 2 - ); - - const pEl0 = pDes[0].nativeElement; - const pEl1 = pDes[1].nativeElement; - - expect(pEl0.textContent).toBeTruthy(); - expect(pEl0.textContent.trim()) - .withContext( - `should contain 'Sorry, but the requested SPARQL query type ${expectedQueryType.toUpperCase()} is not supported yet'` - ) - .toContain( + describe('in fullscreen mode', () => { + beforeEach(() => { + // Set fullscreen flag to true + component.isFullscreen = true; + detectChangesOnPush(fixture); + }); + + it('... should contain one div.accordion with panel (div.accordion-item) header and open body', () => { + // Div.accordion debug element + const accordionDes = getAndExpectDebugElementByCss(compDe, 'div.accordion', 1, 1); + + // Panel (div.accordion-item) + const panelDes = getAndExpectDebugElementByCss( + accordionDes[0], + 'div#awg-graph-visualizer-unsupported-query-type-result.accordion-item', + 1, + 1 + ); + // Header (div.accordion-header) + const panelHeaderDes = getAndExpectDebugElementByCss( + panelDes[0], + 'div#awg-graph-visualizer-unsupported-query-type-result > div.accordion-header', + 1, + 1 + ); + const panelHeaderEl = panelHeaderDes[0].nativeElement; + + expect(panelHeaderEl.classList).not.toContain('collapsed'); + + // Body (div.accordion-collapse) + const panelBodyDes = getAndExpectDebugElementByCss( + panelDes[0], + 'div#awg-graph-visualizer-unsupported-query-type-result > div.accordion-collapse', + 1, + 1 + ); + const panelBodyEl = panelBodyDes[0].nativeElement; + + expectToContain(panelBodyEl.classList, 'show'); + }); + + it('... should display panel header button', () => { + // Header debug elements + const panelHeaderDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-unsupported-query-type-result > div.accordion-header', + 1, + 1 + ); + + // Panel header button + const btnDes = getAndExpectDebugElementByCss(panelHeaderDes[0], 'button.accordion-button', 1, 1); + + const btnEl = btnDes[0].nativeElement; + + // Check button content + expectToBe(btnEl.textContent, 'Resultat'); + }); + + it('... should not toggle panel body on click', () => { + // Header debug elements + const panelHeaderDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-unsupported-query-type-result > div.accordion-header', + 1, + 1 + ); + + // Button debug elements + const btnDes = getAndExpectDebugElementByCss( + panelHeaderDes[0], + 'button#awg-graph-visualizer-unsupported-query-type-result-toggle', + 1, + 1 + ); + // Button native elements to click on + const btnEl = btnDes[0].nativeElement; + + // Panel body does not closed + let panelBodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-unsupported-query-type-result > div.accordion-collapse', + 1, + 1, + 'open' + ); + let panelBodyEl = panelBodyDes[0].nativeElement; + + expect(panelBodyEl.classList).toContain('show'); + + // Click header button + click(btnEl as HTMLElement); + detectChangesOnPush(fixture); + + // Panel is open + panelBodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-unsupported-query-type-result > div.accordion-collapse', + 1, + 1 + ); + panelBodyEl = panelBodyDes[0].nativeElement; + + expect(panelBodyEl.classList).toContain('show'); + }); + + it('... should contain panel body with two centered paragraphs', () => { + // Body debug elements + const panelBodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-unsupported-query-type-result > div.accordion-collapse', + 1, + 1 + ); + + // Panel body paragraphs + const pDes = getAndExpectDebugElementByCss(panelBodyDes[0], 'p', 2, 2); + + pDes.forEach((pDe: DebugElement) => { + const pEl = pDe.nativeElement; + expect(pEl).toBeTruthy(); + expect(pEl).toHaveClass('text-center'); + }); + }); + + it('... should display messages in panel body paragraphs', () => { + // Body debug elements + const panelBodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-unsupported-query-type-result > div.accordion-collapse', + 1, + 1 + ); + + // Panel body paragraphs + const pDes = getAndExpectDebugElementByCss(panelBodyDes[0], 'p', 2, 2); + + const pEl0 = pDes[0].nativeElement; + const pEl1 = pDes[1].nativeElement; + + expectToContain( + pEl0.textContent.trim(), `Sorry, but the requested SPARQL query type ${expectedQueryType.toUpperCase()} is not supported yet` ); + expectToContain(pEl1.textContent.trim(), 'Please try a CONSTRUCT or SELECT query instead.'); + }); + + it('... should display correct queryType in first paragraph if input changes', () => { + // Body debug elements + const panelBodyDes = getAndExpectDebugElementByCss( + compDe, + 'div#awg-graph-visualizer-unsupported-query-type-result > div.accordion-collapse', + 1, + 1 + ); + + // Panel body paragraphs + const pDes = getAndExpectDebugElementByCss(panelBodyDes[0], 'p', 2, 2); + + const pEl0 = pDes[0].nativeElement; + + expectToContain(pEl0.textContent, expectedQueryType.toUpperCase()); + + // DESCRIBE + let newQueryType = 'describe'; + component.queryType = newQueryType; + detectChangesOnPush(fixture); - expect(pEl1.textContent).toBeTruthy(); - expect(pEl1.textContent.trim()) - .withContext(`should be 'Please try a CONSTRUCT or SELECT query instead.'`) - .toBe('Please try a CONSTRUCT or SELECT query instead.'); + expectToContain(pEl0.textContent, newQueryType.toUpperCase()); + + // COUNT + newQueryType = 'count'; + component.queryType = newQueryType; + detectChangesOnPush(fixture); + + expectToContain(pEl0.textContent, newQueryType.toUpperCase()); + }); + }); + }); + + describe('#isAccordionItemDisabled()', () => { + it('... should have a method `isAccordionItemDisabled`', () => { + expect(component.isAccordionItemDisabled).toBeDefined(); }); - it('... should display correct queryType if input changes', () => { - // Panel body paragraphs - const pDes = getAndExpectDebugElementByCss( - compDe, - 'div#awg-graph-visualizer-unsupported-query-type-result > div.accordion-body > p', - 2, - 2 - ); - const pEl0 = pDes[0].nativeElement; - - expect(pEl0.textContent).toBeTruthy(); - expect(pEl0.textContent) - .withContext(`should contain ${expectedQueryType.toUpperCase()}`) - .toContain(expectedQueryType.toUpperCase()); - - // DESCRIBE - let newQueryType = 'describe'; - component.queryType = newQueryType; - detectChangesOnPush(fixture); - - expect(pEl0.textContent).toBeTruthy(); - expect(pEl0.textContent) - .withContext(`should contain ${newQueryType.toUpperCase()}`) - .toContain(newQueryType.toUpperCase()); - - // COUNT - newQueryType = 'count'; - component.queryType = newQueryType; - detectChangesOnPush(fixture); - - expect(pEl0.textContent) - .withContext(`should contain ${newQueryType.toUpperCase()}`) - .toContain(newQueryType.toUpperCase()); + it('... should be triggered from ngbAccordionItem', () => { + expectSpyCall(isAccordionItemDisabledSpy, 2); + }); + + it('... should return false if isFullscreen is false', () => { + expectToBe(component.isAccordionItemDisabled(), false); + }); + + it('... should return true if isFullscreen is true', () => { + // Set fullscreen flag to true + component.isFullscreen = true; + + expectToBe(component.isAccordionItemDisabled(), true); }); }); }); diff --git a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/unsupported-type-results/unsupported-type-results.component.ts b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/unsupported-type-results/unsupported-type-results.component.ts index 637f6170f4..fa1cf32aef 100644 --- a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/unsupported-type-results/unsupported-type-results.component.ts +++ b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-graph/graph-visualizer/unsupported-type-results/unsupported-type-results.component.ts @@ -20,4 +20,24 @@ export class UnsupportedTypeResultsComponent { */ @Input() queryType: string; + + /** + * Input variable: isFullscreen. + * + * It keeps a boolean flag if fullscreenMode is set. + */ + @Input() + isFullscreen: boolean; + + /** + * Public method: isAccordionItemDisabled. + * + * It returns a boolean flag if the accordion item should be disabled. + * It returns true if fullscreenMode is set, otherwise false. + * + * @returns {boolean} The boolean value of the comparison. + */ + isAccordionItemDisabled(): boolean { + return this.isFullscreen ? true : false; + } } diff --git a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-intro/edition-intro.component.html b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-intro/edition-intro.component.html index 89b3dd0535..1044c86879 100644 --- a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-intro/edition-intro.component.html +++ b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-intro/edition-intro.component.html @@ -4,36 +4,35 @@ -
    - -

    -
    - -

    - [Die Einleitung zum Editionskomplex - erscheint im Zusammenhang der - vollständigen Edition von {{ editionComplex.complexId.short }} in - {{ editionRouteConstants.EDITION.short }} {{ editionComplex.series.short }}/{{ - editionComplex.section.short - }}.] - -

    -
    -
    - - + @if (editionIntroData$ | async; as editionIntroData) { +
    + @if (utils.isNotEmptyArray(editionIntroData.intro[0].content)) { + @for (intro of editionIntroData.intro[0].content; track intro) { +

    + } + } @else { +

    + [Die Einleitung zum Editionskomplex + erscheint im Zusammenhang der + vollständigen Edition von {{ editionComplex.complexId.short }} in + {{ editionRouteConstants.EDITION.short }} {{ editionComplex.series.short }}/{{ + editionComplex.section.short + }}.] + +

    + } +
    + } @else { -
    -
    -
    - {{ errorObject }} + @if (errorObject) { +
    +
    +
    + {{ errorObject }} +
    -
    - + } + }
    diff --git a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-report/edition-report.component.html b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-report/edition-report.component.html index 315dfdaa0b..1f6f13de54 100644 --- a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-report/edition-report.component.html +++ b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-report/edition-report.component.html @@ -3,60 +3,86 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @if (editionReportData$ | async; as editionReportData) { +
    + +
    +
    + +
    +
    +
    + + @if (editionReportData[0]) { + + + } + +
    +
    +
    + +
    +
    + +
    +
    +
    + + @if (editionReportData[1]) { + + + } + +
    +
    +
    + +
    +
    + +
    +
    +
    + + @if (editionReportData[2]) { + + + } + +
    +
    +
    + +
    +
    + +
    +
    +
    + + @if (editionReportData[3]) { + + + } + +
    +
    +
    +
    + }
    diff --git a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-report/edition-report.component.spec.ts b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-report/edition-report.component.spec.ts index 7f4ccc01a3..13df324e41 100644 --- a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-report/edition-report.component.spec.ts +++ b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-report/edition-report.component.spec.ts @@ -232,9 +232,9 @@ describe('EditionReportComponent', () => { }); describe('VIEW', () => { - it('... should contain no ngb-accordion yet', () => { - // Ngb-accordion debug element - getAndExpectDebugElementByCss(compDe, 'ngb-accordion', 0, 0); + it('... should contain no div.accordion yet', () => { + // Div.accordion debug element + getAndExpectDebugElementByCss(compDe, 'div.accordion', 0, 0); }); it('... should not contain source list component (stubbed) yet', () => { @@ -290,9 +290,9 @@ describe('EditionReportComponent', () => { })); describe('VIEW', () => { - it('... should contain one ngb-accordion', () => { - // Ngb-accordion debug element - getAndExpectDebugElementByCss(compDe, 'ngb-accordion', 1, 1); + it('... should contain one div.accordion', () => { + // NgbAccordion debug element + getAndExpectDebugElementByCss(compDe, 'div.accordion', 1, 1); }); it('... should contain one source list component (stubbed)', () => { diff --git a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-report/source-description/source-description.component.html b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-report/source-description/source-description.component.html index 8e12cb2a7b..ed79e58f5e 100644 --- a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-report/source-description/source-description.component.html +++ b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-report/source-description/source-description.component.html @@ -1,296 +1,370 @@ -
    -
    -
    -
    -

    - {{ sourceDescription.siglum - }}{{ sourceDescription.siglumAddendum }} -

    -

    -

    -
    -
    - - -

    -
    - - -

    - Beschreibstoff:  - -

    - - - -

    - Schreibstoff:  - - - - - - - . -

    -
    - - -

    - Titel:  - -

    - - -

    - Datierung:  - -

    - - -

    - Paginierung:  - -

    - - -

    - Taktzahlen:  - -

    - - -

    - Instrumentenvorsatz:  - -

    - - -

    - Eintragungen:  - -

    - - -
    -

    Inhalt:

    -

    - - {{ content.item }}{{ content.item }} -  :
    -
    - - - - - - - - -   - - - Bl. {{ folio.folio.slice(0, -1) - }}{{ - folio.folio.slice(-1) - }} + @for (sourceDescription of sourceDescriptionListData.sources; track sourceDescription) { +

    +
    +
    + @if (sourceDescription.siglum) { +

    + {{ sourceDescription.siglum }} + @if (sourceDescription.siglumAddendum) { + {{ sourceDescription.siglumAddendum }} - {{ folio.folio }} - - - -   System {{ system }}:  - - - -   - - - - T. {{ measure }} - - {{ row.rowType }}{{ row.rowBase }} ({{ - row.rowNumber - }}). - ;
    -
    -
    -
    - -
    -
    - - -

    + } +

    + } + @if (sourceDescription.type) { +

    + } + @if (sourceDescription.location) { +

    + } +
    + @if (utils.isNotEmptyObject(sourceDescription.description)) { +
    + + @if (utils.isNotEmptyArray(sourceDescription.description.desc)) { + @for (description of sourceDescription.description.desc; track description) { +

    + } + } + + @if (sourceDescription.description.writingMaterial) { +

    + Beschreibstoff:  + +

    + } + + @if (utils.isNotEmptyObject(sourceDescription.description.writingInstruments)) { + @if (sourceDescription.description.writingInstruments.main) { +

    + Schreibstoff:  + + @if ( + utils.isNotEmptyArray( + sourceDescription.description.writingInstruments.secondary + ) + ) { + + @for ( + secondary of sourceDescription?.description?.writingInstruments + ?.secondary; + track secondary; + let last = $last + ) { + + + @if (!last) { + + } @else { + . + } + + } + } @else { + . + } +

    + } + } + + @if (sourceDescription.description.title) { +

    + Titel:  + +

    + } + + @if (sourceDescription.description.date) { +

    + Datierung:  + +

    + } + + @if (sourceDescription.description.pagination) { +

    + Paginierung:  + +

    + } + + @if (sourceDescription.description.measureNumbers) { +

    + Taktzahlen:  + +

    + } + + @if (sourceDescription.description.instrumentation) { +

    + Instrumentenvorsatz:  + +

    + } + + @if (sourceDescription.description.annotations) { +

    + Eintragungen:  + +

    + } + + @if (utils.isNotEmptyArray(sourceDescription.description.content)) { +
    +

    Inhalt:

    + @for (content of sourceDescription.description.content; track content) { +

    + + @if (content.item || content.itemDescription) { + + @if (content.itemLinkTo) { + {{ content.item }} + } + @if (!content.itemLinkTo) { + {{ content.item }} + } + @if (content.itemDescription) { +   + } + :
    +
    + } + + @if (utils.isNotEmptyArray(content.folios)) { + @for (folio of content.folios; track folio; let lastFolio = $last) { + @if (folio.folio) { + + @if (folio.folioLinkTo) { + + } + @if (!folio.folioLinkTo) { + + } + @if (folio.folioDescription) { +    + } + + } + + Bl.  + @if ( + folio.folio.endsWith('v') || folio.folio.endsWith('r') + ) { + {{ folio.folio.slice(0, -1) + }}{{ folio.folio.slice(-1) }} + } @else { + {{ folio.folio }} + } + + + + @if (utils.isNotEmptyArray(folio.systemGroups)) { + @for ( + systemGroup of folio.systemGroups; + track systemGroup; + let firstSystemGroup = $first; + let lastSystemGroup = $last + ) { + @for ( + system of systemGroup; + track system; + let i = $index; + let firstSystem = $first; + let lastSystem = $last + ) { + @if (system.system) { + + } +   System {{ + system + }}:  + @if (system.systemDescription) { + + + @if (system.measure) { +   + } + + } + @if (system.measure) { + + @if (system.linkTo) { + + } + @if (!system.linkTo) { + + } + T. {{ measure }} + } + @if (utils.isNotEmptyObject(system.row)) { + + @if (system.linkTo) { + + + } + @if (!system.linkTo) { + + } + + {{ row.rowType + }}{{ row.rowBase }} ({{ + row.rowNumber + }}) + } + @if (lastFolio && lastSystemGroup && lastSystem) { + . + } @else { + ; + @if ((i + 1) % systemGroup.length === 0) { +
    + } +
    + } + } + } + } @else { + @if (!lastFolio) { +
    + } + } + } + } +

    + } +
    + } +
    + }
    -
    + }
    -
    +} diff --git a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-report/source-description/source-description.component.spec.ts b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-report/source-description/source-description.component.spec.ts index d45d805bf7..4f1f7cc677 100644 --- a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-report/source-description/source-description.component.spec.ts +++ b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-report/source-description/source-description.component.spec.ts @@ -182,19 +182,23 @@ describe('SourceDescriptionComponent (DONE)', () => { }); it('... the first paragraph displaying a siglum (bold) without an addendum', () => { - const expectedSiglum = - expectedSourceDescriptionListData.sources[0].siglum + - expectedSourceDescriptionListData.sources[0].siglumAddendum; + const expectedSiglum = expectedSourceDescriptionListData.sources[0].siglum; const divDes = getAndExpectDebugElementByCss(compDe, 'div.awg-source-description-head', 2, 2); const pDes = getAndExpectDebugElementByCss(divDes[0], 'p', 3, 3); - const pEl = pDes[0].nativeElement; - expect(pEl).toHaveClass('awg-source-description-siglum'); + const spanDes = getAndExpectDebugElementByCss(pDes[0], 'span', 1, 1); + const siglumDes = spanDes[0]; + const siglumEl = siglumDes.nativeElement; + + expect(pEl).toHaveClass('awg-source-description-siglum-container'); expect(pEl).toHaveClass('bold'); expectToBe(pEl.textContent.trim(), expectedSiglum.trim()); + + expect(siglumEl).toHaveClass('awg-source-description-siglum'); + expectToBe(siglumEl.textContent.trim(), expectedSiglum.trim()); }); it('... the second paragraph displaying the source type', () => { @@ -240,27 +244,30 @@ describe('SourceDescriptionComponent (DONE)', () => { }); it('... the first paragraph displaying a siglum (bold) with addendum', () => { - const expectedSiglum = - expectedSourceDescriptionListData.sources[1].siglum + - expectedSourceDescriptionListData.sources[1].siglumAddendum; + const expectedSiglum = expectedSourceDescriptionListData.sources[1].siglum; + const expectedAddendum = expectedSourceDescriptionListData.sources[1].siglumAddendum; const divDes = getAndExpectDebugElementByCss(compDe, 'div.awg-source-description-head', 2, 2); const pDes = getAndExpectDebugElementByCss(divDes[1], 'p', 2, 2); const pEl = pDes[0].nativeElement; - const addendumDes = getAndExpectDebugElementByCss(pDes[0], 'span', 1, 1); - const addendumEl = addendumDes[0].nativeElement; + const spanDes = getAndExpectDebugElementByCss(pDes[0], 'span', 2, 2); + const siglumDes = spanDes[0]; + const siglumEl = siglumDes.nativeElement; + + const addendumDes = spanDes[1]; + const addendumEl = addendumDes.nativeElement; - expect(pEl).toHaveClass('awg-source-description-siglum'); + expect(pEl).toHaveClass('awg-source-description-siglum-container'); expect(pEl).toHaveClass('bold'); - expectToBe(pEl.textContent.trim(), expectedSiglum.trim()); + expectToBe(pEl.textContent.trim(), expectedSiglum.trim() + expectedAddendum.trim()); + + expect(siglumEl).toHaveClass('awg-source-description-siglum'); + expectToBe(siglumEl.textContent.trim(), expectedSiglum.trim()); expect(addendumEl).toHaveClass('awg-source-description-siglum-addendum'); - expectToBe( - addendumEl.textContent.trim(), - expectedSourceDescriptionListData.sources[1].siglumAddendum - ); + expectToBe(addendumEl.textContent.trim(), expectedAddendum.trim()); }); it('... the second paragraph displaying the source location', () => { @@ -733,7 +740,7 @@ describe('SourceDescriptionComponent (DONE)', () => { // Process HTML expression of expected text content const expectedHtmlTextContent = mockDocument.createElement('a'); expectedHtmlTextContent.innerHTML = - 'Bl. 2v Test item 4 without item'; + 'Bl. 2v  Test item 4 without item'; expectToBe(folioEl.textContent.trim(), expectedHtmlTextContent.textContent.trim()); }); diff --git a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-report/source-evaluation/source-evaluation.component.html b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-report/source-evaluation/source-evaluation.component.html index fb26b2e59f..1c3822ef48 100644 --- a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-report/source-evaluation/source-evaluation.component.html +++ b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-report/source-evaluation/source-evaluation.component.html @@ -1,24 +1,22 @@ -
    -
    - -

    -
    - -

    - [Die Quellenbewertung zum Editionskomplex - erscheint im Zusammenhang der - vollständigen Edition von {{ editionComplex.complexId.short }} in - {{ editionRouteConstants.EDITION.short }} {{ editionComplex.series.short }}/{{ - editionComplex.section.short - }}.] - -

    -
    +@if (sourceEvaluationListData) { +
    +
    + @if (utils.isNotEmptyArray(sourceEvaluationListData.sources?.[0]?.content)) { + @for (evaluation of sourceEvaluationListData.sources?.[0]?.content; track evaluation) { +

    + } + } @else { +

    + [Die Quellenbewertung zum Editionskomplex + erscheint im Zusammenhang der + vollständigen Edition von {{ editionComplex.complexId.short }} in + {{ editionRouteConstants.EDITION.short }} {{ editionComplex.series.short }}/{{ + editionComplex.section.short + }}.] + +

    + } +
    -
    +} diff --git a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-report/source-list/source-list.component.html b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-report/source-list/source-list.component.html index a366354227..7d405a6f77 100644 --- a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-report/source-list/source-list.component.html +++ b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-report/source-list/source-list.component.html @@ -4,48 +4,65 @@ aria-label="Table for list of sources" class="table table-hover borderless awg-source-description-sources"> - - - - {{ source.siglum - }}{{ source.siglumAddendum }} - - - {{ source.siglum - }}{{ source.siglumAddendum }} - - - -
    - - - - - - - - - - - - - - + @for (source of sourceListData.sources; track source; let sourceIndex = $index) { + + + + + }
    Zum vertonten Text:
    - {{ text.siglum }}{{ text.siglumAddendum }} - -
    - -
    + @if (source.hasDescription === true) { + {{ source.siglum }} + @if (source.siglumAddendum) { + + {{ source.siglumAddendum }} + + } + + } @else { + {{ source.siglum }} + @if (source.siglumAddendum) { + + {{ source.siglumAddendum }} + + } + + } + +
    + +
    + @if (utils.isNotEmptyArray(sourceListData.textSources)) { + + + + + + @for (text of sourceListData.textSources; track text; let textIndex = $index) { + + + + + } + +
    Zum vertonten Text:
    + {{ text.siglum }} + @if (text.siglumAddendum) { + {{ text.siglumAddendum }} + } + + +
    + +
    + }
    diff --git a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-report/source-list/source-list.component.spec.ts b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-report/source-list/source-list.component.spec.ts index 68dbe462b1..34bc9b744b 100644 --- a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-report/source-list/source-list.component.spec.ts +++ b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-report/source-list/source-list.component.spec.ts @@ -4,6 +4,7 @@ import { RouterTestingModule } from '@angular/router/testing'; import Spy = jasmine.Spy; import { clickAndAwaitChanges } from '@testing/click-helper'; +import { detectChangesOnPush } from '@testing/detect-changes-on-push-helper'; import { expectSpyCall, expectToBe, expectToEqual, getAndExpectDebugElementByCss } from '@testing/expect-helper'; import { mockEditionData } from '@testing/mock-data'; import { RouterLinkStubDirective } from '@testing/router-stubs'; @@ -11,7 +12,6 @@ import { RouterLinkStubDirective } from '@testing/router-stubs'; import { CompileHtmlComponent } from '@awg-shared/compile-html'; import { SourceList } from '@awg-views/edition-view/models'; -import { detectChangesOnPush } from '@testing/detect-changes-on-push-helper'; import { SourceListComponent } from './source-list.component'; describe('SourceListComponent (DONE)', () => { @@ -132,9 +132,16 @@ describe('SourceListComponent (DONE)', () => { const anchorDes = getAndExpectDebugElementByCss(columnDes[0], 'a', 1, 1); const anchorEl = anchorDes[0].nativeElement; + const spanDes = getAndExpectDebugElementByCss(anchorDes[0], 'span', 1, 1); + + const siglumDes = spanDes[0]; + const siglumEl = siglumDes.nativeElement; + const expectedSiglum = expectedSourceListData.sources[index].siglum; - expectToBe(anchorEl.textContent, expectedSiglum); + expectToBe(anchorEl.textContent.trim(), expectedSiglum.trim()); + expectToBe(siglumEl.textContent.trim(), expectedSiglum.trim()); + expect(siglumEl).toHaveClass('awg-source-list-siglum'); }); }); @@ -160,11 +167,24 @@ describe('SourceListComponent (DONE)', () => { const anchorDes = getAndExpectDebugElementByCss(columnDes[0], 'a', 1, 1); const anchorEl = anchorDes[0].nativeElement; - const expectedSiglum = - expectedSourceListData.sources[index].siglum + - expectedSourceListData.sources[index].siglumAddendum; + const spanDes = getAndExpectDebugElementByCss(anchorDes[0], 'span', 2, 2); + + const siglumDes = spanDes[0]; + const siglumEl = siglumDes.nativeElement; - expectToBe(anchorEl.textContent, expectedSiglum); + const siglumAddendumDes = spanDes[1]; + const siglumAddendumEl = siglumAddendumDes.nativeElement; + + const expectedSiglum = expectedSourceListData.sources[index].siglum; + const expectedAddendum = expectedSourceListData.sources[index].siglumAddendum; + + expectToBe(anchorEl.textContent.trim(), expectedSiglum.trim() + expectedAddendum.trim()); + + expectToBe(siglumEl.textContent.trim(), expectedSiglum.trim()); + expect(siglumEl).toHaveClass('awg-source-list-siglum'); + + expectToBe(siglumAddendumEl.textContent.trim(), expectedAddendum.trim()); + expect(siglumAddendumEl).toHaveClass('awg-source-list-siglum-addendum'); }); }); @@ -265,7 +285,7 @@ describe('SourceListComponent (DONE)', () => { }); }); - it('... should contain text siglum span in header column (th)', () => { + it('... should contain text siglum container span in header column (th)', () => { const expectedSourcesLength = expectedSourceListData.textSources.length + 1; const tableBodyDes = getAndExpectDebugElementByCss(compDe, 'table > tbody', 2, 2); @@ -280,20 +300,32 @@ describe('SourceListComponent (DONE)', () => { if (index === 0) { return; } + const columnDes = getAndExpectDebugElementByCss(rowDe, 'th', 1, 1); - const spanDes = getAndExpectDebugElementByCss(columnDes[0], 'span', 1, 1); - const spanEl = spanDes[0].nativeElement; + const containerDes = getAndExpectDebugElementByCss( + columnDes[0], + 'span.awg-source-list-text-siglum-container', + 1, + 1 + ); + const containerEl = containerDes[0].nativeElement; const expectedSiglum = expectedSourceListData.textSources[index - 1].siglum + expectedSourceListData.textSources[index - 1].siglumAddendum; - expectToBe(spanEl.textContent, expectedSiglum); + expectToBe(containerEl.textContent.trim(), expectedSiglum.trim()); }); }); - it('... should display text siglum addendum if present in header column (th)', () => { + it('... should display text siglum and siglum addendum if present in header column (th)', () => { + expectedSourceListData.textSources[0].siglumAddendum = 'a'; + expectedSourceListData.textSources[1].siglumAddendum = 'H'; + + component.sourceListData = expectedSourceListData; + detectChangesOnPush(fixture); + const expectedSourcesLength = expectedSourceListData.textSources.length + 1; const tableBodyDes = getAndExpectDebugElementByCss(compDe, 'table > tbody', 2, 2); @@ -310,14 +342,31 @@ describe('SourceListComponent (DONE)', () => { } const columnDes = getAndExpectDebugElementByCss(rowDe, 'th', 1, 1); - const spanDes = getAndExpectDebugElementByCss(columnDes[0], 'span', 1, 1); - const spanEl = spanDes[0].nativeElement; + const containerDes = getAndExpectDebugElementByCss( + columnDes[0], + 'span.awg-source-list-text-siglum-container', + 1, + 1 + ); + const containerEl = containerDes[0].nativeElement; + const spanDes = getAndExpectDebugElementByCss(containerDes[0], 'span', 2, 2); - const expectedSiglum = - expectedSourceListData.textSources[index - 1].siglum + - expectedSourceListData.textSources[index - 1].siglumAddendum; + const siglumDes = spanDes[0]; + const siglumEl = siglumDes.nativeElement; + + const siglumAddendumDes = spanDes[1]; + const siglumAddendumEl = siglumAddendumDes.nativeElement; + + const expectedSiglum = expectedSourceListData.textSources[index - 1].siglum; + const expectedAddendum = expectedSourceListData.textSources[index - 1].siglumAddendum; + + expectToBe(containerEl.textContent.trim(), expectedSiglum.trim() + expectedAddendum.trim()); + + expect(siglumEl).toHaveClass('awg-source-list-text-siglum'); + expectToBe(siglumEl.textContent.trim(), expectedSiglum.trim()); - expectToBe(spanEl.textContent, expectedSiglum); + expect(siglumAddendumEl).toHaveClass('awg-source-list-text-siglum-addendum'); + expectToBe(siglumAddendumEl.textContent.trim(), expectedAddendum.trim()); }); }); diff --git a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-report/textcritics-list/textcritics-list.component.html b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-report/textcritics-list/textcritics-list.component.html index eb246875e9..2412a38d77 100644 --- a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-report/textcritics-list/textcritics-list.component.html +++ b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-report/textcritics-list/textcritics-list.component.html @@ -1,31 +1,42 @@ - - - -
    - - +@if (textcriticsData) { +
    + @for (textcritic of textcriticsData.textcritics; track textcritic) { +
    +
    + + +
    +
    +
    + + @if (utils.isNotEmptyArray(textcritic.description)) { +
    +

    Skizzenkommentar:

    + @for (description of textcritic.description; track description) { +

    + } +
    + } + @if (utils.isNotEmptyArray(textcritic.comments)) { +
    +

    Textkritischer Kommentar:

    + + +
    + } +
    +
    +
    - - -
    -

    Skizzenkommentar:

    -

    -
    -
    -

    Textkritischer Kommentar:

    - - -
    -
    - - + } +
    +} diff --git a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-report/textcritics-list/textcritics-list.component.spec.ts b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-report/textcritics-list/textcritics-list.component.spec.ts index bf7455344f..d806db7b4e 100644 --- a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-report/textcritics-list/textcritics-list.component.spec.ts +++ b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-report/textcritics-list/textcritics-list.component.spec.ts @@ -9,6 +9,7 @@ import { detectChangesOnPush } from '@testing/detect-changes-on-push-helper'; import { expectSpyCall, expectToBe, + expectToContain, expectToEqual, getAndExpectDebugElementByCss, getAndExpectDebugElementByDirective, @@ -115,9 +116,9 @@ describe('TextcriticsListComponent (DONE)', () => { }); describe('VIEW', () => { - it('... should contain no ngb-accordion yet', () => { - // Ngb-accordion debug element - getAndExpectDebugElementByCss(compDe, 'ngb-accordion', 0, 0); + it('... should contain no div.accordion yet', () => { + // Div.accordion debug element + getAndExpectDebugElementByCss(compDe, 'div.accordion', 0, 0); }); }); }); @@ -136,64 +137,71 @@ describe('TextcriticsListComponent (DONE)', () => { }); describe('VIEW', () => { - it('... should contain one ngb-accordion with two panels (div.accordion-item)', () => { - // Ngb-accordion debug element - const accordionDes = getAndExpectDebugElementByCss(compDe, 'ngb-accordion', 1, 1); + it('... should contain one div.accordion', () => { + // NgbAccordion debug element + getAndExpectDebugElementByCss(compDe, 'div.accordion', 1, 1); + }); + + it('... should contain two items in div.accordion', () => { + // NgbAccordion debug element + const accordionDes = getAndExpectDebugElementByCss(compDe, 'div.accordion', 1, 1); - // Panel + // Div.accordion-item getAndExpectDebugElementByCss(accordionDes[0], 'div.accordion-item', 2, 2); }); - it('... should contain panel header with collapsed body', () => { - // Ngb-accordion debug element - const accordionDes = getAndExpectDebugElementByCss(compDe, 'ngb-accordion', 1, 1); - - // Panel (div.card) - const panelDes = getAndExpectDebugElementByCss(accordionDes[0], 'div.accordion-item', 2, 2); + it('... should contain item header with collapsed body', () => { + // Div.accordion-item + const itemDes = getAndExpectDebugElementByCss(compDe, 'div.accordion-item', 2, 2); - // Header + // Header (div.accordion-header) getAndExpectDebugElementByCss( - panelDes[0], - `div#${expectedTextcriticsData.textcritics[0].id}-header.accordion-header`, + itemDes[0], + `div#${expectedTextcriticsData.textcritics[0].id} > div.accordion-header`, 1, 1 ); getAndExpectDebugElementByCss( - panelDes[1], - `div#${expectedTextcriticsData.textcritics[1].id}-header.accordion-header`, + itemDes[1], + `div#${expectedTextcriticsData.textcritics[1].id} > div.accordion-header`, 1, 1 ); - // Body - getAndExpectDebugElementByCss( - panelDes[0], - `div#${expectedTextcriticsData.textcritics[0].id} > div.accordion-body`, - 0, - 0 + // Body closed (div.accordion-collapse) + const itemBodyDes1 = getAndExpectDebugElementByCss( + itemDes[0], + `div#${expectedTextcriticsData.textcritics[0].id} > div.accordion-collapse`, + 1, + 1 ); - getAndExpectDebugElementByCss( - panelDes[1], - `div#${expectedTextcriticsData.textcritics[1].id} > div.accordion-body`, - 0, - 0 + const itemBodyDes2 = getAndExpectDebugElementByCss( + itemDes[1], + `div#${expectedTextcriticsData.textcritics[1].id} > div.accordion-collapse`, + 1, + 1 ); + const itemBodyEl1 = itemBodyDes1[0].nativeElement; + const itemBodyEl2 = itemBodyDes2[0].nativeElement; + + expectToContain(itemBodyEl1.classList, 'collapse'); + expectToContain(itemBodyEl2.classList, 'collapse'); }); - it('... should contain div and two buttons in header section (div.accordion-header)', () => { - // Ngb-accordion panel debug element - const panelDes = getAndExpectDebugElementByCss(compDe, 'ngb-accordion > div.accordion-item', 2, 2); + it('... should display item header buttons', () => { + // Div.accordion-item + const itemDes = getAndExpectDebugElementByCss(compDe, 'div.accordion-item', 2, 2); - // Header + // Header (div.accordion-header) const header0Des = getAndExpectDebugElementByCss( - panelDes[0], - `div#${expectedTextcriticsData.textcritics[0].id}-header.accordion-header`, + itemDes[0], + `div#${expectedTextcriticsData.textcritics[0].id} > div.accordion-header`, 1, 1 ); const header1Des = getAndExpectDebugElementByCss( - panelDes[1], - `div#${expectedTextcriticsData.textcritics[1].id}-header.accordion-header`, + itemDes[1], + `div#${expectedTextcriticsData.textcritics[1].id} > div.accordion-header`, 1, 1 ); @@ -234,14 +242,14 @@ describe('TextcriticsListComponent (DONE)', () => { expectToBe(buttonEl11.textContent.trim(), expectedButtonLabelGeneric); }); - it('... should toggle first panel body on click on first header', () => { - // Ngb-accordion panel debug element - const panelDes = getAndExpectDebugElementByCss(compDe, 'ngb-accordion > div.accordion-item', 2, 2); + it('... should toggle first item body on click on first header', () => { + // Div.accordion-item + const itemDes = getAndExpectDebugElementByCss(compDe, 'div.accordion-item', 2, 2); // Header const header0Des = getAndExpectDebugElementByCss( - panelDes[0], - `div#${expectedTextcriticsData.textcritics[0].id}-header.accordion-header`, + itemDes[0], + `div#${expectedTextcriticsData.textcritics[0].id} > div.accordion-header`, 1, 1 ); @@ -250,48 +258,59 @@ describe('TextcriticsListComponent (DONE)', () => { const btnDes = getAndExpectDebugElementByCss(header0Des[0], 'div.accordion-button > button.btn', 2, 2); const btnEl = btnDes[0].nativeElement; - // Panel body is closed - getAndExpectDebugElementByCss( - panelDes[0], - `div#${expectedTextcriticsData.textcritics[0].id} > div.accordion-body`, - 0, - 0, + // Item body is closed + let itemBodyDes = getAndExpectDebugElementByCss( + itemDes[0], + `div#${expectedTextcriticsData.textcritics[0].id} > div.accordion-collapse`, + 1, + 1, 'collapsed' ); + let itemBodyEl = itemBodyDes[0].nativeElement; + + expectToContain(itemBodyEl.classList, 'collapse'); // Click header button click(btnEl as HTMLElement); detectChangesOnPush(fixture); - getAndExpectDebugElementByCss( - panelDes[0], - `div#${expectedTextcriticsData.textcritics[0].id} > div.accordion-body`, + // Item body is open + itemBodyDes = getAndExpectDebugElementByCss( + itemDes[0], + `div#${expectedTextcriticsData.textcritics[0].id} > div.accordion-collapse`, 1, 1, 'open' ); + itemBodyEl = itemBodyDes[0].nativeElement; + + expectToContain(itemBodyEl.classList, 'show'); // Click header button click(btnEl as HTMLElement); detectChangesOnPush(fixture); - getAndExpectDebugElementByCss( - panelDes[0], - `div#${expectedTextcriticsData.textcritics[0].id} > div.accordion-body`, - 0, - 0, + // Item body is closed + itemBodyDes = getAndExpectDebugElementByCss( + itemDes[0], + `div#${expectedTextcriticsData.textcritics[0].id} > div.accordion-collapse`, + 1, + 1, 'collapsed' ); + itemBodyEl = itemBodyDes[0].nativeElement; + + expectToContain(itemBodyEl.classList, 'collapse'); }); - it('... should toggle second panel body on click on second header', () => { - // Ngb-accordion panel debug element - const panelDes = getAndExpectDebugElementByCss(compDe, 'ngb-accordion > div.accordion-item', 2, 2); + it('... should toggle second item body on click on second header', () => { + // Div.accordion-item + const itemDes = getAndExpectDebugElementByCss(compDe, 'div.accordion-item', 2, 2); - // Header + // Header (div.accordion-header) const header1Des = getAndExpectDebugElementByCss( - panelDes[1], - `div#${expectedTextcriticsData.textcritics[1].id}-header.accordion-header`, + itemDes[1], + `div#${expectedTextcriticsData.textcritics[1].id} > div.accordion-header`, 1, 1 ); @@ -300,38 +319,49 @@ describe('TextcriticsListComponent (DONE)', () => { const btnDes = getAndExpectDebugElementByCss(header1Des[0], 'div.accordion-button > button.btn', 2, 2); const btnEl = btnDes[0].nativeElement; - // Panel body is closed - getAndExpectDebugElementByCss( - panelDes[1], - `div#${expectedTextcriticsData.textcritics[1].id} > div.accordion-body`, - 0, - 0, + // Item body is closed + let itemBodyDes = getAndExpectDebugElementByCss( + itemDes[1], + `div#${expectedTextcriticsData.textcritics[1].id} > div.accordion-collapse`, + 1, + 1, 'collapsed' ); + let itemBodyEl = itemBodyDes[0].nativeElement; + + expectToContain(itemBodyEl.classList, 'collapse'); // Click header button click(btnEl as HTMLElement); detectChangesOnPush(fixture); - getAndExpectDebugElementByCss( - panelDes[1], - `div#${expectedTextcriticsData.textcritics[1].id} > div.accordion-body`, + // Item body is open + itemBodyDes = getAndExpectDebugElementByCss( + itemDes[1], + `div#${expectedTextcriticsData.textcritics[1].id} > div.accordion-collapse`, 1, 1, 'open' ); + itemBodyEl = itemBodyDes[0].nativeElement; + + expectToContain(itemBodyEl.classList, 'show'); // Click header button click(btnEl as HTMLElement); detectChangesOnPush(fixture); - getAndExpectDebugElementByCss( - panelDes[1], - `div#${expectedTextcriticsData.textcritics[1].id} > div.accordion-body`, - 0, - 0, + // Item body is closed + itemBodyDes = getAndExpectDebugElementByCss( + itemDes[1], + `div#${expectedTextcriticsData.textcritics[1].id} > div.accordion-collapse`, + 1, + 1, 'collapsed' ); + itemBodyEl = itemBodyDes[0].nativeElement; + + expectToContain(itemBodyEl.classList, 'collapse'); }); describe('... with open body', () => { @@ -339,13 +369,13 @@ describe('TextcriticsListComponent (DONE)', () => { // Open bodies const header0Des = getAndExpectDebugElementByCss( compDe, - `div#${expectedTextcriticsData.textcritics[0].id}-header.accordion-header`, + `div#${expectedTextcriticsData.textcritics[0].id} > div.accordion-header`, 1, 1 ); const header1Des = getAndExpectDebugElementByCss( compDe, - `div#${expectedTextcriticsData.textcritics[1].id}-header.accordion-header`, + `div#${expectedTextcriticsData.textcritics[1].id} > div.accordion-header`, 1, 1 ); @@ -372,11 +402,11 @@ describe('TextcriticsListComponent (DONE)', () => { detectChangesOnPush(fixture); }); - it('... should contain no panel body with div and paragraphs if description array is empty', () => { + it('... should contain no item body with div and paragraphs if description array is empty', () => { const textcritics = expectedTextcriticsData.textcritics[0]; const bodyDes = getAndExpectDebugElementByCss( compDe, - `div#${textcritics.id} > div.accordion-body`, + `div#${textcritics.id} > div.accordion-collapse > div.accordion-body`, 1, 1, 'open' @@ -385,11 +415,11 @@ describe('TextcriticsListComponent (DONE)', () => { getAndExpectDebugElementByCss(bodyDes[0], 'div', 0, 0); }); - it('... should contain panel body with as many paragraphs in first div as textcritics.description if description array is not empty', () => { + it('... should contain item body with as many paragraphs in first div as textcritics.description if description array is not empty', () => { const textcritics = expectedTextcriticsData.textcritics[1]; const bodyDes = getAndExpectDebugElementByCss( compDe, - `div#${textcritics.id} > div.accordion-body`, + `div#${textcritics.id} > div.accordion-collapse > div.accordion-body`, 1, 1, 'open' @@ -418,11 +448,11 @@ describe('TextcriticsListComponent (DONE)', () => { }); }); - it('... should contain panel body with div, paragraph and EditionTkaTableComponent if comments array is not empty', () => { + it('... should contain item body with div, paragraph and EditionTkaTableComponent if comments array is not empty', () => { const textcritics = expectedTextcriticsData.textcritics[1]; const bodyDes = getAndExpectDebugElementByCss( compDe, - `div#${textcritics.id} > div.accordion-body`, + `div#${textcritics.id} > div.accordion-collapse > div.accordion-body`, 1, 1, 'open' @@ -464,10 +494,10 @@ describe('TextcriticsListComponent (DONE)', () => { }); it('... should trigger on event from EditionTkaTableComponent', () => { - // Open second panel + // Open second item const header1Des = getAndExpectDebugElementByCss( compDe, - `div#${expectedTextcriticsData.textcritics[1].id}-header.accordion-header`, + `div#${expectedTextcriticsData.textcritics[1].id} > div.accordion-header`, 1, 1 ); @@ -531,10 +561,10 @@ describe('TextcriticsListComponent (DONE)', () => { }); it('... should trigger on event from EditionTkaTableComponent', () => { - // Open second panel + // Open second item const header1Des = getAndExpectDebugElementByCss( compDe, - `div#${expectedTextcriticsData.textcritics[1].id}-header.accordion-header`, + `div#${expectedTextcriticsData.textcritics[1].id} > div.accordion-header`, 1, 1 ); @@ -594,10 +624,10 @@ describe('TextcriticsListComponent (DONE)', () => { }); it('... should trigger on event from EditionTkaTableComponent', () => { - // Open second panel + // Open second item const header1Des = getAndExpectDebugElementByCss( compDe, - `div#${expectedTextcriticsData.textcritics[1].id}-header.accordion-header`, + `div#${expectedTextcriticsData.textcritics[1].id} > div.accordion-header`, 1, 1 ); diff --git a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-sheets/edition-accolade/edition-accolade.component.html b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-sheets/edition-accolade/edition-accolade.component.html index e603992de0..9c4a097257 100644 --- a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-sheets/edition-accolade/edition-accolade.component.html +++ b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-sheets/edition-accolade/edition-accolade.component.html @@ -1,46 +1,51 @@ - - - -
    - - -
    -
    - -
    -
    - - - -
    -
    - - - +
    +
    +
    + + +
    +
    +
    + +
    +
    + + @if (svgSheetsData && selectedSvgSheet) { + + + } +
    +
    + + @if (selectedSvgSheet) { + + + } - - -
    + + @if (selectedSvgSheet && selectedTextcritics) { + + } +
    +
    +
    - - - +
    +
    +
    diff --git a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-sheets/edition-accolade/edition-accolade.component.spec.ts b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-sheets/edition-accolade/edition-accolade.component.spec.ts index 574bd88b4c..71783f2671 100644 --- a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-sheets/edition-accolade/edition-accolade.component.spec.ts +++ b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-sheets/edition-accolade/edition-accolade.component.spec.ts @@ -192,12 +192,9 @@ describe('EditionAccoladeComponent (DONE)', () => { }); describe('VIEW', () => { - it('... should contain one ngb-accordion without panel (div.accordion-item) yet', () => { - // Ngb-accordion debug element - const accordionDes = getAndExpectDebugElementByCss(compDe, 'ngb-accordion', 1, 1); - - // Panel - getAndExpectDebugElementByCss(accordionDes[0], 'div.accordion-item', 0, 0, 'yet'); + it('... should contain no div.accordion yet', () => { + // Div.accordion debug element + getAndExpectDebugElementByCss(compDe, 'div.accordion', 0, 0); }); }); }); @@ -236,22 +233,27 @@ describe('EditionAccoladeComponent (DONE)', () => { }); describe('VIEW', () => { - it('... should contain one ngb-accordion with one panel (div.accordion-item)', () => { - // Ngb-accordion debug element - const accordionDes = getAndExpectDebugElementByCss(compDe, 'ngb-accordion', 1, 1); + it('... should contain one div.accordion', () => { + // NgbAccordion debug element + getAndExpectDebugElementByCss(compDe, 'div.accordion', 1, 1); + }); - // Panel + it('... should contain one item in div.accordion', () => { + // NgbAccordion debug element + const accordionDes = getAndExpectDebugElementByCss(compDe, 'div.accordion', 1, 1); + + // Div.accordion-item getAndExpectDebugElementByCss(accordionDes[0], 'div.accordion-item', 1, 1); }); it('... should contain header section with div and two buttons (div.accordion-header)', () => { - // Ngb-accordion panel debug element - const panelDes = getAndExpectDebugElementByCss(compDe, 'ngb-accordion > div.accordion-item', 1, 1); + // Div.accordion-item + const itemDes = getAndExpectDebugElementByCss(compDe, 'div.accordion-item', 1, 1); // Header const headerDes = getAndExpectDebugElementByCss( - panelDes[0], - 'div#awg-accolade-view-header.accordion-header', + itemDes[0], + 'div#awg-accolade-view > div.accordion-header', 1, 1 ); @@ -274,10 +276,10 @@ describe('EditionAccoladeComponent (DONE)', () => { }); describe('EditionSvgSheetNavComponent', () => { - it('... should contain one EditionSvgSheetNavComponent (stubbed) in the panel body (div.accordion-body)', () => { - // Ngb-accordion panel debug element - const panelDes = getAndExpectDebugElementByCss(compDe, 'ngb-accordion > div.accordion-item', 1, 1); - const bodyDes = getAndExpectDebugElementByCss(panelDes[0], 'div.accordion-body', 1, 1); + it('... should contain one EditionSvgSheetNavComponent (stubbed) in the item body (div.accordion-body)', () => { + // Div.accordion-item + const itemDes = getAndExpectDebugElementByCss(compDe, 'div.accordion-item', 1, 1); + const bodyDes = getAndExpectDebugElementByCss(itemDes[0], 'div.accordion-body', 1, 1); getAndExpectDebugElementByDirective(bodyDes[0], EditionSvgSheetNavStubComponent, 1, 1); }); @@ -312,10 +314,10 @@ describe('EditionAccoladeComponent (DONE)', () => { }); describe('EditionSvgSheetViewerComponent', () => { - it('... should contain one EditionSvgSheetViewerComponent (stubbed) in the panel body (div.accordion-body)', () => { - // Ngb-accordion panel debug element - const panelDes = getAndExpectDebugElementByCss(compDe, 'ngb-accordion > div.accordion-item', 1, 1); - const bodyDes = getAndExpectDebugElementByCss(panelDes[0], 'div.accordion-body', 1, 1); + it('... should contain one EditionSvgSheetViewerComponent (stubbed) in the item body (div.accordion-body)', () => { + // Div.accordion-item + const itemDes = getAndExpectDebugElementByCss(compDe, 'div.accordion-item', 1, 1); + const bodyDes = getAndExpectDebugElementByCss(itemDes[0], 'div.accordion-body', 1, 1); getAndExpectDebugElementByDirective(bodyDes[0], EditionSvgSheetViewerStubComponent, 1, 1); }); @@ -336,10 +338,10 @@ describe('EditionAccoladeComponent (DONE)', () => { }); describe('EditionSvgSheetFooterComponent', () => { - it('... should contain one EditionSvgSheetFooterComponent (stubbed) in the panel body (div.accordion-body)', () => { - // Ngb-accordion panel debug element - const panelDes = getAndExpectDebugElementByCss(compDe, 'ngb-accordion > div.accordion-item', 1, 1); - const bodyDes = getAndExpectDebugElementByCss(panelDes[0], 'div.accordion-body', 1, 1); + it('... should contain one EditionSvgSheetFooterComponent (stubbed) in the item body (div.accordion-body)', () => { + // Div.accordion-item + const itemDes = getAndExpectDebugElementByCss(compDe, 'div.accordion-item', 1, 1); + const bodyDes = getAndExpectDebugElementByCss(itemDes[0], 'div.accordion-body', 1, 1); getAndExpectDebugElementByDirective(bodyDes[0], EditionSvgSheetFooterStubComponent, 1, 1); }); @@ -490,9 +492,17 @@ describe('EditionAccoladeComponent (DONE)', () => { it('... should trigger on click on header button', fakeAsync(() => { // Header - const buttonDes = getAndExpectDebugElementByCss( + const headerDes = getAndExpectDebugElementByCss( compDe, - 'div#awg-accolade-view-header.accordion-header > div.accordion-button > button', + 'div#awg-accolade-view > div.accordion-header', + 1, + 1 + ); + + // Header Buttons + const buttonDes = getAndExpectDebugElementByCss( + headerDes[0], + 'div.accordion-button > button.btn', 2, 2 ); diff --git a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-sheets/edition-accolade/edition-svg-sheet-footer/edition-svg-sheet-footer.component.html b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-sheets/edition-accolade/edition-svg-sheet-footer/edition-svg-sheet-footer.component.html index 4fa3fabd16..24441da807 100644 --- a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-sheets/edition-accolade/edition-svg-sheet-footer/edition-svg-sheet-footer.component.html +++ b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-sheets/edition-accolade/edition-svg-sheet-footer/edition-svg-sheet-footer.component.html @@ -1,36 +1,38 @@ diff --git a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-sheets/edition-accolade/edition-svg-sheet-nav/edition-svg-sheet-nav-item/edition-svg-sheet-nav-item.component.html b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-sheets/edition-accolade/edition-svg-sheet-nav/edition-svg-sheet-nav-item/edition-svg-sheet-nav-item.component.html index 4ff7c64d0a..bb5d2fadf0 100644 --- a/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-sheets/edition-accolade/edition-svg-sheet-nav/edition-svg-sheet-nav-item/edition-svg-sheet-nav-item.component.html +++ b/src/app/views/edition-view/edition-outlets/edition-complex/edition-detail/edition-sheets/edition-accolade/edition-svg-sheet-nav/edition-svg-sheet-nav-item/edition-svg-sheet-nav-item.component.html @@ -1,34 +1,45 @@ -
    {{ navItemLabel }}: ---
    - - - {{ svgSheet.label }} - -