diff --git a/.eslintrc.json b/.eslintrc.json index 50a9be3d59..4cc8c6dbc5 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -11,7 +11,13 @@ "eslint-plugin-jsonc", "eslint-plugin-rxjs", "eslint-plugin-simple-import-sort", - "eslint-plugin-import-newlines" + "eslint-plugin-import-newlines", + "eslint-plugin-jsonc", + "dspace-angular-ts", + "dspace-angular-html" + ], + "ignorePatterns": [ + "lint/test/fixture" ], "overrides": [ { @@ -21,7 +27,8 @@ "parserOptions": { "project": [ "./tsconfig.json", - "./cypress/tsconfig.json" + "./cypress/tsconfig.json", + "./lint/tsconfig.json" ], "createDefaultProgram": true }, @@ -38,7 +45,10 @@ "error", 2, { - "SwitchCase": 1 + "SwitchCase": 1, + "ignoredNodes": [ + "ClassBody.body > PropertyDefinition[decorators.length > 0] > .key" + ] } ], "max-classes-per-file": [ @@ -212,6 +222,15 @@ "@typescript-eslint/no-unsafe-return": "off", "@typescript-eslint/restrict-template-expressions": "off", "@typescript-eslint/require-await": "off", + "@typescript-eslint/no-base-to-string": [ + "error", + { + "ignoredTypeNames": [ + "ResourceType", + "Error" + ] + } + ], "deprecation/deprecation": "warn", @@ -238,7 +257,12 @@ "method" ], - "rxjs/no-nested-subscribe": "off" // todo: go over _all_ cases + "rxjs/no-nested-subscribe": "off", // todo: go over _all_ cases + + // Custom DSpace Angular rules + "dspace-angular-ts/themed-component-classes": "error", + "dspace-angular-ts/themed-component-selectors": "error", + "dspace-angular-ts/themed-component-usages": "error" } }, { @@ -253,7 +277,10 @@ "createDefaultProgram": true }, "rules": { - "prefer-const": "off" + "prefer-const": "off", + + // Custom DSpace Angular rules + "dspace-angular-ts/themed-component-usages": "error" } }, { @@ -262,7 +289,11 @@ ], "extends": [ "plugin:@angular-eslint/template/recommended" - ] + ], + "rules": { + // Custom DSpace Angular rules + "dspace-angular-html/themed-component-usages": "error" + } }, { "files": [ diff --git a/.gitattributes b/.gitattributes index 406640bfcc..b5ad93b1bc 100644 --- a/.gitattributes +++ b/.gitattributes @@ -13,4 +13,7 @@ *.css eol=lf *.scss eol=lf *.html eol=lf -*.svg eol=lf \ No newline at end of file +*.svg eol=lf + +# Generated documentation should have LF line endings to reduce git noise +docs/lint/**/*.md eol=lf \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f6ffa5e004..4f2a84ce8a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -85,8 +85,14 @@ jobs: - name: Install Yarn dependencies run: yarn install --frozen-lockfile + - name: Build lint plugins + run: yarn run build:lint + + - name: Run lint plugin tests + run: yarn run test:lint:nobuild + - name: Run lint - run: yarn run lint --quiet + run: yarn run lint:nobuild --quiet - name: Check for circular dependencies run: yarn run check-circ-deps diff --git a/angular.json b/angular.json index 98463fa732..5f0204249b 100644 --- a/angular.json +++ b/angular.json @@ -266,6 +266,8 @@ "options": { "lintFilePatterns": [ "src/**/*.ts", + "cypress/**/*.ts", + "lint/**/*.ts", "src/**/*.html", "src/**/*.json5" ] diff --git a/cypress/e2e/collection-create.cy.ts b/cypress/e2e/collection-create.cy.ts new file mode 100644 index 0000000000..29f7dd5cac --- /dev/null +++ b/cypress/e2e/collection-create.cy.ts @@ -0,0 +1,13 @@ +beforeEach(() => { + cy.visit('/collections/create?parent='.concat(Cypress.env('DSPACE_TEST_COMMUNITY'))); + cy.loginViaForm(Cypress.env('DSPACE_TEST_ADMIN_USER'), Cypress.env('DSPACE_TEST_ADMIN_PASSWORD')); +}); + +it('should show loading component while saving', () => { + const title = 'Test Collection Title'; + cy.get('#title').type(title); + + cy.get('button[type="submit"]').click(); + + cy.get('ds-loading').should('be.visible'); +}); diff --git a/cypress/e2e/community-create.cy.ts b/cypress/e2e/community-create.cy.ts new file mode 100644 index 0000000000..96bc003ba2 --- /dev/null +++ b/cypress/e2e/community-create.cy.ts @@ -0,0 +1,13 @@ +beforeEach(() => { + cy.visit('/communities/create'); + cy.loginViaForm(Cypress.env('DSPACE_TEST_ADMIN_USER'), Cypress.env('DSPACE_TEST_ADMIN_PASSWORD')); +}); + +it('should show loading component while saving', () => { + const title = 'Test Community Title'; + cy.get('#title').type(title); + + cy.get('button[type="submit"]').click(); + + cy.get('ds-loading').should('be.visible'); +}); diff --git a/cypress/e2e/login-modal.cy.ts b/cypress/e2e/login-modal.cy.ts index 190f3ff927..3d978dfaca 100644 --- a/cypress/e2e/login-modal.cy.ts +++ b/cypress/e2e/login-modal.cy.ts @@ -3,31 +3,31 @@ import { testA11y } from 'cypress/support/utils'; const page = { openLoginMenu() { // Click the "Log In" dropdown menu in header - cy.get('ds-themed-header [data-test="login-menu"]').click(); + cy.get('ds-header [data-test="login-menu"]').click(); }, openUserMenu() { // Once logged in, click the User menu in header - cy.get('ds-themed-header [data-test="user-menu"]').click(); + cy.get('ds-header [data-test="user-menu"]').click(); }, submitLoginAndPasswordByPressingButton(email, password) { // Enter email - cy.get('ds-themed-header [data-test="email"]').type(email); + cy.get('ds-header [data-test="email"]').type(email); // Enter password - cy.get('ds-themed-header [data-test="password"]').type(password); + cy.get('ds-header [data-test="password"]').type(password); // Click login button - cy.get('ds-themed-header [data-test="login-button"]').click(); + cy.get('ds-header [data-test="login-button"]').click(); }, submitLoginAndPasswordByPressingEnter(email, password) { // In opened Login modal, fill out email & password, then click Enter - cy.get('ds-themed-header [data-test="email"]').type(email); - cy.get('ds-themed-header [data-test="password"]').type(password); - cy.get('ds-themed-header [data-test="password"]').type('{enter}'); + cy.get('ds-header [data-test="email"]').type(email); + cy.get('ds-header [data-test="password"]').type(password); + cy.get('ds-header [data-test="password"]').type('{enter}'); }, submitLogoutByPressingButton() { // This is the POST command that will actually log us out cy.intercept('POST', '/server/api/authn/logout').as('logout'); // Click logout button - cy.get('ds-themed-header [data-test="logout-button"]').click(); + cy.get('ds-header [data-test="logout-button"]').click(); // Wait until above POST command responds before continuing // (This ensures next action waits until logout completes) cy.wait('@logout'); @@ -102,10 +102,10 @@ describe('Login Modal', () => { page.openLoginMenu(); // Registration link should be visible - cy.get('ds-themed-header [data-test="register"]').should('be.visible'); + cy.get('ds-header [data-test="register"]').should('be.visible'); // Click registration link & you should go to registration page - cy.get('ds-themed-header [data-test="register"]').click(); + cy.get('ds-header [data-test="register"]').click(); cy.location('pathname').should('eq', '/register'); cy.get('ds-register-email').should('exist'); @@ -119,10 +119,10 @@ describe('Login Modal', () => { page.openLoginMenu(); // Forgot password link should be visible - cy.get('ds-themed-header [data-test="forgot"]').should('be.visible'); + cy.get('ds-header [data-test="forgot"]').should('be.visible'); // Click link & you should go to Forgot Password page - cy.get('ds-themed-header [data-test="forgot"]').click(); + cy.get('ds-header [data-test="forgot"]').click(); cy.location('pathname').should('eq', '/forgot'); cy.get('ds-forgot-email').should('exist'); diff --git a/cypress/e2e/search-navbar.cy.ts b/cypress/e2e/search-navbar.cy.ts index b168219916..0613e5e712 100644 --- a/cypress/e2e/search-navbar.cy.ts +++ b/cypress/e2e/search-navbar.cy.ts @@ -1,15 +1,15 @@ const page = { fillOutQueryInNavBar(query) { // Click the magnifying glass - cy.get('ds-themed-header [data-test="header-search-icon"]').click(); + cy.get('ds-header [data-test="header-search-icon"]').click(); // Fill out a query in input that appears - cy.get('ds-themed-header [data-test="header-search-box"]').type(query); + cy.get('ds-header [data-test="header-search-box"]').type(query); }, submitQueryByPressingEnter() { - cy.get('ds-themed-header [data-test="header-search-box"]').type('{enter}'); + cy.get('ds-header [data-test="header-search-box"]').type('{enter}'); }, submitQueryByPressingIcon() { - cy.get('ds-themed-header [data-test="header-search-icon"]').click(); + cy.get('ds-header [data-test="header-search-icon"]').click(); }, }; diff --git a/docker/cli.assetstore.yml b/docker/cli.assetstore.yml index 31bc53f64d..98f7414861 100644 --- a/docker/cli.assetstore.yml +++ b/docker/cli.assetstore.yml @@ -12,8 +12,6 @@ # https://github.com/DSpace/DSpace/blob/main/dspace/src/main/docker-compose/cli.assetstore.yml # # Therefore, it should be kept in sync with that file -version: "3.7" - services: dspace-cli: environment: diff --git a/docker/cli.ingest.yml b/docker/cli.ingest.yml index 1db241af3b..cc3623298e 100644 --- a/docker/cli.ingest.yml +++ b/docker/cli.ingest.yml @@ -12,8 +12,6 @@ # https://github.com/DSpace/DSpace/blob/main/dspace/src/main/docker-compose/cli.ingest.yml # # Therefore, it should be kept in sync with that file -version: "3.7" - services: dspace-cli: environment: diff --git a/docker/cli.yml b/docker/cli.yml index cc266b186f..9b1973426f 100644 --- a/docker/cli.yml +++ b/docker/cli.yml @@ -12,7 +12,6 @@ # https://github.com/DSpace/DSpace/blob/main/docker-compose-cli.yml # # Therefore, it should be kept in sync with that file -version: "3.7" networks: # Default to using network named 'dspacenet' from docker-compose-rest.yml. # Its full name will be prepended with the project name (e.g. "-p d7" means it will be named "d7_dspacenet") diff --git a/docker/db.entities.yml b/docker/db.entities.yml index 6473bf2e38..5c0a15c8f6 100644 --- a/docker/db.entities.yml +++ b/docker/db.entities.yml @@ -12,8 +12,6 @@ # https://github.com/DSpace/DSpace/blob/main/dspace/src/main/docker-compose/db.entities.yml # # # Therefore, it should be kept in sync with that file -version: "3.7" - services: dspacedb: image: dspace/dspace-postgres-pgcrypto:loadsql diff --git a/docker/docker-compose-ci.yml b/docker/docker-compose-ci.yml index fef7330ba1..d78a05362e 100644 --- a/docker/docker-compose-ci.yml +++ b/docker/docker-compose-ci.yml @@ -10,7 +10,6 @@ # This is used by our GitHub CI at .github/workflows/build.yml # It is based heavily on the Backend's Docker Compose: # https://github.com/DSpace/DSpace/blob/main/docker-compose.yml -version: '3.7' networks: dspacenet: services: diff --git a/docker/docker-compose-dist.yml b/docker/docker-compose-dist.yml index 38278085cd..67eba16785 100644 --- a/docker/docker-compose-dist.yml +++ b/docker/docker-compose-dist.yml @@ -8,7 +8,6 @@ # Docker Compose for running the DSpace Angular UI dist build # for previewing with the DSpace Demo site backend -version: '3.7' networks: dspacenet: services: diff --git a/docker/docker-compose-rest.yml b/docker/docker-compose-rest.yml index 6267b32bbe..2ec4eccf20 100644 --- a/docker/docker-compose-rest.yml +++ b/docker/docker-compose-rest.yml @@ -10,7 +10,6 @@ # This is based heavily on the docker-compose.yml that is available in the DSpace/DSpace # (Backend) at: # https://github.com/DSpace/DSpace/blob/main/docker-compose.yml -version: '3.7' networks: dspacenet: ipam: diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 1071b8d6ce..1c268b84b7 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -9,7 +9,6 @@ # Docker Compose for running the DSpace Angular UI for testing/development # Requires also running a REST API backend (either locally or remotely), # for example via 'docker-compose-rest.yml' -version: '3.7' networks: dspacenet: services: diff --git a/docs/lint/html/index.md b/docs/lint/html/index.md new file mode 100644 index 0000000000..15d693843c --- /dev/null +++ b/docs/lint/html/index.md @@ -0,0 +1,4 @@ +[DSpace ESLint plugins](../../../lint/README.md) > HTML rules +_______ + +- [`dspace-angular-html/themed-component-usages`](./rules/themed-component-usages.md): Themeable components should be used via the selector of their `ThemedComponent` wrapper class diff --git a/docs/lint/html/rules/themed-component-usages.md b/docs/lint/html/rules/themed-component-usages.md new file mode 100644 index 0000000000..a04fe1c770 --- /dev/null +++ b/docs/lint/html/rules/themed-component-usages.md @@ -0,0 +1,110 @@ +[DSpace ESLint plugins](../../../../lint/README.md) > [HTML rules](../index.md) > `dspace-angular-html/themed-component-usages` +_______ + +Themeable components should be used via the selector of their `ThemedComponent` wrapper class + +This ensures that custom themes can correctly override _all_ instances of this component. +The only exception to this rule are unit tests, where we may want to use the base component in order to keep the test setup simple. + + +_______ + +[Source code](../../../../lint/src/rules/html/themed-component-usages.ts) + +### Examples + + +#### Valid code + +##### use no-prefix selectors in HTML templates + +```html + + + +``` + +##### use no-prefix selectors in TypeScript templates + +```html +@Component({ + template: '' +}) +class Test { +} +``` + +##### use no-prefix selectors in TypeScript test templates + +Filename: `lint/test/fixture/src/test.spec.ts` + +```html +@Component({ + template: '' +}) +class Test { +} +``` + +##### base selectors are also allowed in TypeScript test templates + +Filename: `lint/test/fixture/src/test.spec.ts` + +```html +@Component({ + template: '' +}) +class Test { +} +``` + + + + +#### Invalid code & automatic fixes + +##### themed override selectors are not allowed in HTML templates + +```html + + + +``` +Will produce the following error(s): +``` +Themeable components should be used via their ThemedComponent wrapper's selector +Themeable components should be used via their ThemedComponent wrapper's selector +Themeable components should be used via their ThemedComponent wrapper's selector +``` + +Result of `yarn lint --fix`: +```html + + + +``` + + +##### base selectors are not allowed in HTML templates + +```html + + + +``` +Will produce the following error(s): +``` +Themeable components should be used via their ThemedComponent wrapper's selector +Themeable components should be used via their ThemedComponent wrapper's selector +Themeable components should be used via their ThemedComponent wrapper's selector +``` + +Result of `yarn lint --fix`: +```html + + + +``` + + + diff --git a/docs/lint/ts/index.md b/docs/lint/ts/index.md new file mode 100644 index 0000000000..ed060c946e --- /dev/null +++ b/docs/lint/ts/index.md @@ -0,0 +1,6 @@ +[DSpace ESLint plugins](../../../lint/README.md) > TypeScript rules +_______ + +- [`dspace-angular-ts/themed-component-classes`](./rules/themed-component-classes.md): Formatting rules for themeable component classes +- [`dspace-angular-ts/themed-component-selectors`](./rules/themed-component-selectors.md): Themeable component selectors should follow the DSpace convention +- [`dspace-angular-ts/themed-component-usages`](./rules/themed-component-usages.md): Themeable components should be used via their `ThemedComponent` wrapper class diff --git a/docs/lint/ts/rules/themed-component-classes.md b/docs/lint/ts/rules/themed-component-classes.md new file mode 100644 index 0000000000..1f4ec72801 --- /dev/null +++ b/docs/lint/ts/rules/themed-component-classes.md @@ -0,0 +1,257 @@ +[DSpace ESLint plugins](../../../../lint/README.md) > [TypeScript rules](../index.md) > `dspace-angular-ts/themed-component-classes` +_______ + +Formatting rules for themeable component classes + +- All themeable components must be standalone. +- The base component must always be imported in the `ThemedComponent` wrapper. This ensures that it is always sufficient to import just the wrapper whenever we use the component. + + +_______ + +[Source code](../../../../lint/src/rules/ts/themed-component-classes.ts) + +### Examples + + +#### Valid code + +##### Regular non-themeable component + +```typescript +@Component({ + selector: 'ds-something', + standalone: true, +}) +class Something { +} +``` + +##### Base component + +```typescript +@Component({ + selector: 'ds-base-test-themable', + standalone: true, +}) +class TestThemeableTomponent { +} +``` + +##### Wrapper component + +Filename: `lint/test/fixture/src/app/test/themed-test-themeable.component.ts` + +```typescript +@Component({ + selector: 'ds-test-themable', + standalone: true, + imports: [ + TestThemeableComponent, + ], +}) +class ThemedTestThemeableTomponent extends ThemedComponent { +} +``` + +##### Override component + +Filename: `lint/test/fixture/src/themes/test/app/test/test-themeable.component.ts` + +```typescript +@Component({ + selector: 'ds-themed-test-themable', + standalone: true, +}) +class Override extends BaseComponent { +} +``` + + + + +#### Invalid code & automatic fixes + +##### Base component must be standalone + +```typescript +@Component({ + selector: 'ds-base-test-themable', +}) +class TestThemeableComponent { +} +``` +Will produce the following error(s): +``` +Themeable components must be standalone +``` + +Result of `yarn lint --fix`: +```typescript +@Component({ + selector: 'ds-base-test-themable', + standalone: true, +}) +class TestThemeableComponent { +} +``` + + +##### Wrapper component must be standalone and import base component + +Filename: `lint/test/fixture/src/app/test/themed-test-themeable.component.ts` + +```typescript +@Component({ + selector: 'ds-test-themable', +}) +class ThemedTestThemeableComponent extends ThemedComponent { +} +``` +Will produce the following error(s): +``` +Themeable component wrapper classes must be standalone and import the base class +``` + +Result of `yarn lint --fix`: +```typescript +@Component({ + selector: 'ds-test-themable', + standalone: true, + imports: [TestThemeableComponent], +}) +class ThemedTestThemeableComponent extends ThemedComponent { +} +``` + + +##### Wrapper component must import base component (array present but empty) + +Filename: `lint/test/fixture/src/app/test/themed-test-themeable.component.ts` + +```typescript +@Component({ + selector: 'ds-test-themable', + standalone: true, + imports: [], +}) +class ThemedTestThemeableComponent extends ThemedComponent { +} +``` +Will produce the following error(s): +``` +Themed component wrapper classes must only import the base class +``` + +Result of `yarn lint --fix`: +```typescript +@Component({ + selector: 'ds-test-themable', + standalone: true, + imports: [TestThemeableComponent], +}) +class ThemedTestThemeableComponent extends ThemedComponent { +} +``` + + +##### Wrapper component must import base component (array is wrong) + +Filename: `lint/test/fixture/src/app/test/themed-test-themeable.component.ts` + +```typescript +import { SomethingElse } from './somewhere-else'; + +@Component({ + selector: 'ds-test-themable', + standalone: true, + imports: [ + SomethingElse, + ], +}) +class ThemedTestThemeableComponent extends ThemedComponent { +} +``` +Will produce the following error(s): +``` +Themed component wrapper classes must only import the base class +``` + +Result of `yarn lint --fix`: +```typescript +import { SomethingElse } from './somewhere-else'; + +@Component({ + selector: 'ds-test-themable', + standalone: true, + imports: [TestThemeableComponent], +}) +class ThemedTestThemeableComponent extends ThemedComponent { +} +``` + + +##### Wrapper component must import base component (array is wrong) + +Filename: `lint/test/fixture/src/app/test/themed-test-themeable.component.ts` + +```typescript +import { Something, SomethingElse } from './somewhere-else'; + +@Component({ + selector: 'ds-test-themable', + standalone: true, + imports: [ + SomethingElse, + ], +}) +class ThemedTestThemeableComponent extends ThemedComponent { +} +``` +Will produce the following error(s): +``` +Themed component wrapper classes must only import the base class +``` + +Result of `yarn lint --fix`: +```typescript +import { Something, SomethingElse } from './somewhere-else'; + +@Component({ + selector: 'ds-test-themable', + standalone: true, + imports: [TestThemeableComponent], +}) +class ThemedTestThemeableComponent extends ThemedComponent { +} +``` + + +##### Override component must be standalone + +Filename: `lint/test/fixture/src/themes/test/app/test/test-themeable.component.ts` + +```typescript +@Component({ + selector: 'ds-themed-test-themable', +}) +class Override extends BaseComponent { +} +``` +Will produce the following error(s): +``` +Themeable components must be standalone +``` + +Result of `yarn lint --fix`: +```typescript +@Component({ + selector: 'ds-themed-test-themable', + standalone: true, +}) +class Override extends BaseComponent { +} +``` + + + diff --git a/docs/lint/ts/rules/themed-component-selectors.md b/docs/lint/ts/rules/themed-component-selectors.md new file mode 100644 index 0000000000..f4d0ea177c --- /dev/null +++ b/docs/lint/ts/rules/themed-component-selectors.md @@ -0,0 +1,156 @@ +[DSpace ESLint plugins](../../../../lint/README.md) > [TypeScript rules](../index.md) > `dspace-angular-ts/themed-component-selectors` +_______ + +Themeable component selectors should follow the DSpace convention + +Each themeable component is comprised of a base component, a wrapper component and any number of themed components +- Base components should have a selector starting with `ds-base-` +- Themed components should have a selector starting with `ds-themed-` +- Wrapper components should have a selector starting with `ds-`, but not `ds-base-` or `ds-themed-` + - This is the regular DSpace selector prefix + - **When making a regular component themeable, its selector prefix should be changed to `ds-base-`, and the new wrapper's component should reuse the previous selector** + +Unit tests are exempt from this rule, because they may redefine components using the same class name as other themeable components elsewhere in the source. + + +_______ + +[Source code](../../../../lint/src/rules/ts/themed-component-selectors.ts) + +### Examples + + +#### Valid code + +##### Regular non-themeable component selector + +```typescript +@Component({ + selector: 'ds-something', +}) +class Something { +} +``` + +##### Themeable component selector should replace the original version, unthemed version should be changed to ds-base- + +```typescript +@Component({ + selector: 'ds-base-something', +}) +class Something { +} + +@Component({ + selector: 'ds-something', +}) +class ThemedSomething extends ThemedComponent { +} + +@Component({ + selector: 'ds-themed-something', +}) +class OverrideSomething extends Something { +} +``` + +##### Other themed component wrappers should not interfere + +```typescript +@Component({ + selector: 'ds-something', +}) +class Something { +} + +@Component({ + selector: 'ds-something-else', +}) +class ThemedSomethingElse extends ThemedComponent { +} +``` + + + + +#### Invalid code & automatic fixes + +##### Wrong selector for base component + +Filename: `lint/test/fixture/src/app/test/test-themeable.component.ts` + +```typescript +@Component({ + selector: 'ds-something', +}) +class TestThemeableComponent { +} +``` +Will produce the following error(s): +``` +Unthemed version of themeable component should have a selector starting with 'ds-base-' +``` + +Result of `yarn lint --fix`: +```typescript +@Component({ + selector: 'ds-base-something', +}) +class TestThemeableComponent { +} +``` + + +##### Wrong selector for wrapper component + +Filename: `lint/test/fixture/src/app/test/themed-test-themeable.component.ts` + +```typescript +@Component({ + selector: 'ds-themed-something', +}) +class ThemedTestThemeableComponent extends ThemedComponent { +} +``` +Will produce the following error(s): +``` +Themed component wrapper of themeable component shouldn't have a selector starting with 'ds-themed-' +``` + +Result of `yarn lint --fix`: +```typescript +@Component({ + selector: 'ds-something', +}) +class ThemedTestThemeableComponent extends ThemedComponent { +} +``` + + +##### Wrong selector for theme override + +Filename: `lint/test/fixture/src/themes/test/app/test/test-themeable.component.ts` + +```typescript +@Component({ + selector: 'ds-something', +}) +class TestThememeableComponent extends BaseComponent { +} +``` +Will produce the following error(s): +``` +Theme override of themeable component should have a selector starting with 'ds-themed-' +``` + +Result of `yarn lint --fix`: +```typescript +@Component({ + selector: 'ds-themed-something', +}) +class TestThememeableComponent extends BaseComponent { +} +``` + + + diff --git a/docs/lint/ts/rules/themed-component-usages.md b/docs/lint/ts/rules/themed-component-usages.md new file mode 100644 index 0000000000..16ccb701c2 --- /dev/null +++ b/docs/lint/ts/rules/themed-component-usages.md @@ -0,0 +1,332 @@ +[DSpace ESLint plugins](../../../../lint/README.md) > [TypeScript rules](../index.md) > `dspace-angular-ts/themed-component-usages` +_______ + +Themeable components should be used via their `ThemedComponent` wrapper class + +This ensures that custom themes can correctly override _all_ instances of this component. +There are a few exceptions where the base class can still be used: +- Class declaration expressions (otherwise we can't declare, extend or override the class in the first place) +- Angular modules (except for routing modules) +- Angular `@ViewChild` decorators +- Type annotations + + +_______ + +[Source code](../../../../lint/src/rules/ts/themed-component-usages.ts) + +### Examples + + +#### Valid code + +##### allow wrapper class usages + +```typescript +import { ThemedTestThemeableComponent } from './app/test/themed-test-themeable.component'; + +const config = { + a: ThemedTestThemeableComponent, + b: ChipsComponent, +} +``` + +##### allow base class in class declaration + +```typescript +export class TestThemeableComponent { +} +``` + +##### allow inheriting from base class + +```typescript +import { TestThemeableComponent } from './app/test/test-themeable.component'; + +export class ThemedAdminSidebarComponent extends ThemedComponent { +} +``` + +##### allow base class in ViewChild + +```typescript +import { TestThemeableComponent } from './app/test/test-themeable.component'; + +export class Something { + @ViewChild(TestThemeableComponent) test: TestThemeableComponent; +} +``` + +##### allow wrapper selectors in test queries + +Filename: `lint/test/fixture/src/app/test/test.component.spec.ts` + +```typescript +By.css('ds-themeable'); +By.css('#test > ds-themeable > #nest'); +``` + +##### allow wrapper selectors in cypress queries + +Filename: `lint/test/fixture/src/app/test/test.component.cy.ts` + +```typescript +By.css('ds-themeable'); +By.css('#test > ds-themeable > #nest'); +``` + + + + +#### Invalid code & automatic fixes + +##### disallow direct usages of base class + +```typescript +import { TestThemeableComponent } from './app/test/test-themeable.component'; +import { TestComponent } from './app/test/test.component'; + +const config = { + a: TestThemeableComponent, + b: TestComponent, +} +``` +Will produce the following error(s): +``` +Themeable components should be used via their ThemedComponent wrapper +Themeable components should be used via their ThemedComponent wrapper +``` + +Result of `yarn lint --fix`: +```typescript +import { ThemedTestThemeableComponent } from './app/test/themed-test-themeable.component'; +import { TestComponent } from './app/test/test.component'; + +const config = { + a: ThemedTestThemeableComponent, + b: TestComponent, +} +``` + + +##### disallow direct usages of base class, keep other imports + +```typescript +import { Something, TestThemeableComponent } from './app/test/test-themeable.component'; +import { TestComponent } from './app/test/test.component'; + +const config = { + a: TestThemeableComponent, + b: TestComponent, + c: Something, +} +``` +Will produce the following error(s): +``` +Themeable components should be used via their ThemedComponent wrapper +Themeable components should be used via their ThemedComponent wrapper +``` + +Result of `yarn lint --fix`: +```typescript +import { Something } from './app/test/test-themeable.component'; +import { ThemedTestThemeableComponent } from './app/test/themed-test-themeable.component'; +import { TestComponent } from './app/test/test.component'; + +const config = { + a: ThemedTestThemeableComponent, + b: TestComponent, + c: Something, +} +``` + + +##### handle array replacements correctly + +```typescript +const DECLARATIONS = [ + Something, + TestThemeableComponent, + Something, + ThemedTestThemeableComponent, +]; +``` +Will produce the following error(s): +``` +Themeable components should be used via their ThemedComponent wrapper +``` + +Result of `yarn lint --fix`: +```typescript +const DECLARATIONS = [ + Something, + Something, + ThemedTestThemeableComponent, +]; +``` + + +##### disallow override selector in test queries + +Filename: `lint/test/fixture/src/app/test/test.component.spec.ts` + +```typescript +By.css('ds-themed-themeable'); +By.css('#test > ds-themed-themeable > #nest'); +``` +Will produce the following error(s): +``` +Themeable components should be used via their ThemedComponent wrapper +Themeable components should be used via their ThemedComponent wrapper +``` + +Result of `yarn lint --fix`: +```typescript +By.css('ds-themeable'); +By.css('#test > ds-themeable > #nest'); +``` + + +##### disallow base selector in test queries + +Filename: `lint/test/fixture/src/app/test/test.component.spec.ts` + +```typescript +By.css('ds-base-themeable'); +By.css('#test > ds-base-themeable > #nest'); +``` +Will produce the following error(s): +``` +Themeable components should be used via their ThemedComponent wrapper +Themeable components should be used via their ThemedComponent wrapper +``` + +Result of `yarn lint --fix`: +```typescript +By.css('ds-themeable'); +By.css('#test > ds-themeable > #nest'); +``` + + +##### disallow override selector in cypress queries + +Filename: `lint/test/fixture/src/app/test/test.component.cy.ts` + +```typescript +cy.get('ds-themed-themeable'); +cy.get('#test > ds-themed-themeable > #nest'); +``` +Will produce the following error(s): +``` +Themeable components should be used via their ThemedComponent wrapper +Themeable components should be used via their ThemedComponent wrapper +``` + +Result of `yarn lint --fix`: +```typescript +cy.get('ds-themeable'); +cy.get('#test > ds-themeable > #nest'); +``` + + +##### disallow base selector in cypress queries + +Filename: `lint/test/fixture/src/app/test/test.component.cy.ts` + +```typescript +cy.get('ds-base-themeable'); +cy.get('#test > ds-base-themeable > #nest'); +``` +Will produce the following error(s): +``` +Themeable components should be used via their ThemedComponent wrapper +Themeable components should be used via their ThemedComponent wrapper +``` + +Result of `yarn lint --fix`: +```typescript +cy.get('ds-themeable'); +cy.get('#test > ds-themeable > #nest'); +``` + + +##### edge case: unable to find usage node through usage token, but import is still flagged and fixed + +Filename: `lint/test/fixture/src/themes/test/app/test/other-themeable.component.ts` + +```typescript +import { Component } from '@angular/core'; + +import { Context } from './app/core/shared/context.model'; +import { TestThemeableComponent } from '../../../../app/test/test-themeable.component'; + +@Component({ + standalone: true, + imports: [TestThemeableComponent], +}) +export class UsageComponent { +} +``` +Will produce the following error(s): +``` +Themeable components should be used via their ThemedComponent wrapper +Themeable components should be used via their ThemedComponent wrapper +``` + +Result of `yarn lint --fix`: +```typescript +import { Component } from '@angular/core'; + +import { Context } from './app/core/shared/context.model'; +import { ThemedTestThemeableComponent } from '../../../../app/test/themed-test-themeable.component'; + +@Component({ + standalone: true, + imports: [ThemedTestThemeableComponent], +}) +export class UsageComponent { +} +``` + + +##### edge case edge case: both are imported, only wrapper is retained + +Filename: `lint/test/fixture/src/themes/test/app/test/other-themeable.component.ts` + +```typescript +import { Component } from '@angular/core'; + +import { Context } from './app/core/shared/context.model'; +import { TestThemeableComponent } from '../../../../app/test/test-themeable.component'; +import { ThemedTestThemeableComponent } from '../../../../app/test/themed-test-themeable.component'; + +@Component({ + standalone: true, + imports: [TestThemeableComponent, ThemedTestThemeableComponent], +}) +export class UsageComponent { +} +``` +Will produce the following error(s): +``` +Themeable components should be used via their ThemedComponent wrapper +Themeable components should be used via their ThemedComponent wrapper +``` + +Result of `yarn lint --fix`: +```typescript +import { Component } from '@angular/core'; + +import { Context } from './app/core/shared/context.model'; +import { ThemedTestThemeableComponent } from '../../../../app/test/themed-test-themeable.component'; + +@Component({ + standalone: true, + imports: [ThemedTestThemeableComponent], +}) +export class UsageComponent { +} +``` + + + diff --git a/lint/.gitignore b/lint/.gitignore new file mode 100644 index 0000000000..0d22081b3b --- /dev/null +++ b/lint/.gitignore @@ -0,0 +1,3 @@ +/dist/ +/coverage/ +/node-modules/ diff --git a/lint/README.md b/lint/README.md new file mode 100644 index 0000000000..7251a35c06 --- /dev/null +++ b/lint/README.md @@ -0,0 +1,50 @@ +# DSpace ESLint plugins + +Custom ESLint rules for DSpace Angular peculiarities. + +## Usage + +These plugins are included with the rest of our ESLint configuration in [.eslintc.json](../.eslintrc.json). Individual rules can be configured or disabled there, like usual. +- In order for the new rules to be picked up by your IDE, you should first run `yarn build:lint` to build the plugins. +- This will also happen automatically each time `yarn lint` is run. + +## Documentation + +The rules are split up into plugins by language: +- [TypeScript rules](../docs/lint/ts/index.md) +- [HTML rules](../docs/lint/html/index.md) + +> Run `yarn docs:lint` to generate this documentation! + +## Developing + +### Overview + +- All rules are written in TypeScript and compiled into [`dist`](./dist) + - The plugins are linked into the main project dependencies from here + - These directories already contain the necessary `package.json` files to mark them as ESLint plugins +- Rule source files are structured, so they can be imported all in one go + - Each rule must export the following: + - `Messages`: an Enum of error message IDs + - `info`: metadata about this rule (name, description, messages, options, ...) + - `rule`: the implementation of the rule + - `tests`: the tests for this rule, as a set of valid/invalid code snippets. These snippets are used as example in the documentation. + - New rules should be added to their plugin's `index.ts` +- Some useful links + - [Developing ESLint plugins](https://eslint.org/docs/latest/extend/plugins) + - [Custom rules in typescript-eslint](https://typescript-eslint.io/developers/custom-rules) + - [Angular ESLint](https://github.com/angular-eslint/angular-eslint) + +### Parsing project metadata in advance ~ TypeScript AST + +While it is possible to retain persistent state between files during the linting process, it becomes quite complicated if the content of one file determines how we want to lint another file. +Because the two files may be linted out of order, we may not know whether the first file is wrong before we pass by the second. This means that we cannot report or fix the issue, because the first file is already detached from the linting context. + +For example, we cannot consistently determine which components are themeable (i.e. have a `ThemedComponent` wrapper) while linting. +To work around this issue, we construct a registry of themeable components _before_ linting anything. +- We don't have a good way to hook into the ESLint parser at this time +- Instead, we leverage the actual TypeScript AST parser + - Retrieve all `ThemedComponent` wrapper files by the pattern of their path (`themed-*.component.ts`) + - Determine the themed component they're linked to (by the actual type annotation/import path, since filenames are prone to errors) + - Store metadata describing these component pairs in a global registry that can be shared between rules +- This only needs to happen once, and only takes a fraction of a second (for ~100 themeable components) \ No newline at end of file diff --git a/lint/dist/src/rules/html/package.json b/lint/dist/src/rules/html/package.json new file mode 100644 index 0000000000..d3f310d23b --- /dev/null +++ b/lint/dist/src/rules/html/package.json @@ -0,0 +1,6 @@ +{ + "name": "eslint-plugin-dspace-angular-html", + "version": "0.0.0", + "main": "./index.js", + "private": true +} diff --git a/lint/dist/src/rules/ts/package.json b/lint/dist/src/rules/ts/package.json new file mode 100644 index 0000000000..f19e18756a --- /dev/null +++ b/lint/dist/src/rules/ts/package.json @@ -0,0 +1,6 @@ +{ + "name": "eslint-plugin-dspace-angular-ts", + "version": "0.0.0", + "main": "./index.js", + "private": true +} diff --git a/lint/generate-docs.ts b/lint/generate-docs.ts new file mode 100644 index 0000000000..fb2bf53fb5 --- /dev/null +++ b/lint/generate-docs.ts @@ -0,0 +1,85 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ + +import { + existsSync, + mkdirSync, + readFileSync, + rmSync, + writeFileSync, +} from 'fs'; +import { join } from 'path'; + +import { default as htmlPlugin } from './src/rules/html'; +import { default as tsPlugin } from './src/rules/ts'; + +const templates = new Map(); + +function lazyEJS(path: string, data: object): string { + if (!templates.has(path)) { + templates.set(path, require('ejs').compile(readFileSync(path).toString())); + } + + return templates.get(path)(data).replace(/\r\n/g, '\n'); +} + +const docsDir = join('docs', 'lint'); +const tsDir = join(docsDir, 'ts'); +const htmlDir = join(docsDir, 'html'); + +if (existsSync(docsDir)) { + rmSync(docsDir, { recursive: true }); +} + +mkdirSync(join(tsDir, 'rules'), { recursive: true }); +mkdirSync(join(htmlDir, 'rules'), { recursive: true }); + +function template(name: string): string { + return join('lint', 'src', 'util', 'templates', name); +} + +// TypeScript docs +writeFileSync( + join(tsDir, 'index.md'), + lazyEJS(template('index.ejs'), { + plugin: tsPlugin, + rules: tsPlugin.index.map(rule => rule.info), + }), +); + +for (const rule of tsPlugin.index) { + writeFileSync( + join(tsDir, 'rules', rule.info.name + '.md'), + lazyEJS(template('rule.ejs'), { + plugin: tsPlugin, + rule: rule.info, + tests: rule.tests, + }), + ); +} + +// HTML docs +writeFileSync( + join(htmlDir, 'index.md'), + lazyEJS(template('index.ejs'), { + plugin: htmlPlugin, + rules: htmlPlugin.index.map(rule => rule.info), + }), +); + +for (const rule of htmlPlugin.index) { + writeFileSync( + join(htmlDir, 'rules', rule.info.name + '.md'), + lazyEJS(template('rule.ejs'), { + plugin: htmlPlugin, + rule: rule.info, + tests: rule.tests, + }), + ); +} + diff --git a/lint/jasmine.json b/lint/jasmine.json new file mode 100644 index 0000000000..dfacd41a96 --- /dev/null +++ b/lint/jasmine.json @@ -0,0 +1,7 @@ +{ + "spec_files": ["**/*.spec.js"], + "spec_dir": "lint/dist/test", + "helpers": [ + "./test/helpers.js" + ] +} diff --git a/lint/src/rules/html/index.ts b/lint/src/rules/html/index.ts new file mode 100644 index 0000000000..7c1370ae2d --- /dev/null +++ b/lint/src/rules/html/index.ts @@ -0,0 +1,22 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +/* eslint-disable import/no-namespace */ +import { + bundle, + RuleExports, +} from '../../util/structure'; +import * as themedComponentUsages from './themed-component-usages'; + +const index = [ + themedComponentUsages, +] as unknown as RuleExports[]; + +export = { + parser: require('@angular-eslint/template-parser'), + ...bundle('dspace-angular-html', 'HTML', index), +}; diff --git a/lint/src/rules/html/themed-component-usages.ts b/lint/src/rules/html/themed-component-usages.ts new file mode 100644 index 0000000000..0b9a13456a --- /dev/null +++ b/lint/src/rules/html/themed-component-usages.ts @@ -0,0 +1,191 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +import { TmplAstElement } from '@angular-eslint/bundled-angular-compiler'; +import { TemplateParserServices } from '@angular-eslint/utils'; +import { + ESLintUtils, + TSESLint, +} from '@typescript-eslint/utils'; + +import { fixture } from '../../../test/fixture'; +import { + DSpaceESLintRuleInfo, + NamedTests, +} from '../../util/structure'; +import { + DISALLOWED_THEME_SELECTORS, + fixSelectors, +} from '../../util/theme-support'; +import { + getFilename, + getSourceCode, +} from '../../util/typescript'; + +export enum Message { + WRONG_SELECTOR = 'mustUseThemedWrapperSelector', +} + +export const info = { + name: 'themed-component-usages', + meta: { + docs: { + description: `Themeable components should be used via the selector of their \`ThemedComponent\` wrapper class + +This ensures that custom themes can correctly override _all_ instances of this component. +The only exception to this rule are unit tests, where we may want to use the base component in order to keep the test setup simple. + `, + }, + type: 'problem', + fixable: 'code', + schema: [], + messages: { + [Message.WRONG_SELECTOR]: 'Themeable components should be used via their ThemedComponent wrapper\'s selector', + }, + }, + defaultOptions: [], +} as DSpaceESLintRuleInfo; + +export const rule = ESLintUtils.RuleCreator.withoutDocs({ + ...info, + create(context: TSESLint.RuleContext) { + if (getFilename(context).includes('.spec.ts')) { + // skip inline templates in unit tests + return {}; + } + + const parserServices = getSourceCode(context).parserServices as TemplateParserServices; + + return { + [`Element$1[name = /^${DISALLOWED_THEME_SELECTORS}/]`](node: TmplAstElement) { + const { startSourceSpan, endSourceSpan } = node; + const openStart = startSourceSpan.start.offset as number; + + context.report({ + messageId: Message.WRONG_SELECTOR, + loc: parserServices.convertNodeSourceSpanToLoc(startSourceSpan), + fix(fixer) { + const oldSelector = node.name; + const newSelector = fixSelectors(oldSelector); + + const ops = [ + fixer.replaceTextRange([openStart + 1, openStart + 1 + oldSelector.length], newSelector), + ]; + + // make sure we don't mangle self-closing tags + if (endSourceSpan !== null && startSourceSpan.end.offset !== endSourceSpan.end.offset) { + const closeStart = endSourceSpan.start.offset as number; + const closeEnd = endSourceSpan.end.offset as number; + + ops.push(fixer.replaceTextRange([closeStart + 2, closeEnd - 1], newSelector)); + } + + return ops; + }, + }); + }, + }; + }, +}); + +export const tests = { + plugin: info.name, + valid: [ + { + name: 'use no-prefix selectors in HTML templates', + code: ` + + + + `, + }, + { + name: 'use no-prefix selectors in TypeScript templates', + code: ` +@Component({ + template: '' +}) +class Test { +} + `, + }, + { + name: 'use no-prefix selectors in TypeScript test templates', + filename: fixture('src/test.spec.ts'), + code: ` +@Component({ + template: '' +}) +class Test { +} + `, + }, + { + name: 'base selectors are also allowed in TypeScript test templates', + filename: fixture('src/test.spec.ts'), + code: ` +@Component({ + template: '' +}) +class Test { +} + `, + }, + ], + invalid: [ + { + name: 'themed override selectors are not allowed in HTML templates', + code: ` + + + + `, + errors: [ + { + messageId: Message.WRONG_SELECTOR, + }, + { + messageId: Message.WRONG_SELECTOR, + }, + { + messageId: Message.WRONG_SELECTOR, + }, + ], + output: ` + + + + `, + }, + { + name: 'base selectors are not allowed in HTML templates', + code: ` + + + + `, + errors: [ + { + messageId: Message.WRONG_SELECTOR, + }, + { + messageId: Message.WRONG_SELECTOR, + }, + { + messageId: Message.WRONG_SELECTOR, + }, + ], + output: ` + + + + `, + }, + ], +} as NamedTests; + +export default rule; diff --git a/lint/src/rules/ts/index.ts b/lint/src/rules/ts/index.ts new file mode 100644 index 0000000000..a7fdfe41ef --- /dev/null +++ b/lint/src/rules/ts/index.ts @@ -0,0 +1,25 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +import { + bundle, + RuleExports, +} from '../../util/structure'; +/* eslint-disable import/no-namespace */ +import * as themedComponentClasses from './themed-component-classes'; +import * as themedComponentSelectors from './themed-component-selectors'; +import * as themedComponentUsages from './themed-component-usages'; + +const index = [ + themedComponentClasses, + themedComponentSelectors, + themedComponentUsages, +] as unknown as RuleExports[]; + +export = { + ...bundle('dspace-angular-ts', 'TypeScript', index), +}; diff --git a/lint/src/rules/ts/themed-component-classes.ts b/lint/src/rules/ts/themed-component-classes.ts new file mode 100644 index 0000000000..66c37395b4 --- /dev/null +++ b/lint/src/rules/ts/themed-component-classes.ts @@ -0,0 +1,382 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +import { + ESLintUtils, + TSESLint, + TSESTree, +} from '@typescript-eslint/utils'; + +import { fixture } from '../../../test/fixture'; +import { + getComponentImportNode, + getComponentInitializer, + getComponentStandaloneNode, +} from '../../util/angular'; +import { appendObjectProperties } from '../../util/fix'; +import { DSpaceESLintRuleInfo } from '../../util/structure'; +import { + getBaseComponentClassName, + inThemedComponentOverrideFile, + isThemeableComponent, + isThemedComponentWrapper, +} from '../../util/theme-support'; +import { getFilename } from '../../util/typescript'; + +export enum Message { + NOT_STANDALONE = 'mustBeStandalone', + NOT_STANDALONE_IMPORTS_BASE = 'mustBeStandaloneAndImportBase', + WRAPPER_IMPORTS_BASE = 'wrapperShouldImportBase', +} + +export const info = { + name: 'themed-component-classes', + meta: { + docs: { + description: `Formatting rules for themeable component classes + +- All themeable components must be standalone. +- The base component must always be imported in the \`ThemedComponent\` wrapper. This ensures that it is always sufficient to import just the wrapper whenever we use the component. + `, + }, + type: 'problem', + fixable: 'code', + schema: [], + messages: { + [Message.NOT_STANDALONE]: 'Themeable components must be standalone', + [Message.NOT_STANDALONE_IMPORTS_BASE]: 'Themeable component wrapper classes must be standalone and import the base class', + [Message.WRAPPER_IMPORTS_BASE]: 'Themed component wrapper classes must only import the base class', + }, + }, + defaultOptions: [], +} as DSpaceESLintRuleInfo; + +export const rule = ESLintUtils.RuleCreator.withoutDocs({ + ...info, + create(context: TSESLint.RuleContext) { + const filename = getFilename(context); + + if (filename.endsWith('.spec.ts')) { + return {}; + } + + function enforceStandalone(decoratorNode: TSESTree.Decorator, withBaseImport = false) { + const standaloneNode = getComponentStandaloneNode(decoratorNode); + + if (standaloneNode === undefined) { + // We may need to add these properties in one go + if (!withBaseImport) { + context.report({ + messageId: Message.NOT_STANDALONE, + node: decoratorNode, + fix(fixer) { + const initializer = getComponentInitializer(decoratorNode); + return appendObjectProperties(context, fixer, initializer, ['standalone: true']); + }, + }); + } + } else if (!standaloneNode.value) { + context.report({ + messageId: Message.NOT_STANDALONE, + node: standaloneNode, + fix(fixer) { + return fixer.replaceText(standaloneNode, 'true'); + }, + }); + } + + if (withBaseImport) { + const baseClass = getBaseComponentClassName(decoratorNode); + + if (baseClass === undefined) { + return; + } + + const importsNode = getComponentImportNode(decoratorNode); + + if (importsNode === undefined) { + if (standaloneNode === undefined) { + context.report({ + messageId: Message.NOT_STANDALONE_IMPORTS_BASE, + node: decoratorNode, + fix(fixer) { + const initializer = getComponentInitializer(decoratorNode); + return appendObjectProperties(context, fixer, initializer, ['standalone: true', `imports: [${baseClass}]`]); + }, + }); + } else { + context.report({ + messageId: Message.WRAPPER_IMPORTS_BASE, + node: decoratorNode, + fix(fixer) { + const initializer = getComponentInitializer(decoratorNode); + return appendObjectProperties(context, fixer, initializer, [`imports: [${baseClass}]`]); + }, + }); + } + } else { + // If we have an imports node, standalone: true will be enforced by another rule + + const imports = importsNode.elements.map(e => (e as TSESTree.Identifier).name); + + if (!imports.includes(baseClass) || imports.length > 1) { + // The wrapper should _only_ import the base component + context.report({ + messageId: Message.WRAPPER_IMPORTS_BASE, + node: importsNode, + fix(fixer) { + // todo: this may leave unused imports, but that's better than mangling things + return fixer.replaceText(importsNode, `[${baseClass}]`); + }, + }); + } + } + } + } + + return { + 'ClassDeclaration > Decorator[expression.callee.name = "Component"]'(node: TSESTree.Decorator) { + const classNode = node.parent as TSESTree.ClassDeclaration; + const className = classNode.id?.name; + + if (className === undefined) { + return; + } + + if (isThemedComponentWrapper(node)) { + enforceStandalone(node, true); + } else if (inThemedComponentOverrideFile(filename)) { + enforceStandalone(node); + } else if (isThemeableComponent(className)) { + enforceStandalone(node); + } + }, + }; + }, +}); + +export const tests = { + plugin: info.name, + valid: [ + { + name: 'Regular non-themeable component', + code: ` +@Component({ + selector: 'ds-something', + standalone: true, +}) +class Something { +} + `, + }, + { + name: 'Base component', + code: ` +@Component({ + selector: 'ds-base-test-themable', + standalone: true, +}) +class TestThemeableTomponent { +} + `, + }, + { + name: 'Wrapper component', + filename: fixture('src/app/test/themed-test-themeable.component.ts'), + code: ` +@Component({ + selector: 'ds-test-themable', + standalone: true, + imports: [ + TestThemeableComponent, + ], +}) +class ThemedTestThemeableTomponent extends ThemedComponent { +} + `, + }, + { + name: 'Override component', + filename: fixture('src/themes/test/app/test/test-themeable.component.ts'), + code: ` +@Component({ + selector: 'ds-themed-test-themable', + standalone: true, +}) +class Override extends BaseComponent { +} + `, + }, + ], + invalid: [ + { + name: 'Base component must be standalone', + code: ` +@Component({ + selector: 'ds-base-test-themable', +}) +class TestThemeableComponent { +} + `, + errors:[ + { + messageId: Message.NOT_STANDALONE, + }, + ], + output: ` +@Component({ + selector: 'ds-base-test-themable', + standalone: true, +}) +class TestThemeableComponent { +} + `, + }, + { + name: 'Wrapper component must be standalone and import base component', + filename: fixture('src/app/test/themed-test-themeable.component.ts'), + code: ` +@Component({ + selector: 'ds-test-themable', +}) +class ThemedTestThemeableComponent extends ThemedComponent { +} + `, + errors:[ + { + messageId: Message.NOT_STANDALONE_IMPORTS_BASE, + }, + ], + output: ` +@Component({ + selector: 'ds-test-themable', + standalone: true, + imports: [TestThemeableComponent], +}) +class ThemedTestThemeableComponent extends ThemedComponent { +} + `, + }, + + { + name: 'Wrapper component must import base component (array present but empty)', + filename: fixture('src/app/test/themed-test-themeable.component.ts'), + code: ` +@Component({ + selector: 'ds-test-themable', + standalone: true, + imports: [], +}) +class ThemedTestThemeableComponent extends ThemedComponent { +} + `, + errors:[ + { + messageId: Message.WRAPPER_IMPORTS_BASE, + }, + ], + output: ` +@Component({ + selector: 'ds-test-themable', + standalone: true, + imports: [TestThemeableComponent], +}) +class ThemedTestThemeableComponent extends ThemedComponent { +} + `, + }, + { + name: 'Wrapper component must import base component (array is wrong)', + filename: fixture('src/app/test/themed-test-themeable.component.ts'), + code: ` +import { SomethingElse } from './somewhere-else'; + +@Component({ + selector: 'ds-test-themable', + standalone: true, + imports: [ + SomethingElse, + ], +}) +class ThemedTestThemeableComponent extends ThemedComponent { +} + `, + errors:[ + { + messageId: Message.WRAPPER_IMPORTS_BASE, + }, + ], + output: ` +import { SomethingElse } from './somewhere-else'; + +@Component({ + selector: 'ds-test-themable', + standalone: true, + imports: [TestThemeableComponent], +}) +class ThemedTestThemeableComponent extends ThemedComponent { +} + `, + }, { + name: 'Wrapper component must import base component (array is wrong)', + filename: fixture('src/app/test/themed-test-themeable.component.ts'), + code: ` +import { Something, SomethingElse } from './somewhere-else'; + +@Component({ + selector: 'ds-test-themable', + standalone: true, + imports: [ + SomethingElse, + ], +}) +class ThemedTestThemeableComponent extends ThemedComponent { +} + `, + errors:[ + { + messageId: Message.WRAPPER_IMPORTS_BASE, + }, + ], + output: ` +import { Something, SomethingElse } from './somewhere-else'; + +@Component({ + selector: 'ds-test-themable', + standalone: true, + imports: [TestThemeableComponent], +}) +class ThemedTestThemeableComponent extends ThemedComponent { +} + `, + }, + { + name: 'Override component must be standalone', + filename: fixture('src/themes/test/app/test/test-themeable.component.ts'), + code: ` +@Component({ + selector: 'ds-themed-test-themable', +}) +class Override extends BaseComponent { +} + `, + errors:[ + { + messageId: Message.NOT_STANDALONE, + }, + ], + output: ` +@Component({ + selector: 'ds-themed-test-themable', + standalone: true, +}) +class Override extends BaseComponent { +} + `, + }, + ], +}; diff --git a/lint/src/rules/ts/themed-component-selectors.ts b/lint/src/rules/ts/themed-component-selectors.ts new file mode 100644 index 0000000000..e06f5ababf --- /dev/null +++ b/lint/src/rules/ts/themed-component-selectors.ts @@ -0,0 +1,257 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +import { + ESLintUtils, + TSESLint, + TSESTree, +} from '@typescript-eslint/utils'; + +import { fixture } from '../../../test/fixture'; +import { getComponentSelectorNode } from '../../util/angular'; +import { stringLiteral } from '../../util/misc'; +import { DSpaceESLintRuleInfo } from '../../util/structure'; +import { + inThemedComponentOverrideFile, + isThemeableComponent, + isThemedComponentWrapper, +} from '../../util/theme-support'; +import { getFilename } from '../../util/typescript'; + +export enum Message { + BASE = 'wrongSelectorUnthemedComponent', + WRAPPER = 'wrongSelectorThemedComponentWrapper', + THEMED = 'wrongSelectorThemedComponentOverride', +} + +export const info = { + name: 'themed-component-selectors', + meta: { + docs: { + description: `Themeable component selectors should follow the DSpace convention + +Each themeable component is comprised of a base component, a wrapper component and any number of themed components +- Base components should have a selector starting with \`ds-base-\` +- Themed components should have a selector starting with \`ds-themed-\` +- Wrapper components should have a selector starting with \`ds-\`, but not \`ds-base-\` or \`ds-themed-\` + - This is the regular DSpace selector prefix + - **When making a regular component themeable, its selector prefix should be changed to \`ds-base-\`, and the new wrapper's component should reuse the previous selector** + +Unit tests are exempt from this rule, because they may redefine components using the same class name as other themeable components elsewhere in the source. + `, + }, + type: 'problem', + schema: [], + fixable: 'code', + messages: { + [Message.BASE]: 'Unthemed version of themeable component should have a selector starting with \'ds-base-\'', + [Message.WRAPPER]: 'Themed component wrapper of themeable component shouldn\'t have a selector starting with \'ds-themed-\'', + [Message.THEMED]: 'Theme override of themeable component should have a selector starting with \'ds-themed-\'', + }, + }, + defaultOptions: [], +} as DSpaceESLintRuleInfo; + +export const rule = ESLintUtils.RuleCreator.withoutDocs({ + ...info, + create(context: TSESLint.RuleContext) { + const filename = getFilename(context); + + if (filename.endsWith('.spec.ts')) { + return {}; + } + + function enforceWrapperSelector(selectorNode: TSESTree.StringLiteral) { + if (selectorNode?.value.startsWith('ds-themed-')) { + context.report({ + messageId: Message.WRAPPER, + node: selectorNode, + fix(fixer) { + return fixer.replaceText(selectorNode, stringLiteral(selectorNode.value.replace('ds-themed-', 'ds-'))); + }, + }); + } + } + + function enforceBaseSelector(selectorNode: TSESTree.StringLiteral) { + if (!selectorNode?.value.startsWith('ds-base-')) { + context.report({ + messageId: Message.BASE, + node: selectorNode, + fix(fixer) { + return fixer.replaceText(selectorNode, stringLiteral(selectorNode.value.replace('ds-', 'ds-base-'))); + }, + }); + } + } + + function enforceThemedSelector(selectorNode: TSESTree.StringLiteral) { + if (!selectorNode?.value.startsWith('ds-themed-')) { + context.report({ + messageId: Message.THEMED, + node: selectorNode, + fix(fixer) { + return fixer.replaceText(selectorNode, stringLiteral(selectorNode.value.replace('ds-', 'ds-themed-'))); + }, + }); + } + } + + return { + 'ClassDeclaration > Decorator[expression.callee.name = "Component"]'(node: TSESTree.Decorator) { + const selectorNode = getComponentSelectorNode(node); + + if (selectorNode === undefined) { + return; + } + + const selector = selectorNode?.value; + const classNode = node.parent as TSESTree.ClassDeclaration; + const className = classNode.id?.name; + + if (selector === undefined || className === undefined) { + return; + } + + if (isThemedComponentWrapper(node)) { + enforceWrapperSelector(selectorNode); + } else if (inThemedComponentOverrideFile(filename)) { + enforceThemedSelector(selectorNode); + } else if (isThemeableComponent(className)) { + enforceBaseSelector(selectorNode); + } + }, + }; + }, +}); + +export const tests = { + plugin: info.name, + valid: [ + { + name: 'Regular non-themeable component selector', + code: ` +@Component({ + selector: 'ds-something', +}) +class Something { +} + `, + }, + { + name: 'Themeable component selector should replace the original version, unthemed version should be changed to ds-base-', + code: ` +@Component({ + selector: 'ds-base-something', +}) +class Something { +} + +@Component({ + selector: 'ds-something', +}) +class ThemedSomething extends ThemedComponent { +} + +@Component({ + selector: 'ds-themed-something', +}) +class OverrideSomething extends Something { +} + `, + }, + { + name: 'Other themed component wrappers should not interfere', + code: ` +@Component({ + selector: 'ds-something', +}) +class Something { +} + +@Component({ + selector: 'ds-something-else', +}) +class ThemedSomethingElse extends ThemedComponent { +} + `, + }, + ], + invalid: [ + { + name: 'Wrong selector for base component', + filename: fixture('src/app/test/test-themeable.component.ts'), + code: ` +@Component({ + selector: 'ds-something', +}) +class TestThemeableComponent { +} + `, + errors: [ + { + messageId: Message.BASE, + }, + ], + output: ` +@Component({ + selector: 'ds-base-something', +}) +class TestThemeableComponent { +} + `, + }, + { + name: 'Wrong selector for wrapper component', + filename: fixture('src/app/test/themed-test-themeable.component.ts'), + code: ` +@Component({ + selector: 'ds-themed-something', +}) +class ThemedTestThemeableComponent extends ThemedComponent { +} + `, + errors: [ + { + messageId: Message.WRAPPER, + }, + ], + output: ` +@Component({ + selector: 'ds-something', +}) +class ThemedTestThemeableComponent extends ThemedComponent { +} + `, + }, + { + name: 'Wrong selector for theme override', + filename: fixture('src/themes/test/app/test/test-themeable.component.ts'), + code: ` +@Component({ + selector: 'ds-something', +}) +class TestThememeableComponent extends BaseComponent { +} + `, + errors: [ + { + messageId: Message.THEMED, + }, + ], + output: ` +@Component({ + selector: 'ds-themed-something', +}) +class TestThememeableComponent extends BaseComponent { +} + `, + }, + ], +}; + +export default rule; diff --git a/lint/src/rules/ts/themed-component-usages.ts b/lint/src/rules/ts/themed-component-usages.ts new file mode 100644 index 0000000000..96e9962ccf --- /dev/null +++ b/lint/src/rules/ts/themed-component-usages.ts @@ -0,0 +1,502 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +import { + ESLintUtils, + TSESLint, + TSESTree, +} from '@typescript-eslint/utils'; + +import { fixture } from '../../../test/fixture'; +import { + removeWithCommas, + replaceOrRemoveArrayIdentifier, +} from '../../util/fix'; +import { DSpaceESLintRuleInfo } from '../../util/structure'; +import { + allThemeableComponents, + DISALLOWED_THEME_SELECTORS, + fixSelectors, + getThemeableComponentByBaseClass, + isAllowedUnthemedUsage, +} from '../../util/theme-support'; +import { + findImportSpecifier, + findUsages, + findUsagesByName, + getFilename, + relativePath, +} from '../../util/typescript'; + +export enum Message { + WRONG_CLASS = 'mustUseThemedWrapperClass', + WRONG_IMPORT = 'mustImportThemedWrapper', + WRONG_SELECTOR = 'mustUseThemedWrapperSelector', + BASE_IN_MODULE = 'baseComponentNotNeededInModule', +} + +export const info = { + name: 'themed-component-usages', + meta: { + docs: { + description: `Themeable components should be used via their \`ThemedComponent\` wrapper class + +This ensures that custom themes can correctly override _all_ instances of this component. +There are a few exceptions where the base class can still be used: +- Class declaration expressions (otherwise we can't declare, extend or override the class in the first place) +- Angular modules (except for routing modules) +- Angular \`@ViewChild\` decorators +- Type annotations + `, + }, + type: 'problem', + schema: [], + fixable: 'code', + messages: { + [Message.WRONG_CLASS]: 'Themeable components should be used via their ThemedComponent wrapper', + [Message.WRONG_IMPORT]: 'Themeable components should be used via their ThemedComponent wrapper', + [Message.WRONG_SELECTOR]: 'Themeable components should be used via their ThemedComponent wrapper', + [Message.BASE_IN_MODULE]: 'Base themeable components shouldn\'t be declared in modules', + }, + }, + defaultOptions: [], +} as DSpaceESLintRuleInfo; + +export const rule = ESLintUtils.RuleCreator.withoutDocs({ + ...info, + create(context: TSESLint.RuleContext) { + const filename = getFilename(context); + + function handleUnthemedUsagesInTypescript(node: TSESTree.Identifier) { + if (isAllowedUnthemedUsage(node)) { + return; + } + + const entry = getThemeableComponentByBaseClass(node.name); + + if (entry === undefined) { + // this should never happen + throw new Error(`No such themeable component in registry: '${node.name}'`); + } + + context.report({ + messageId: Message.WRONG_CLASS, + node: node, + fix(fixer) { + if (node.parent.type === TSESTree.AST_NODE_TYPES.ArrayExpression) { + return replaceOrRemoveArrayIdentifier(context, fixer, node, entry.wrapperClass); + } else { + return fixer.replaceText(node, entry.wrapperClass); + } + }, + }); + } + + function handleThemedSelectorQueriesInTests(node: TSESTree.Literal) { + context.report({ + node, + messageId: Message.WRONG_SELECTOR, + fix(fixer){ + const newSelector = fixSelectors(node.raw); + return fixer.replaceText(node, newSelector); + }, + }); + } + + function handleUnthemedImportsInTypescript(specifierNode: TSESTree.ImportSpecifier) { + const allUsages = findUsages(context, specifierNode.local); + const badUsages = allUsages.filter(usage => !isAllowedUnthemedUsage(usage)); + + if (badUsages.length === 0) { + return; + } + + const importedNode = specifierNode.imported; + const declarationNode = specifierNode.parent as TSESTree.ImportDeclaration; + + const entry = getThemeableComponentByBaseClass(importedNode.name); + if (entry === undefined) { + // this should never happen + throw new Error(`No such themeable component in registry: '${importedNode.name}'`); + } + + context.report({ + messageId: Message.WRONG_IMPORT, + node: importedNode, + fix(fixer) { + const ops = []; + + const wrapperImport = findImportSpecifier(context, entry.wrapperClass); + + if (findUsagesByName(context, entry.wrapperClass).length === 0) { + // Wrapper is not present in this file, safe to add import + + const newImportLine = `import { ${entry.wrapperClass} } from '${relativePath(filename, entry.wrapperPath)}';`; + + if (declarationNode.specifiers.length === 1) { + if (allUsages.length === badUsages.length) { + ops.push(fixer.replaceText(declarationNode, newImportLine)); + } else if (wrapperImport === undefined) { + ops.push(fixer.insertTextAfter(declarationNode, '\n' + newImportLine)); + } + } else { + ops.push(...removeWithCommas(context, fixer, specifierNode)); + if (wrapperImport === undefined) { + ops.push(fixer.insertTextAfter(declarationNode, '\n' + newImportLine)); + } + } + } else { + // Wrapper already present in the file, remove import instead + + if (allUsages.length === badUsages.length) { + if (declarationNode.specifiers.length === 1) { + // Make sure we remove the newline as well + ops.push(fixer.removeRange([declarationNode.range[0], declarationNode.range[1] + 1])); + } else { + ops.push(...removeWithCommas(context, fixer, specifierNode)); + } + } + } + + return ops; + }, + }); + } + + // ignore tests and non-routing modules + if (filename.endsWith('.spec.ts')) { + return { + [`CallExpression[callee.object.name = "By"][callee.property.name = "css"] > Literal:first-child[value = /.*${DISALLOWED_THEME_SELECTORS}.*/]`]: handleThemedSelectorQueriesInTests, + }; + } else if (filename.endsWith('.cy.ts')) { + return { + [`CallExpression[callee.object.name = "cy"][callee.property.name = "get"] > Literal:first-child[value = /.*${DISALLOWED_THEME_SELECTORS}.*/]`]: handleThemedSelectorQueriesInTests, + }; + } else if ( + filename.match(/(?!src\/themes\/).*(?!routing).module.ts$/) + || filename.match(/themed-.+\.component\.ts$/) + ) { + // do nothing + return {}; + } else { + return allThemeableComponents().reduce( + (rules, entry) => { + return { + ...rules, + [`:not(:matches(ClassDeclaration, ImportSpecifier)) > Identifier[name = "${entry.baseClass}"]`]: handleUnthemedUsagesInTypescript, + [`ImportSpecifier[imported.name = "${entry.baseClass}"]`]: handleUnthemedImportsInTypescript, + }; + }, {}, + ); + } + + }, +}); + +export const tests = { + plugin: info.name, + valid: [ + { + name: 'allow wrapper class usages', + code: ` +import { ThemedTestThemeableComponent } from './app/test/themed-test-themeable.component'; + +const config = { + a: ThemedTestThemeableComponent, + b: ChipsComponent, +} + `, + }, + { + name: 'allow base class in class declaration', + code: ` +export class TestThemeableComponent { +} + `, + }, + { + name: 'allow inheriting from base class', + code: ` +import { TestThemeableComponent } from './app/test/test-themeable.component'; + +export class ThemedAdminSidebarComponent extends ThemedComponent { +} + `, + }, + { + name: 'allow base class in ViewChild', + code: ` +import { TestThemeableComponent } from './app/test/test-themeable.component'; + +export class Something { + @ViewChild(TestThemeableComponent) test: TestThemeableComponent; +} + `, + }, + { + name: 'allow wrapper selectors in test queries', + filename: fixture('src/app/test/test.component.spec.ts'), + code: ` +By.css('ds-themeable'); +By.css('#test > ds-themeable > #nest'); + `, + }, + { + name: 'allow wrapper selectors in cypress queries', + filename: fixture('src/app/test/test.component.cy.ts'), + code: ` +By.css('ds-themeable'); +By.css('#test > ds-themeable > #nest'); + `, + }, + ], + invalid: [ + { + name: 'disallow direct usages of base class', + code: ` +import { TestThemeableComponent } from './app/test/test-themeable.component'; +import { TestComponent } from './app/test/test.component'; + +const config = { + a: TestThemeableComponent, + b: TestComponent, +} + `, + errors: [ + { + messageId: Message.WRONG_IMPORT, + }, + { + messageId: Message.WRONG_CLASS, + }, + ], + output: ` +import { ThemedTestThemeableComponent } from './app/test/themed-test-themeable.component'; +import { TestComponent } from './app/test/test.component'; + +const config = { + a: ThemedTestThemeableComponent, + b: TestComponent, +} + `, + }, + { + name: 'disallow direct usages of base class, keep other imports', + code: ` +import { Something, TestThemeableComponent } from './app/test/test-themeable.component'; +import { TestComponent } from './app/test/test.component'; + +const config = { + a: TestThemeableComponent, + b: TestComponent, + c: Something, +} + `, + errors: [ + { + messageId: Message.WRONG_IMPORT, + }, + { + messageId: Message.WRONG_CLASS, + }, + ], + output: ` +import { Something } from './app/test/test-themeable.component'; +import { ThemedTestThemeableComponent } from './app/test/themed-test-themeable.component'; +import { TestComponent } from './app/test/test.component'; + +const config = { + a: ThemedTestThemeableComponent, + b: TestComponent, + c: Something, +} + `, + }, + { + name: 'handle array replacements correctly', + code: ` +const DECLARATIONS = [ + Something, + TestThemeableComponent, + Something, + ThemedTestThemeableComponent, +]; + `, + errors: [ + { + messageId: Message.WRONG_CLASS, + }, + ], + output: ` +const DECLARATIONS = [ + Something, + Something, + ThemedTestThemeableComponent, +]; + `, + }, + { + name: 'disallow override selector in test queries', + filename: fixture('src/app/test/test.component.spec.ts'), + code: ` +By.css('ds-themed-themeable'); +By.css('#test > ds-themed-themeable > #nest'); + `, + errors: [ + { + messageId: Message.WRONG_SELECTOR, + }, + { + messageId: Message.WRONG_SELECTOR, + }, + ], + output: ` +By.css('ds-themeable'); +By.css('#test > ds-themeable > #nest'); + `, + }, + { + name: 'disallow base selector in test queries', + filename: fixture('src/app/test/test.component.spec.ts'), + code: ` +By.css('ds-base-themeable'); +By.css('#test > ds-base-themeable > #nest'); + `, + errors: [ + { + messageId: Message.WRONG_SELECTOR, + }, + { + messageId: Message.WRONG_SELECTOR, + }, + ], + output: ` +By.css('ds-themeable'); +By.css('#test > ds-themeable > #nest'); + `, + }, + { + name: 'disallow override selector in cypress queries', + filename: fixture('src/app/test/test.component.cy.ts'), + code: ` +cy.get('ds-themed-themeable'); +cy.get('#test > ds-themed-themeable > #nest'); + `, + errors: [ + { + messageId: Message.WRONG_SELECTOR, + }, + { + messageId: Message.WRONG_SELECTOR, + }, + ], + output: ` +cy.get('ds-themeable'); +cy.get('#test > ds-themeable > #nest'); + `, + }, + { + name: 'disallow base selector in cypress queries', + filename: fixture('src/app/test/test.component.cy.ts'), + code: ` +cy.get('ds-base-themeable'); +cy.get('#test > ds-base-themeable > #nest'); + `, + errors: [ + { + messageId: Message.WRONG_SELECTOR, + }, + { + messageId: Message.WRONG_SELECTOR, + }, + ], + output: ` +cy.get('ds-themeable'); +cy.get('#test > ds-themeable > #nest'); + `, + }, + { + name: 'edge case: unable to find usage node through usage token, but import is still flagged and fixed', + filename: fixture('src/themes/test/app/test/other-themeable.component.ts'), + code: ` +import { Component } from '@angular/core'; + +import { Context } from './app/core/shared/context.model'; +import { TestThemeableComponent } from '../../../../app/test/test-themeable.component'; + +@Component({ + standalone: true, + imports: [TestThemeableComponent], +}) +export class UsageComponent { +} + `, + errors: [ + { + messageId: Message.WRONG_IMPORT, + }, + { + messageId: Message.WRONG_CLASS, + }, + ], + output: ` +import { Component } from '@angular/core'; + +import { Context } from './app/core/shared/context.model'; +import { ThemedTestThemeableComponent } from '../../../../app/test/themed-test-themeable.component'; + +@Component({ + standalone: true, + imports: [ThemedTestThemeableComponent], +}) +export class UsageComponent { +} + `, + }, + { + name: 'edge case edge case: both are imported, only wrapper is retained', + filename: fixture('src/themes/test/app/test/other-themeable.component.ts'), + code: ` +import { Component } from '@angular/core'; + +import { Context } from './app/core/shared/context.model'; +import { TestThemeableComponent } from '../../../../app/test/test-themeable.component'; +import { ThemedTestThemeableComponent } from '../../../../app/test/themed-test-themeable.component'; + +@Component({ + standalone: true, + imports: [TestThemeableComponent, ThemedTestThemeableComponent], +}) +export class UsageComponent { +} + `, + errors: [ + { + messageId: Message.WRONG_IMPORT, + }, + { + messageId: Message.WRONG_CLASS, + }, + ], + output: ` +import { Component } from '@angular/core'; + +import { Context } from './app/core/shared/context.model'; +import { ThemedTestThemeableComponent } from '../../../../app/test/themed-test-themeable.component'; + +@Component({ + standalone: true, + imports: [ThemedTestThemeableComponent], +}) +export class UsageComponent { +} + `, + }, + ], +}; + +export default rule; diff --git a/lint/src/util/angular.ts b/lint/src/util/angular.ts new file mode 100644 index 0000000000..70ee903fb8 --- /dev/null +++ b/lint/src/util/angular.ts @@ -0,0 +1,83 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +import { TSESTree } from '@typescript-eslint/utils'; + +import { getObjectPropertyNodeByName } from './typescript'; + +export function getComponentSelectorNode(componentDecoratorNode: TSESTree.Decorator): TSESTree.StringLiteral | undefined { + const property = getComponentInitializerNodeByName(componentDecoratorNode, 'selector'); + + if (property !== undefined) { + // todo: support template literals as well + if (property.type === TSESTree.AST_NODE_TYPES.Literal && typeof property.value === 'string') { + return property as TSESTree.StringLiteral; + } + } + + return undefined; +} + +export function getComponentStandaloneNode(componentDecoratorNode: TSESTree.Decorator): TSESTree.BooleanLiteral | undefined { + const property = getComponentInitializerNodeByName(componentDecoratorNode, 'standalone'); + + if (property !== undefined) { + if (property.type === TSESTree.AST_NODE_TYPES.Literal && typeof property.value === 'boolean') { + return property as TSESTree.BooleanLiteral; + } + } + + return undefined; +} +export function getComponentImportNode(componentDecoratorNode: TSESTree.Decorator): TSESTree.ArrayExpression | undefined { + const property = getComponentInitializerNodeByName(componentDecoratorNode, 'imports'); + + if (property !== undefined) { + if (property.type === TSESTree.AST_NODE_TYPES.ArrayExpression) { + return property as TSESTree.ArrayExpression; + } + } + + return undefined; +} + +export function getComponentClassName(decoratorNode: TSESTree.Decorator): string | undefined { + if (decoratorNode.parent.type !== TSESTree.AST_NODE_TYPES.ClassDeclaration) { + return undefined; + } + + if (decoratorNode.parent.id?.type !== TSESTree.AST_NODE_TYPES.Identifier) { + return undefined; + } + + return decoratorNode.parent.id.name; +} + +export function getComponentSuperClassName(decoratorNode: TSESTree.Decorator): string | undefined { + if (decoratorNode.parent.type !== TSESTree.AST_NODE_TYPES.ClassDeclaration) { + return undefined; + } + + if (decoratorNode.parent.superClass?.type !== TSESTree.AST_NODE_TYPES.Identifier) { + return undefined; + } + + return decoratorNode.parent.superClass.name; +} + +export function getComponentInitializer(componentDecoratorNode: TSESTree.Decorator): TSESTree.ObjectExpression { + return (componentDecoratorNode.expression as TSESTree.CallExpression).arguments[0] as TSESTree.ObjectExpression; +} + +export function getComponentInitializerNodeByName(componentDecoratorNode: TSESTree.Decorator, name: string): TSESTree.Node | undefined { + const initializer = getComponentInitializer(componentDecoratorNode); + return getObjectPropertyNodeByName(initializer, name); +} + +export function isPartOfViewChild(node: TSESTree.Identifier): boolean { + return (node.parent as any)?.callee?.name === 'ViewChild'; +} diff --git a/lint/src/util/fix.ts b/lint/src/util/fix.ts new file mode 100644 index 0000000000..10408cc316 --- /dev/null +++ b/lint/src/util/fix.ts @@ -0,0 +1,125 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +import { TSESTree } from '@typescript-eslint/utils'; +import { + RuleContext, + RuleFix, + RuleFixer, +} from '@typescript-eslint/utils/ts-eslint'; + +import { getSourceCode } from './typescript'; + + + +export function appendObjectProperties(context: RuleContext, fixer: RuleFixer, objectNode: TSESTree.ObjectExpression, properties: string[]): RuleFix { + // todo: may not handle empty objects too well + const lastProperty = objectNode.properties[objectNode.properties.length - 1]; + const source = getSourceCode(context); + const nextToken = source.getTokenAfter(lastProperty); + + // todo: newline & indentation are hardcoded for @Component({}) + // todo: we're assuming that we need trailing commas, what if we don't? + const newPart = '\n' + properties.map(p => ` ${p},`).join('\n'); + + if (nextToken !== null && nextToken.value === ',') { + return fixer.insertTextAfter(nextToken, newPart); + } else { + return fixer.insertTextAfter(lastProperty, ',' + newPart); + } +} + +export function appendArrayElement(context: RuleContext, fixer: RuleFixer, arrayNode: TSESTree.ArrayExpression, value: string): RuleFix { + const source = getSourceCode(context); + + if (arrayNode.elements.length === 0) { + // This is the first element + const openArray = source.getTokenByRangeStart(arrayNode.range[0]); + + if (openArray == null) { + throw new Error('Unexpected null token for opening square bracket'); + } + + // safe to assume the list is single-line + return fixer.insertTextAfter(openArray, `${value}`); + } else { + const lastElement = arrayNode.elements[arrayNode.elements.length - 1]; + + if (lastElement == null) { + throw new Error('Unexpected null node in array'); + } + + const nextToken = source.getTokenAfter(lastElement); + + // todo: we don't know if the list is chopped or not, so we can't make any assumptions -- may produce output that will be flagged by other rules on the next run! + // todo: we're assuming that we need trailing commas, what if we don't? + if (nextToken !== null && nextToken.value === ',') { + return fixer.insertTextAfter(nextToken, ` ${value},`); + } else { + return fixer.insertTextAfter(lastElement, `, ${value},`); + } + } + +} + +export function isLast(elementNode: TSESTree.Node): boolean { + if (!elementNode.parent) { + return false; + } + + let siblingNodes: (TSESTree.Node | null)[] = [null]; + if (elementNode.parent.type === TSESTree.AST_NODE_TYPES.ArrayExpression) { + siblingNodes = elementNode.parent.elements; + } else if (elementNode.parent.type === TSESTree.AST_NODE_TYPES.ImportDeclaration) { + siblingNodes = elementNode.parent.specifiers; + } + + return elementNode === siblingNodes[siblingNodes.length - 1]; +} + +export function removeWithCommas(context: RuleContext, fixer: RuleFixer, elementNode: TSESTree.Node): RuleFix[] { + const ops = []; + + const source = getSourceCode(context); + let nextToken = source.getTokenAfter(elementNode); + let prevToken = source.getTokenBefore(elementNode); + + if (nextToken !== null && prevToken !== null) { + if (nextToken.value === ',') { + nextToken = source.getTokenAfter(nextToken); + if (nextToken !== null) { + ops.push(fixer.removeRange([elementNode.range[0], nextToken.range[0]])); + } + } + if (isLast(elementNode) && prevToken.value === ',') { + prevToken = source.getTokenBefore(prevToken); + if (prevToken !== null) { + ops.push(fixer.removeRange([prevToken.range[1], elementNode.range[1]])); + } + } + } else if (nextToken !== null) { + ops.push(fixer.removeRange([elementNode.range[0], nextToken.range[0]])); + } + + return ops; +} + +export function replaceOrRemoveArrayIdentifier(context: RuleContext, fixer: RuleFixer, identifierNode: TSESTree.Identifier, newValue: string): RuleFix[] { + if (identifierNode.parent.type !== TSESTree.AST_NODE_TYPES.ArrayExpression) { + throw new Error('Parent node is not an array expression!'); + } + + const array = identifierNode.parent as TSESTree.ArrayExpression; + + for (const element of array.elements) { + if (element !== null && element.type === TSESTree.AST_NODE_TYPES.Identifier && element.name === newValue) { + return removeWithCommas(context, fixer, identifierNode); + } + } + + return [fixer.replaceText(identifierNode, newValue)]; +} diff --git a/lint/src/util/misc.ts b/lint/src/util/misc.ts new file mode 100644 index 0000000000..49cb60124e --- /dev/null +++ b/lint/src/util/misc.ts @@ -0,0 +1,28 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ + +export function match(rangeA: number[], rangeB: number[]) { + return rangeA[0] === rangeB[0] && rangeA[1] === rangeB[1]; +} + + +export function stringLiteral(value: string): string { + return `'${value}'`; +} + +/** + * Transform Windows-style paths into Unix-style paths + */ +export function toUnixStylePath(path: string): string { + // note: we're assuming that none of the directory/file names contain '\' or '/' characters. + // using these characters in paths is very bad practice in general, so this should be a safe assumption. + if (path.includes('\\')) { + return path.replace(/^[A-Z]:\\/, '/').replaceAll('\\', '/'); + } + return path; +} diff --git a/lint/src/util/structure.ts b/lint/src/util/structure.ts new file mode 100644 index 0000000000..bfbf7ec7f2 --- /dev/null +++ b/lint/src/util/structure.ts @@ -0,0 +1,57 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +import { TSESLint } from '@typescript-eslint/utils'; +import { RuleTester } from 'eslint'; +import { EnumType } from 'typescript'; + +export type Meta = TSESLint.RuleMetaData; +export type Valid = TSESLint.ValidTestCase | RuleTester.ValidTestCase; +export type Invalid = TSESLint.InvalidTestCase | RuleTester.InvalidTestCase; + +export interface DSpaceESLintRuleInfo { + name: string; + meta: Meta, + defaultOptions: unknown[], +} + +export interface NamedTests { + plugin: string; + valid: Valid[]; + invalid: Invalid[]; +} + +export interface RuleExports { + Message: EnumType, + info: DSpaceESLintRuleInfo, + rule: TSESLint.RuleModule, + tests: NamedTests, + default: unknown, +} + +export interface PluginExports { + name: string, + language: string, + rules: Record, + index: RuleExports[], +} + +export function bundle( + name: string, + language: string, + index: RuleExports[], +): PluginExports { + return index.reduce((o: PluginExports, i: RuleExports) => { + o.rules[i.info.name] = i.rule; + return o; + }, { + name, + language, + rules: {}, + index, + }); +} diff --git a/lint/src/util/templates/index.ejs b/lint/src/util/templates/index.ejs new file mode 100644 index 0000000000..d959f29291 --- /dev/null +++ b/lint/src/util/templates/index.ejs @@ -0,0 +1,5 @@ +[DSpace ESLint plugins](../../../lint/README.md) > <%= plugin.language %> rules +_______ +<% rules.forEach(rule => { %> +- [`<%= plugin.name %>/<%= rule.name %>`](./rules/<%= rule.name %>.md)<% if (rule.meta?.docs?.description) {%>: <%= rule.meta.docs.description.split('\n')[0].trim() -%><% }-%> +<% }) %> diff --git a/lint/src/util/templates/rule.ejs b/lint/src/util/templates/rule.ejs new file mode 100644 index 0000000000..b39d193cc1 --- /dev/null +++ b/lint/src/util/templates/rule.ejs @@ -0,0 +1,48 @@ +[DSpace ESLint plugins](../../../../lint/README.md) > [<%= plugin.language %> rules](../index.md) > `<%= plugin.name %>/<%= rule.name %>` +_______ + +<%- rule.meta.docs?.description %> + +_______ + +[Source code](../../../../lint/src/rules/<%- plugin.name.replace('dspace-angular-', '') %>/<%- rule.name %>.ts) + +### Examples + +<% if (tests.valid) {%> +#### Valid code + <% tests.valid.forEach(test => { %> +##### <%= test.name !== undefined ? test.name : 'UNNAMED' %> + <% if (test.filename) { %> +Filename: `<%- test.filename %>` + <% } %> +```<%- plugin.language.toLowerCase() %> +<%- test.code.trim() %> +``` + <% }) %> +<% } %> + +<% if (tests.invalid) {%> +#### Invalid code <%= rule.meta.fixable ? ' & automatic fixes' : '' %> + <% tests.invalid.forEach(test => { %> +##### <%= test.name !== undefined ? test.name : 'UNNAMED' %> + <% if (test.filename) { %> +Filename: `<%- test.filename %>` + <% } %> +```<%- plugin.language.toLowerCase() %> +<%- test.code.trim() %> +``` +Will produce the following error(s): +``` +<% for (const error of test.errors) { -%> +<%- rule.meta.messages[error.messageId] %> +<% } -%> +``` + <% if (test.output) { %> +Result of `yarn lint --fix`: +```<%- plugin.language.toLowerCase() %> +<%- test.output.trim() %> +``` + <% } %> + <% }) %> +<% } %> diff --git a/lint/src/util/theme-support.ts b/lint/src/util/theme-support.ts new file mode 100644 index 0000000000..64644145fa --- /dev/null +++ b/lint/src/util/theme-support.ts @@ -0,0 +1,265 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ + +import { TSESTree } from '@typescript-eslint/utils'; +import { readFileSync } from 'fs'; +import { basename } from 'path'; +import ts, { Identifier } from 'typescript'; + +import { + getComponentClassName, + isPartOfViewChild, +} from './angular'; +import { + isPartOfClassDeclaration, + isPartOfTypeExpression, +} from './typescript'; + +/** + * Couples a themeable Component to its ThemedComponent wrapper + */ +export interface ThemeableComponentRegistryEntry { + basePath: string; + baseFileName: string, + baseClass: string; + + wrapperPath: string; + wrapperFileName: string, + wrapperClass: string; +} + +function isAngularComponentDecorator(node: ts.Node) { + if (node.kind === ts.SyntaxKind.Decorator && node.parent.kind === ts.SyntaxKind.ClassDeclaration) { + const decorator = node as ts.Decorator; + + if (decorator.expression.kind === ts.SyntaxKind.CallExpression) { + const method = decorator.expression as ts.CallExpression; + + if (method.expression.kind === ts.SyntaxKind.Identifier) { + return (method.expression as Identifier).text === 'Component'; + } + } + } + + return false; +} + +function findImportDeclaration(source: ts.SourceFile, identifierName: string): ts.ImportDeclaration | undefined { + return ts.forEachChild(source, (topNode: ts.Node) => { + if (topNode.kind === ts.SyntaxKind.ImportDeclaration) { + const importDeclaration = topNode as ts.ImportDeclaration; + + if (importDeclaration.importClause?.namedBindings?.kind === ts.SyntaxKind.NamedImports) { + const namedImports = importDeclaration.importClause?.namedBindings as ts.NamedImports; + + for (const element of namedImports.elements) { + if (element.name.text === identifierName) { + return importDeclaration; + } + } + } + } + + return undefined; + }); +} + +/** + * Listing of all themeable Components + */ +class ThemeableComponentRegistry { + public readonly entries: Set; + public readonly byBaseClass: Map; + public readonly byWrapperClass: Map; + public readonly byBasePath: Map; + public readonly byWrapperPath: Map; + + constructor() { + this.entries = new Set(); + this.byBaseClass = new Map(); + this.byWrapperClass = new Map(); + this.byBasePath = new Map(); + this.byWrapperPath = new Map(); + } + + public initialize(prefix = '') { + if (this.entries.size > 0) { + return; + } + + function registerWrapper(path: string) { + const source = getSource(path); + + function traverse(node: ts.Node) { + if (node.parent !== undefined && isAngularComponentDecorator(node)) { + const classNode = node.parent as ts.ClassDeclaration; + + if (classNode.name === undefined || classNode.heritageClauses === undefined) { + return; + } + + const wrapperClass = classNode.name?.escapedText as string; + + for (const heritageClause of classNode.heritageClauses) { + for (const type of heritageClause.types) { + if ((type as any).expression.escapedText === 'ThemedComponent') { + if (type.kind !== ts.SyntaxKind.ExpressionWithTypeArguments || type.typeArguments === undefined) { + continue; + } + + const firstTypeArg = type.typeArguments[0] as ts.TypeReferenceNode; + const baseClass = (firstTypeArg.typeName as ts.Identifier)?.escapedText; + + if (baseClass === undefined) { + continue; + } + + const importDeclaration = findImportDeclaration(source, baseClass); + + if (importDeclaration === undefined) { + continue; + } + + const basePath = resolveLocalPath((importDeclaration.moduleSpecifier as ts.StringLiteral).text, path); + + themeableComponents.add({ + baseClass, + basePath: basePath.replace(new RegExp(`^${prefix}`), ''), + baseFileName: basename(basePath).replace(/\.ts$/, ''), + wrapperClass, + wrapperPath: path.replace(new RegExp(`^${prefix}`), ''), + wrapperFileName: basename(path).replace(/\.ts$/, ''), + }); + } + } + } + + return; + } else { + ts.forEachChild(node, traverse); + } + } + + traverse(source); + } + + const glob = require('glob'); + + // note: this outputs Unix-style paths on Windows + const wrappers: string[] = glob.GlobSync(prefix + 'src/app/**/themed-*.component.ts', { ignore: 'node_modules/**' }).found; + + for (const wrapper of wrappers) { + registerWrapper(wrapper); + } + } + + private add(entry: ThemeableComponentRegistryEntry) { + this.entries.add(entry); + this.byBaseClass.set(entry.baseClass, entry); + this.byWrapperClass.set(entry.wrapperClass, entry); + this.byBasePath.set(entry.basePath, entry); + this.byWrapperPath.set(entry.wrapperPath, entry); + } +} + +export const themeableComponents = new ThemeableComponentRegistry(); + +/** + * Construct the AST of a TypeScript source file + * @param file + */ +function getSource(file: string): ts.SourceFile { + return ts.createSourceFile( + file, + readFileSync(file).toString(), + ts.ScriptTarget.ES2020, // todo: actually use tsconfig.json? + /*setParentNodes */ true, + ); +} + +/** + * Resolve a possibly relative local path into an absolute path starting from the root directory of the project + */ +function resolveLocalPath(path: string, relativeTo: string) { + if (path.startsWith('src/')) { + return path; + } else if (path.startsWith('./')) { + const parts = relativeTo.split('/'); + return [ + ...parts.slice(0, parts.length - 1), + path.replace(/^.\//, ''), + ].join('/') + '.ts'; + } else { + throw new Error(`Unsupported local path: ${path}`); + } +} + +export function isThemedComponentWrapper(decoratorNode: TSESTree.Decorator): boolean { + if (decoratorNode.parent.type !== TSESTree.AST_NODE_TYPES.ClassDeclaration) { + return false; + } + + if (decoratorNode.parent.superClass?.type !== TSESTree.AST_NODE_TYPES.Identifier) { + return false; + } + + return (decoratorNode.parent.superClass as any)?.name === 'ThemedComponent'; +} + +export function getBaseComponentClassName(decoratorNode: TSESTree.Decorator): string | undefined { + const wrapperClass = getComponentClassName(decoratorNode); + + if (wrapperClass === undefined) { + return; + } + + themeableComponents.initialize(); + const entry = themeableComponents.byWrapperClass.get(wrapperClass); + + if (entry === undefined) { + return undefined; + } + + return entry.baseClass; +} + +export function isThemeableComponent(className: string): boolean { + themeableComponents.initialize(); + return themeableComponents.byBaseClass.has(className); +} + +export function inThemedComponentOverrideFile(filename: string): boolean { + const match = filename.match(/src\/themes\/[^\/]+\/(app\/.*)/); + + if (!match) { + return false; + } + themeableComponents.initialize(); + // todo: this is fragile! + return themeableComponents.byBasePath.has(`src/${match[1]}`); +} + +export function allThemeableComponents(): ThemeableComponentRegistryEntry[] { + themeableComponents.initialize(); + return [...themeableComponents.entries]; +} + +export function getThemeableComponentByBaseClass(baseClass: string): ThemeableComponentRegistryEntry | undefined { + themeableComponents.initialize(); + return themeableComponents.byBaseClass.get(baseClass); +} + +export function isAllowedUnthemedUsage(usageNode: TSESTree.Identifier) { + return isPartOfClassDeclaration(usageNode) || isPartOfTypeExpression(usageNode) || isPartOfViewChild(usageNode); +} + +export const DISALLOWED_THEME_SELECTORS = 'ds-(base|themed)-'; + +export function fixSelectors(text: string): string { + return text.replaceAll(/ds-(base|themed)-/g, 'ds-'); +} diff --git a/lint/src/util/typescript.ts b/lint/src/util/typescript.ts new file mode 100644 index 0000000000..3fecad270e --- /dev/null +++ b/lint/src/util/typescript.ts @@ -0,0 +1,154 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +import { + TSESLint, + TSESTree, +} from '@typescript-eslint/utils'; + +import { + match, + toUnixStylePath, +} from './misc'; + +export type AnyRuleContext = TSESLint.RuleContext; + +/** + * Return the current filename based on the ESLint rule context as a Unix-style path. + * This is easier for regex and comparisons to glob paths. + */ +export function getFilename(context: AnyRuleContext): string { + // TSESLint claims this is deprecated, but the suggested alternative is undefined (could be a version mismatch between ESLint and TSESlint?) + // eslint-disable-next-line deprecation/deprecation + return toUnixStylePath(context.getFilename()); +} + +export function getSourceCode(context: AnyRuleContext): TSESLint.SourceCode { + // TSESLint claims this is deprecated, but the suggested alternative is undefined (could be a version mismatch between ESLint and TSESlint?) + // eslint-disable-next-line deprecation/deprecation + return context.getSourceCode(); +} + +export function getObjectPropertyNodeByName(objectNode: TSESTree.ObjectExpression, propertyName: string): TSESTree.Node | undefined { + for (const propertyNode of objectNode.properties) { + if ( + propertyNode.type === TSESTree.AST_NODE_TYPES.Property + && ( + ( + propertyNode.key?.type === TSESTree.AST_NODE_TYPES.Identifier + && propertyNode.key?.name === propertyName + ) || ( + propertyNode.key?.type === TSESTree.AST_NODE_TYPES.Literal + && propertyNode.key?.value === propertyName + ) + ) + ) { + return propertyNode.value; + } + } + return undefined; +} + +export function findUsages(context: AnyRuleContext, localNode: TSESTree.Identifier): TSESTree.Identifier[] { + const source = getSourceCode(context); + + const usages: TSESTree.Identifier[] = []; + + for (const token of source.ast.tokens) { + if (token.type === TSESTree.AST_TOKEN_TYPES.Identifier && token.value === localNode.name && !match(token.range, localNode.range)) { + const node = source.getNodeByRangeIndex(token.range[0]); + // todo: in some cases, the resulting node can actually be the whole program (!) + if (node !== null) { + usages.push(node as TSESTree.Identifier); + } + } + } + + return usages; +} + +export function findUsagesByName(context: AnyRuleContext, identifier: string): TSESTree.Identifier[] { + const source = getSourceCode(context); + + const usages: TSESTree.Identifier[] = []; + + for (const token of source.ast.tokens) { + if (token.type === TSESTree.AST_TOKEN_TYPES.Identifier && token.value === identifier) { + const node = source.getNodeByRangeIndex(token.range[0]); + // todo: in some cases, the resulting node can actually be the whole program (!) + if (node !== null) { + usages.push(node as TSESTree.Identifier); + } + } + } + + return usages; +} + +export function isPartOfTypeExpression(node: TSESTree.Identifier): boolean { + return node.parent?.type?.valueOf().startsWith('TSType'); +} + +export function isPartOfClassDeclaration(node: TSESTree.Identifier): boolean { + return node.parent?.type === TSESTree.AST_NODE_TYPES.ClassDeclaration; +} + +function fromSrc(path: string): string { + const m = path.match(/^.*(src\/.+)(\.(ts|json|js)?)$/); + + if (m) { + return m[1]; + } else { + throw new Error(`Can't infer project-absolute TS/resource path from: ${path}`); + } +} + + +export function relativePath(thisFile: string, importFile: string): string { + const fromParts = fromSrc(thisFile).split('/'); + const toParts = fromSrc(importFile).split('/'); + + let lastCommon = 0; + for (let i = 0; i < fromParts.length - 1; i++) { + if (fromParts[i] === toParts[i]) { + lastCommon++; + } else { + break; + } + } + + const path = toParts.slice(lastCommon, toParts.length).join('/'); + const backtrack = fromParts.length - lastCommon - 1; + + let prefix: string; + if (backtrack > 0) { + prefix = '../'.repeat(backtrack); + } else { + prefix = './'; + } + + return prefix + path; +} + + +export function findImportSpecifier(context: AnyRuleContext, identifier: string): TSESTree.ImportSpecifier | undefined { + const source = getSourceCode(context); + + const usages: TSESTree.Identifier[] = []; + + for (const token of source.ast.tokens) { + if (token.type === TSESTree.AST_TOKEN_TYPES.Identifier && token.value === identifier) { + const node = source.getNodeByRangeIndex(token.range[0]); + // todo: in some cases, the resulting node can actually be the whole program (!) + if (node && node.parent && node.parent.type === TSESTree.AST_NODE_TYPES.ImportSpecifier) { + return node.parent; + } + } + } + + return undefined; +} diff --git a/lint/test/fixture/README.md b/lint/test/fixture/README.md new file mode 100644 index 0000000000..b19ae11b55 --- /dev/null +++ b/lint/test/fixture/README.md @@ -0,0 +1,9 @@ +# ESLint testing fixtures + +The files in this directory are used for the ESLint testing environment +- Some rules rely on registries that must be built up _before_ the rule is run + - In order to test these registries, the fixture sources contain a few dummy components +- The TypeScript ESLint test runner requires at least one dummy file to exist to run any tests + - By default, [`test.ts`](./src/test.ts) is used. Note that this file is empty; it's only there for the TypeScript configuration, the actual content is injected from the `code` property in the tests. + - To test rules that make assertions based on the path of the file, you'll need to include the `filename` property in the test configuration. Note that it must point to an existing file too! + - The `filename` must be provided as `fixture('src/something.ts')` \ No newline at end of file diff --git a/lint/test/fixture/index.ts b/lint/test/fixture/index.ts new file mode 100644 index 0000000000..1d4f33f7e2 --- /dev/null +++ b/lint/test/fixture/index.ts @@ -0,0 +1,13 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ + +export const FIXTURE = 'lint/test/fixture/'; + +export function fixture(path: string): string { + return FIXTURE + path; +} diff --git a/lint/test/fixture/src/app/test/test-routing.module.ts b/lint/test/fixture/src/app/test/test-routing.module.ts new file mode 100644 index 0000000000..1ccbccc599 --- /dev/null +++ b/lint/test/fixture/src/app/test/test-routing.module.ts @@ -0,0 +1,14 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +import { ThemedTestThemeableComponent } from './themed-test-themeable.component'; + +export const ROUTES = [ + { + component: ThemedTestThemeableComponent, + }, +]; diff --git a/lint/test/fixture/src/app/test/test-themeable.component.ts b/lint/test/fixture/src/app/test/test-themeable.component.ts new file mode 100644 index 0000000000..b445040539 --- /dev/null +++ b/lint/test/fixture/src/app/test/test-themeable.component.ts @@ -0,0 +1,16 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +import { Component } from '@angular/core'; + +@Component({ + selector: 'ds-base-test-themeable', + template: '', + standalone: true, +}) +export class TestThemeableComponent { +} diff --git a/lint/test/fixture/src/app/test/test.component.cy.ts b/lint/test/fixture/src/app/test/test.component.cy.ts new file mode 100644 index 0000000000..2300ac4a56 --- /dev/null +++ b/lint/test/fixture/src/app/test/test.component.cy.ts @@ -0,0 +1,8 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ + diff --git a/lint/test/fixture/src/app/test/test.component.spec.ts b/lint/test/fixture/src/app/test/test.component.spec.ts new file mode 100644 index 0000000000..2300ac4a56 --- /dev/null +++ b/lint/test/fixture/src/app/test/test.component.spec.ts @@ -0,0 +1,8 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ + diff --git a/lint/test/fixture/src/app/test/test.component.ts b/lint/test/fixture/src/app/test/test.component.ts new file mode 100644 index 0000000000..c01f104c98 --- /dev/null +++ b/lint/test/fixture/src/app/test/test.component.ts @@ -0,0 +1,15 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +import { Component } from '@angular/core'; + +@Component({ + selector: 'ds-test', + template: '', +}) +export class TestComponent { +} diff --git a/lint/test/fixture/src/app/test/test.module.ts b/lint/test/fixture/src/app/test/test.module.ts new file mode 100644 index 0000000000..a37396ef45 --- /dev/null +++ b/lint/test/fixture/src/app/test/test.module.ts @@ -0,0 +1,24 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +// @ts-ignore +import { NgModule } from '@angular/core'; + +import { TestComponent } from './test.component'; +import { TestThemeableComponent } from './test-themeable.component'; +import { ThemedTestThemeableComponent } from './themed-test-themeable.component'; + +@NgModule({ + declarations: [ + TestComponent, + TestThemeableComponent, + ThemedTestThemeableComponent, + ], +}) +export class TestModule { + +} diff --git a/lint/test/fixture/src/app/test/themed-test-themeable.component.ts b/lint/test/fixture/src/app/test/themed-test-themeable.component.ts new file mode 100644 index 0000000000..2697a8c598 --- /dev/null +++ b/lint/test/fixture/src/app/test/themed-test-themeable.component.ts @@ -0,0 +1,31 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +import { Component } from '@angular/core'; + +import { ThemedComponent } from '../../../../../../src/app/shared/theme-support/themed.component'; +import { TestThemeableComponent } from './test-themeable.component'; + +@Component({ + selector: 'ds-test-themeable', + template: '', + standalone: true, + imports: [TestThemeableComponent], +}) +export class ThemedTestThemeableComponent extends ThemedComponent { + protected getComponentName(): string { + return ''; + } + + protected importThemedComponent(themeName: string): Promise { + return Promise.resolve(undefined); + } + + protected importUnthemedComponent(): Promise { + return Promise.resolve(undefined); + } +} diff --git a/lint/test/fixture/src/test.ts b/lint/test/fixture/src/test.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/lint/test/fixture/src/themes/test/app/test/other-themeable.component.ts b/lint/test/fixture/src/themes/test/app/test/other-themeable.component.ts new file mode 100644 index 0000000000..f72161b2bf --- /dev/null +++ b/lint/test/fixture/src/themes/test/app/test/other-themeable.component.ts @@ -0,0 +1,16 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +import { Component } from '@angular/core'; + +@Component({ + selector: 'ds-themed-test-themeable', + template: '', +}) +export class OtherThemeableComponent { + +} diff --git a/lint/test/fixture/src/themes/test/app/test/test-themeable.component.ts b/lint/test/fixture/src/themes/test/app/test/test-themeable.component.ts new file mode 100644 index 0000000000..d2b02ca9f1 --- /dev/null +++ b/lint/test/fixture/src/themes/test/app/test/test-themeable.component.ts @@ -0,0 +1,18 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +import { Component } from '@angular/core'; + +import { TestThemeableComponent as BaseComponent } from '../../../../app/test/test-themeable.component'; + +@Component({ + selector: 'ds-themed-test-themeable', + template: '', +}) +export class TestThemeableComponent extends BaseComponent { + +} diff --git a/lint/test/fixture/src/themes/test/test.module.ts b/lint/test/fixture/src/themes/test/test.module.ts new file mode 100644 index 0000000000..ff6ec3b2c0 --- /dev/null +++ b/lint/test/fixture/src/themes/test/test.module.ts @@ -0,0 +1,22 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +// @ts-ignore +import { NgModule } from '@angular/core'; + +import { OtherThemeableComponent } from './app/test/other-themeable.component'; +import { TestThemeableComponent } from './app/test/test-themeable.component'; + +@NgModule({ + declarations: [ + TestThemeableComponent, + OtherThemeableComponent, + ], +}) +export class TestModule { + +} diff --git a/lint/test/fixture/tsconfig.json b/lint/test/fixture/tsconfig.json new file mode 100644 index 0000000000..0fd1141ae0 --- /dev/null +++ b/lint/test/fixture/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "include": [ + "src/**/*.ts" + ], + "exclude": [] +} diff --git a/lint/test/helpers.js b/lint/test/helpers.js new file mode 100644 index 0000000000..bd648d007f --- /dev/null +++ b/lint/test/helpers.js @@ -0,0 +1,13 @@ +const SpecReporter = require('jasmine-spec-reporter').SpecReporter; +const StacktraceOption = require('jasmine-spec-reporter').StacktraceOption; + +jasmine.getEnv().clearReporters(); // Clear default console reporter for those instead +jasmine.getEnv().addReporter(new SpecReporter({ + spec: { + displayErrorMessages: false, + }, + summary: { + displayFailed: true, + displayStacktrace: StacktraceOption.PRETTY, + }, +})); diff --git a/lint/test/rules.spec.ts b/lint/test/rules.spec.ts new file mode 100644 index 0000000000..11c9bec46c --- /dev/null +++ b/lint/test/rules.spec.ts @@ -0,0 +1,26 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ + +import { default as htmlPlugin } from '../src/rules/html'; +import { default as tsPlugin } from '../src/rules/ts'; +import { + htmlRuleTester, + tsRuleTester, +} from './testing'; + +describe('TypeScript rules', () => { + for (const { info, rule, tests } of tsPlugin.index) { + tsRuleTester.run(info.name, rule, tests as any); + } +}); + +describe('HTML rules', () => { + for (const { info, rule, tests } of htmlPlugin.index) { + htmlRuleTester.run(info.name, rule, tests); + } +}); diff --git a/lint/test/structure.spec.ts b/lint/test/structure.spec.ts new file mode 100644 index 0000000000..24e69e42d9 --- /dev/null +++ b/lint/test/structure.spec.ts @@ -0,0 +1,76 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ + +import { default as html } from '../src/rules/html'; +import { default as ts } from '../src/rules/ts'; + +describe('plugin structure', () => { + for (const pluginExports of [ts, html]) { + const pluginName = pluginExports.name ?? 'UNNAMED PLUGIN'; + + describe(pluginName, () => { + it('should have a name', () => { + expect(pluginExports.name).toBeTruthy(); + }); + + it('should have rules', () => { + expect(pluginExports.index).toBeTruthy(); + expect(pluginExports.rules).toBeTruthy(); + expect(pluginExports.index.length).toBeGreaterThan(0); + }); + + for (const ruleExports of pluginExports.index) { + const ruleName = ruleExports.info.name ?? 'UNNAMED RULE'; + + describe(ruleName, () => { + it('should have a name', () => { + expect(ruleExports.info.name).toBeTruthy(); + }); + + it('should be included under the right name in the plugin', () => { + expect(pluginExports.rules[ruleExports.info.name]).toBe(ruleExports.rule); + }); + + it('should contain metadata', () => { + expect(ruleExports.info).toBeTruthy(); + expect(ruleExports.info.name).toBeTruthy(); + expect(ruleExports.info.meta).toBeTruthy(); + expect(ruleExports.info.defaultOptions).toBeTruthy(); + }); + + it('should contain messages', () => { + expect(ruleExports.Message).toBeTruthy(); + expect(ruleExports.info.meta.messages).toBeTruthy(); + }); + + describe('messages', () => { + for (const member of Object.keys(ruleExports.Message)) { + describe(member, () => { + const id = (ruleExports.Message as any)[member]; + + it('should have a valid ID', () => { + expect(id).toBeTruthy(); + }); + + it('should have valid metadata', () => { + expect(ruleExports.info.meta.messages[id]).toBeTruthy(); + }); + }); + } + }); + + it('should contain tests', () => { + expect(ruleExports.tests).toBeTruthy(); + expect(ruleExports.tests.valid.length).toBeGreaterThan(0); + expect(ruleExports.tests.invalid.length).toBeGreaterThan(0); + }); + }); + } + }); + } +}); diff --git a/lint/test/testing.ts b/lint/test/testing.ts new file mode 100644 index 0000000000..cfa54c5b85 --- /dev/null +++ b/lint/test/testing.ts @@ -0,0 +1,53 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ + +import { RuleTester as TypeScriptRuleTester } from '@typescript-eslint/rule-tester'; +import { RuleTester } from 'eslint'; + +import { themeableComponents } from '../src/util/theme-support'; +import { + FIXTURE, + fixture, +} from './fixture'; + + +// Register themed components from test fixture +themeableComponents.initialize(FIXTURE); + +TypeScriptRuleTester.itOnly = fit; +TypeScriptRuleTester.itSkip = xit; + +export const tsRuleTester = new TypeScriptRuleTester({ + parser: '@typescript-eslint/parser', + defaultFilenames: { + ts: fixture('src/test.ts'), + tsx: 'n/a', + }, + parserOptions: { + project: fixture('tsconfig.json'), + }, +}); + +class HtmlRuleTester extends RuleTester { + run(name: string, rule: any, tests: { valid: any[], invalid: any[] }) { + super.run(name, rule, { + valid: tests.valid.map((test) => ({ + filename: fixture('test.html'), + ...test, + })), + invalid: tests.invalid.map((test) => ({ + filename: fixture('test.html'), + ...test, + })), + }); + } +} + +export const htmlRuleTester = new HtmlRuleTester({ + parser: require.resolve('@angular-eslint/template-parser'), +}); diff --git a/lint/test/theme-support.spec.ts b/lint/test/theme-support.spec.ts new file mode 100644 index 0000000000..2edf9594b6 --- /dev/null +++ b/lint/test/theme-support.spec.ts @@ -0,0 +1,24 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ + +import { themeableComponents } from '../src/util/theme-support'; + +describe('theme-support', () => { + describe('themeable component registry', () => { + it('should contain all themeable components from the fixture', () => { + expect(themeableComponents.entries.size).toBe(1); + expect(themeableComponents.byBasePath.size).toBe(1); + expect(themeableComponents.byWrapperPath.size).toBe(1); + expect(themeableComponents.byBaseClass.size).toBe(1); + + expect(themeableComponents.byBaseClass.get('TestThemeableComponent')).toBeTruthy(); + expect(themeableComponents.byBasePath.get('src/app/test/test-themeable.component.ts')).toBeTruthy(); + expect(themeableComponents.byWrapperPath.get('src/app/test/themed-test-themeable.component.ts')).toBeTruthy(); + }); + }); +}); diff --git a/lint/tsconfig.json b/lint/tsconfig.json new file mode 100644 index 0000000000..d3537a7376 --- /dev/null +++ b/lint/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "target": "ES2021", + "lib": [ + "es2021" + ], + "module": "nodenext", + "moduleResolution": "nodenext", + "noImplicitReturns": true, + "skipLibCheck": true, + "strict": true, + "outDir": "./dist", + "sourceMap": true, + "allowSyntheticDefaultImports": true, + "types": [ + "jasmine", + "node" + ] + }, + "include": [ + "**/*.ts", + ], + "exclude": [ + "dist", + "test/fixture" + ] +} diff --git a/package.json b/package.json index 0571d166bc..72b11eec8b 100644 --- a/package.json +++ b/package.json @@ -17,11 +17,16 @@ "build:stats": "ng build --stats-json", "build:prod": "cross-env NODE_ENV=production yarn run build:ssr", "build:ssr": "ng build --configuration production && ng run dspace-angular:server:production", + "build:lint": "rimraf 'lint/dist/**/*.js' 'lint/dist/**/*.js.map' && tsc -b lint/tsconfig.json", "test": "ng test --source-map=true --watch=false --configuration test", "test:watch": "nodemon --exec \"ng test --source-map=true --watch=true --configuration test\"", "test:headless": "ng test --source-map=true --watch=false --configuration test --browsers=ChromeHeadless --code-coverage", - "lint": "ng lint", - "lint-fix": "ng lint --fix=true", + "test:lint": "yarn build:lint && yarn test:lint:nobuild", + "test:lint:nobuild": "jasmine --config=lint/jasmine.json", + "lint": "yarn build:lint && yarn lint:nobuild", + "lint:nobuild": "ng lint", + "lint-fix": "yarn build:lint && ng lint --fix=true", + "docs:lint": "ts-node --project ./lint/tsconfig.json ./lint/generate-docs.ts", "e2e": "cross-env NODE_ENV=production ng e2e", "clean:dev:config": "rimraf src/assets/config.json", "clean:coverage": "rimraf coverage", @@ -40,7 +45,8 @@ "cypress:run": "cypress run", "env:yaml": "ts-node --project ./tsconfig.ts-node.json scripts/env-to-yaml.ts", "base-href": "ts-node --project ./tsconfig.ts-node.json scripts/base-href.ts", - "check-circ-deps": "npx madge --exclude '(bitstream|bundle|collection|config-submission-form|eperson|item|version)\\.model\\.ts$' --circular --extensions ts ./" + "check-circ-deps": "npx madge --exclude '(bitstream|bundle|collection|config-submission-form|eperson|item|version)\\.model\\.ts$' --circular --extensions ts ./", + "postinstall": "yarn build:lint || echo 'Skipped DSpace ESLint plugins.'" }, "browser": { "fs": false, @@ -136,6 +142,7 @@ "@angular-builders/custom-webpack": "~17.0.1", "@angular-devkit/build-angular": "^17.3.0", "@angular-eslint/builder": "17.2.1", + "@angular-eslint/bundled-angular-compiler": "17.2.1", "@angular-eslint/eslint-plugin": "17.2.1", "@angular-eslint/eslint-plugin-template": "17.2.1", "@angular-eslint/schematics": "17.2.1", @@ -155,8 +162,10 @@ "@types/lodash": "^4.14.194", "@types/node": "^14.14.9", "@types/sanitize-html": "^2.9.0", - "@typescript-eslint/eslint-plugin": "^5.59.1", - "@typescript-eslint/parser": "^5.59.1", + "@typescript-eslint/eslint-plugin": "^7.2.0", + "@typescript-eslint/parser": "^7.2.0", + "@typescript-eslint/rule-tester": "^7.2.0", + "@typescript-eslint/utils": "^7.2.0", "axe-core": "^4.7.2", "browser-sync": "^3.0.0", "compression-webpack-plugin": "^9.2.0", @@ -167,6 +176,8 @@ "deep-freeze": "0.0.1", "eslint": "^8.39.0", "eslint-plugin-deprecation": "^1.4.1", + "eslint-plugin-dspace-angular-html": "link:./lint/dist/src/rules/html", + "eslint-plugin-dspace-angular-ts": "link:./lint/dist/src/rules/ts", "eslint-plugin-import": "^2.27.5", "eslint-plugin-import-newlines": "^1.3.1", "eslint-plugin-jsdoc": "^45.0.0", @@ -176,6 +187,7 @@ "eslint-plugin-simple-import-sort": "^10.0.0", "eslint-plugin-unused-imports": "^2.0.0", "express-static-gzip": "^2.1.7", + "jasmine": "^3.8.0", "jasmine-core": "^3.8.0", "jasmine-marbles": "0.9.2", "karma": "^6.4.2", @@ -206,4 +218,4 @@ "webpack-cli": "^4.2.0", "webpack-dev-server": "^4.13.3" } -} \ No newline at end of file +} diff --git a/src/app/access-control/bulk-access/browse/bulk-access-browse.component.html b/src/app/access-control/bulk-access/browse/bulk-access-browse.component.html index 131cb49d6b..f96ddf4a23 100644 --- a/src/app/access-control/bulk-access/browse/bulk-access-browse.component.html +++ b/src/app/access-control/bulk-access/browse/bulk-access-browse.component.html @@ -23,10 +23,10 @@ {{'admin.access-control.bulk-access-browse.search.header' | translate}}
- + [showThumbnails]="false">
diff --git a/src/app/access-control/epeople-registry/epeople-registry.component.html b/src/app/access-control/epeople-registry/epeople-registry.component.html index 641615d182..b16d8ac659 100644 --- a/src/app/access-control/epeople-registry/epeople-registry.component.html +++ b/src/app/access-control/epeople-registry/epeople-registry.component.html @@ -41,7 +41,7 @@ - + - +

{{messagePrefix + '.groupsEPersonIsMemberOf' | translate}}

- + - + (); + response = new EventEmitter(); public isCoarMessageVisible = false; diff --git a/src/app/admin/admin-notify-dashboard/admin-notify-logs/admin-notify-logs-result/admin-notify-logs-result.component.html b/src/app/admin/admin-notify-dashboard/admin-notify-logs/admin-notify-logs-result/admin-notify-logs-result.component.html index f7c4f77a70..57ce83934c 100644 --- a/src/app/admin/admin-notify-dashboard/admin-notify-logs/admin-notify-logs-result/admin-notify-logs-result.component.html +++ b/src/app/admin/admin-notify-dashboard/admin-notify-logs/admin-notify-logs-result/admin-notify-logs-result.component.html @@ -15,11 +15,11 @@
- + >
diff --git a/src/app/admin/admin-notify-dashboard/admin-notify-logs/admin-notify-logs-result/admin-notify-logs-result.component.ts b/src/app/admin/admin-notify-dashboard/admin-notify-logs/admin-notify-logs-result/admin-notify-logs-result.component.ts index 4cb6696fe2..33ac2b4cb1 100644 --- a/src/app/admin/admin-notify-dashboard/admin-notify-logs/admin-notify-logs-result/admin-notify-logs-result.component.ts +++ b/src/app/admin/admin-notify-dashboard/admin-notify-logs/admin-notify-logs-result/admin-notify-logs-result.component.ts @@ -51,7 +51,7 @@ import { ThemedSearchComponent } from '../../../../shared/search/themed-search.c export class AdminNotifyLogsResultComponent implements OnInit { @Input() - defaultConfiguration: string; + defaultConfiguration: string; public selectedSearchConfig$: Observable; diff --git a/src/app/admin/admin-notify-dashboard/admin-notify-metrics/admin-notify-metrics.component.ts b/src/app/admin/admin-notify-dashboard/admin-notify-metrics/admin-notify-metrics.component.ts index 727b081d89..af5345f512 100644 --- a/src/app/admin/admin-notify-dashboard/admin-notify-metrics/admin-notify-metrics.component.ts +++ b/src/app/admin/admin-notify-dashboard/admin-notify-metrics/admin-notify-metrics.component.ts @@ -27,7 +27,7 @@ import { AdminNotifyMetricsRow } from './admin-notify-metrics.model'; export class AdminNotifyMetricsComponent { @Input() - boxesConfig: AdminNotifyMetricsRow[]; + boxesConfig: AdminNotifyMetricsRow[]; private incomingConfiguration = 'NOTIFY.incoming'; private involvedItemsSuffix = 'involvedItems'; diff --git a/src/app/admin/admin-notify-dashboard/models/admin-notify-message.model.ts b/src/app/admin/admin-notify-dashboard/models/admin-notify-message.model.ts index f32451eaff..2524ffed9b 100644 --- a/src/app/admin/admin-notify-dashboard/models/admin-notify-message.model.ts +++ b/src/app/admin/admin-notify-dashboard/models/admin-notify-message.model.ts @@ -24,138 +24,138 @@ export class AdminNotifyMessage extends DSpaceObject { * The type of the resource */ @excludeFromEquals - type = ADMIN_NOTIFY_MESSAGE; + type = ADMIN_NOTIFY_MESSAGE; /** * The id of the message */ @autoserialize - id: string; + id: string; /** * The id of the notification */ @autoserialize - notificationId: string; + notificationId: string; /** * The type of the notification */ @autoserialize - notificationType: string; + notificationType: string; /** * The type of the notification */ @autoserialize - coarNotifyType: string; + coarNotifyType: string; /** * The type of the activity */ @autoserialize - activityStreamType: string; + activityStreamType: string; /** * The object the message reply to */ @autoserialize - inReplyTo: string; + inReplyTo: string; /** * The object the message relates to */ @autoserialize - object: string; + object: string; /** * The name of the related item */ @autoserialize - relatedItem: string; + relatedItem: string; /** * The name of the related ldn service */ @autoserialize - ldnService: string; + ldnService: string; /** * The context of the message */ @autoserialize - context: string; + context: string; /** * The related COAR message */ @autoserialize - message: string; + message: string; /** * The attempts of the queue */ @autoserialize - queueAttempts: number; + queueAttempts: number; /** * Timestamp of the last queue attempt */ @autoserialize - queueLastStartTime: string; + queueLastStartTime: string; /** * The type of the activity stream */ @autoserialize - origin: number | string; + origin: number | string; /** * The type of the activity stream */ @autoserialize - target: number | string; + target: number | string; /** * The label for the status of the queue */ @autoserialize - queueStatusLabel: string; + queueStatusLabel: string; /** * The timeout of the queue */ @autoserialize - queueTimeout: string; + queueTimeout: string; /** * The status of the queue */ @autoserialize - queueStatus: number; + queueStatus: number; /** * Thumbnail link used when browsing items with showThumbs config enabled. */ @autoserialize - thumbnail: string; + thumbnail: string; /** * The observable pointing to the item itself */ @autoserialize - item: Observable; + item: Observable; /** * The observable pointing to the access status of the item */ @autoserialize - accessStatus: Observable; + accessStatus: Observable; @deserialize - _links: { + _links: { self: { href: string; }; diff --git a/src/app/admin/admin-search-page/admin-search-page.component.html b/src/app/admin/admin-search-page/admin-search-page.component.html index 516799ddf9..69ff132fe3 100644 --- a/src/app/admin/admin-search-page/admin-search-page.component.html +++ b/src/app/admin/admin-search-page/admin-search-page.component.html @@ -1 +1 @@ - + diff --git a/src/app/admin/admin-sidebar/admin-sidebar.component.scss b/src/app/admin/admin-sidebar/admin-sidebar.component.scss index d3607ca625..a679155c32 100644 --- a/src/app/admin/admin-sidebar/admin-sidebar.component.scss +++ b/src/app/admin/admin-sidebar/admin-sidebar.component.scss @@ -138,3 +138,9 @@ } } } + +::ng-deep { + .browser-firefox-windows { + --ds-dark-scrollbar-width: 20px; + } +} diff --git a/src/app/admin/admin-sidebar/admin-sidebar.component.ts b/src/app/admin/admin-sidebar/admin-sidebar.component.ts index cd26a11995..00514d2afc 100644 --- a/src/app/admin/admin-sidebar/admin-sidebar.component.ts +++ b/src/app/admin/admin-sidebar/admin-sidebar.component.ts @@ -41,7 +41,7 @@ import { ThemeService } from '../../shared/theme-support/theme.service'; * Component representing the admin sidebar */ @Component({ - selector: 'ds-admin-sidebar', + selector: 'ds-base-admin-sidebar', templateUrl: './admin-sidebar.component.html', styleUrls: ['./admin-sidebar.component.scss'], animations: [slideSidebar], diff --git a/src/app/admin/admin-sidebar/themed-admin-sidebar.component.ts b/src/app/admin/admin-sidebar/themed-admin-sidebar.component.ts index 165f48384a..6127fd42a9 100644 --- a/src/app/admin/admin-sidebar/themed-admin-sidebar.component.ts +++ b/src/app/admin/admin-sidebar/themed-admin-sidebar.component.ts @@ -11,10 +11,11 @@ import { AdminSidebarComponent } from './admin-sidebar.component'; * Themed wrapper for AdminSidebarComponent */ @Component({ - selector: 'ds-themed-admin-sidebar', + selector: 'ds-admin-sidebar', styleUrls: [], templateUrl: '../../shared/theme-support/themed.component.html', standalone: true, + imports: [AdminSidebarComponent], }) export class ThemedAdminSidebarComponent extends ThemedComponent { diff --git a/src/app/admin/admin-workflow-page/admin-workflow-page.component.html b/src/app/admin/admin-workflow-page/admin-workflow-page.component.html index c16ed31168..d12cefb331 100644 --- a/src/app/admin/admin-workflow-page/admin-workflow-page.component.html +++ b/src/app/admin/admin-workflow-page/admin-workflow-page.component.html @@ -1 +1 @@ - + diff --git a/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/supervision-order-group-selector/supervision-order-group-selector.component.ts b/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/supervision-order-group-selector/supervision-order-group-selector.component.ts index 597940ca10..67dcd09b8e 100644 --- a/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/supervision-order-group-selector/supervision-order-group-selector.component.ts +++ b/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/supervision-order-group-selector/supervision-order-group-selector.component.ts @@ -15,6 +15,7 @@ import { NotificationsService } from 'src/app/shared/notifications/notifications import { DSONameService } from '../../../../../../core/breadcrumbs/dso-name.service'; import { RemoteData } from '../../../../../../core/data/remote-data'; +import { RequestEntryState } from '../../../../../../core/data/request-entry-state.model'; import { Group } from '../../../../../../core/eperson/models/group.model'; import { SupervisionOrder } from '../../../../../../core/supervision-order/models/supervision-order.model'; import { SupervisionOrderDataService } from '../../../../../../core/supervision-order/supervision-order-data.service'; @@ -95,7 +96,7 @@ export class SupervisionOrderGroupSelectorComponent { this.supervisionOrderDataService.create(supervisionDataObject, this.itemUUID, this.selectedGroup.uuid, this.selectedOrderType).pipe( getFirstCompletedRemoteData(), ).subscribe((rd: RemoteData) => { - if (rd.state === 'Success') { + if (rd.state === RequestEntryState.Success) { this.notificationsService.success(this.translateService.get('supervision-group-selector.notification.create.success.title', { name: this.dsoNameService.getName(this.selectedGroup) })); this.create.emit(rd.payload); this.close(); diff --git a/src/app/app.component.html b/src/app/app.component.html index fb9983a6ef..9016f42dc1 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,3 +1,3 @@ - + [shouldShowRouteLoader]="isRouteLoading$ | async"> diff --git a/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.html b/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.html index b306eb2721..f7d2c60832 100644 --- a/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.html +++ b/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.html @@ -2,7 +2,7 @@
- +
@@ -27,7 +27,7 @@
- +
diff --git a/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.ts b/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.ts index f4d285ac74..36b0816ade 100644 --- a/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.ts +++ b/src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.ts @@ -80,7 +80,7 @@ import { VarDirective } from '../../shared/utils/var.directive'; import { ThemedThumbnailComponent } from '../../thumbnail/themed-thumbnail.component'; @Component({ - selector: 'ds-edit-bitstream-page', + selector: 'ds-base-edit-bitstream-page', styleUrls: ['./edit-bitstream-page.component.scss'], templateUrl: './edit-bitstream-page.component.html', changeDetection: ChangeDetectionStrategy.OnPush, diff --git a/src/app/bitstream-page/edit-bitstream-page/themed-edit-bitstream-page.component.ts b/src/app/bitstream-page/edit-bitstream-page/themed-edit-bitstream-page.component.ts index 4d8a9946f5..7e922485cb 100644 --- a/src/app/bitstream-page/edit-bitstream-page/themed-edit-bitstream-page.component.ts +++ b/src/app/bitstream-page/edit-bitstream-page/themed-edit-bitstream-page.component.ts @@ -4,10 +4,11 @@ import { ThemedComponent } from '../../shared/theme-support/themed.component'; import { EditBitstreamPageComponent } from './edit-bitstream-page.component'; @Component({ - selector: 'ds-themed-edit-bitstream-page', + selector: 'ds-edit-bitstream-page', styleUrls: [], templateUrl: '../../shared/theme-support/themed.component.html', standalone: true, + imports: [EditBitstreamPageComponent], }) export class ThemedEditBitstreamPageComponent extends ThemedComponent { protected getComponentName(): string { diff --git a/src/app/breadcrumbs/breadcrumbs.component.ts b/src/app/breadcrumbs/breadcrumbs.component.ts index 2483335245..9820d672c9 100644 --- a/src/app/breadcrumbs/breadcrumbs.component.ts +++ b/src/app/breadcrumbs/breadcrumbs.component.ts @@ -18,7 +18,7 @@ import { BreadcrumbsService } from './breadcrumbs.service'; * Component representing the breadcrumbs of a page */ @Component({ - selector: 'ds-breadcrumbs', + selector: 'ds-base-breadcrumbs', templateUrl: './breadcrumbs.component.html', styleUrls: ['./breadcrumbs.component.scss'], standalone: true, diff --git a/src/app/breadcrumbs/themed-breadcrumbs.component.ts b/src/app/breadcrumbs/themed-breadcrumbs.component.ts index 2e471fd92d..255af605dc 100644 --- a/src/app/breadcrumbs/themed-breadcrumbs.component.ts +++ b/src/app/breadcrumbs/themed-breadcrumbs.component.ts @@ -7,10 +7,11 @@ import { BreadcrumbsComponent } from './breadcrumbs.component'; * Themed wrapper for BreadcrumbsComponent */ @Component({ - selector: 'ds-themed-breadcrumbs', + selector: 'ds-breadcrumbs', styleUrls: [], templateUrl: '../shared/theme-support/themed.component.html', standalone: true, + imports: [BreadcrumbsComponent], }) export class ThemedBreadcrumbsComponent extends ThemedComponent { protected getComponentName(): string { diff --git a/src/app/browse-by/browse-by-date/browse-by-date.component.spec.ts b/src/app/browse-by/browse-by-date/browse-by-date.component.spec.ts index 7cdb083c10..edd7cd951a 100644 --- a/src/app/browse-by/browse-by-date/browse-by-date.component.spec.ts +++ b/src/app/browse-by/browse-by-date/browse-by-date.component.spec.ts @@ -28,7 +28,6 @@ import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.serv import { PaginationService } from '../../core/pagination/pagination.service'; import { Community } from '../../core/shared/community.model'; import { Item } from '../../core/shared/item.model'; -import { BrowseByComponent } from '../../shared/browse-by/browse-by.component'; import { ThemedBrowseByComponent } from '../../shared/browse-by/themed-browse-by.component'; import { ThemedComcolPageBrowseByComponent } from '../../shared/comcol/comcol-page-browse-by/themed-comcol-page-browse-by.component'; import { ComcolPageContentComponent } from '../../shared/comcol/comcol-page-content/comcol-page-content.component'; @@ -135,7 +134,6 @@ describe('BrowseByDateComponent', () => { ThemedComcolPageHandleComponent, ComcolPageContentComponent, ThemedComcolPageBrowseByComponent, - BrowseByComponent, ThemedLoadingComponent, ThemedBrowseByComponent, ], diff --git a/src/app/browse-by/browse-by-date/browse-by-date.component.ts b/src/app/browse-by/browse-by-date/browse-by-date.component.ts index 3382a46f58..c84fc4cb77 100644 --- a/src/app/browse-by/browse-by-date/browse-by-date.component.ts +++ b/src/app/browse-by/browse-by-date/browse-by-date.component.ts @@ -35,7 +35,6 @@ import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.serv import { RemoteData } from '../../core/data/remote-data'; import { PaginationService } from '../../core/pagination/pagination.service'; import { Item } from '../../core/shared/item.model'; -import { BrowseByComponent } from '../../shared/browse-by/browse-by.component'; import { ThemedComcolPageBrowseByComponent } from '../../shared/comcol/comcol-page-browse-by/themed-comcol-page-browse-by.component'; import { ComcolPageContentComponent } from '../../shared/comcol/comcol-page-content/comcol-page-content.component'; import { ThemedComcolPageHandleComponent } from '../../shared/comcol/comcol-page-handle/themed-comcol-page-handle.component'; @@ -72,7 +71,6 @@ import { ComcolPageContentComponent, DsoEditMenuComponent, ThemedComcolPageBrowseByComponent, - BrowseByComponent, TranslateModule, ThemedLoadingComponent, ThemedBrowseByComponent, diff --git a/src/app/browse-by/browse-by-metadata/browse-by-metadata.component.html b/src/app/browse-by/browse-by-metadata/browse-by-metadata.component.html index 69c3c31c46..22e564ac27 100644 --- a/src/app/browse-by/browse-by-metadata/browse-by-metadata.component.html +++ b/src/app/browse-by/browse-by-metadata/browse-by-metadata.component.html @@ -1,6 +1,6 @@
diff --git a/src/app/browse-by/browse-by-metadata/browse-by-metadata.component.ts b/src/app/browse-by/browse-by-metadata/browse-by-metadata.component.ts index c3bad2a923..3c893f5259 100644 --- a/src/app/browse-by/browse-by-metadata/browse-by-metadata.component.ts +++ b/src/app/browse-by/browse-by-metadata/browse-by-metadata.component.ts @@ -45,7 +45,6 @@ import { BrowseEntry } from '../../core/shared/browse-entry.model'; import { Context } from '../../core/shared/context.model'; import { Item } from '../../core/shared/item.model'; import { getFirstSucceededRemoteData } from '../../core/shared/operators'; -import { BrowseByComponent } from '../../shared/browse-by/browse-by.component'; import { ThemedComcolPageBrowseByComponent } from '../../shared/comcol/comcol-page-browse-by/themed-comcol-page-browse-by.component'; import { ComcolPageContentComponent } from '../../shared/comcol/comcol-page-content/comcol-page-content.component'; import { ThemedComcolPageHandleComponent } from '../../shared/comcol/comcol-page-handle/themed-comcol-page-handle.component'; @@ -78,7 +77,6 @@ export const BBM_PAGINATION_ID = 'bbm'; ComcolPageContentComponent, DsoEditMenuComponent, ThemedComcolPageBrowseByComponent, - BrowseByComponent, TranslateModule, ThemedLoadingComponent, ThemedBrowseByComponent, diff --git a/src/app/browse-by/browse-by-taxonomy/browse-by-taxonomy.component.ts b/src/app/browse-by/browse-by-taxonomy/browse-by-taxonomy.component.ts index 39a90de83e..94956f257d 100644 --- a/src/app/browse-by/browse-by-taxonomy/browse-by-taxonomy.component.ts +++ b/src/app/browse-by/browse-by-taxonomy/browse-by-taxonomy.component.ts @@ -27,7 +27,6 @@ import { Context } from '../../core/shared/context.model'; import { HierarchicalBrowseDefinition } from '../../core/shared/hierarchical-browse-definition.model'; import { VocabularyEntryDetail } from '../../core/submission/vocabularies/models/vocabulary-entry-detail.model'; import { VocabularyOptions } from '../../core/submission/vocabularies/models/vocabulary-options.model'; -import { BrowseByComponent } from '../../shared/browse-by/browse-by.component'; import { ThemedBrowseByComponent } from '../../shared/browse-by/themed-browse-by.component'; import { ThemedComcolPageBrowseByComponent } from '../../shared/comcol/comcol-page-browse-by/themed-comcol-page-browse-by.component'; import { ComcolPageContentComponent } from '../../shared/comcol/comcol-page-content/comcol-page-content.component'; @@ -55,7 +54,6 @@ import { BrowseByDataType } from '../browse-by-switcher/browse-by-data-type'; ComcolPageContentComponent, DsoEditMenuComponent, ThemedComcolPageBrowseByComponent, - BrowseByComponent, TranslateModule, ThemedLoadingComponent, ThemedBrowseByComponent, diff --git a/src/app/browse-by/browse-by-title/browse-by-title.component.spec.ts b/src/app/browse-by/browse-by-title/browse-by-title.component.spec.ts index a184a44953..e2e022391a 100644 --- a/src/app/browse-by/browse-by-title/browse-by-title.component.spec.ts +++ b/src/app/browse-by/browse-by-title/browse-by-title.component.spec.ts @@ -25,7 +25,6 @@ import { ItemDataService } from '../../core/data/item-data.service'; import { PaginationService } from '../../core/pagination/pagination.service'; import { Community } from '../../core/shared/community.model'; import { Item } from '../../core/shared/item.model'; -import { BrowseByComponent } from '../../shared/browse-by/browse-by.component'; import { ThemedBrowseByComponent } from '../../shared/browse-by/themed-browse-by.component'; import { ThemedComcolPageBrowseByComponent } from '../../shared/comcol/comcol-page-browse-by/themed-comcol-page-browse-by.component'; import { ComcolPageContentComponent } from '../../shared/comcol/comcol-page-content/comcol-page-content.component'; @@ -108,7 +107,6 @@ describe('BrowseByTitleComponent', () => { ComcolPageContentComponent, DsoEditMenuComponent, ThemedComcolPageBrowseByComponent, - BrowseByComponent, ThemedLoadingComponent, ThemedBrowseByComponent, ] }, diff --git a/src/app/browse-by/browse-by-title/browse-by-title.component.ts b/src/app/browse-by/browse-by-title/browse-by-title.component.ts index 38c4c1333c..d99332baf1 100644 --- a/src/app/browse-by/browse-by-title/browse-by-title.component.ts +++ b/src/app/browse-by/browse-by-title/browse-by-title.component.ts @@ -15,7 +15,6 @@ import { SortDirection, SortOptions, } from '../../core/cache/models/sort-options.model'; -import { BrowseByComponent } from '../../shared/browse-by/browse-by.component'; import { ThemedBrowseByComponent } from '../../shared/browse-by/themed-browse-by.component'; import { ThemedComcolPageBrowseByComponent } from '../../shared/comcol/comcol-page-browse-by/themed-comcol-page-browse-by.component'; import { ComcolPageContentComponent } from '../../shared/comcol/comcol-page-content/comcol-page-content.component'; @@ -47,7 +46,6 @@ import { ComcolPageContentComponent, DsoEditMenuComponent, ThemedComcolPageBrowseByComponent, - BrowseByComponent, TranslateModule, ThemedLoadingComponent, ThemedBrowseByComponent, diff --git a/src/app/collection-page/collection-item-mapper/collection-item-mapper.component.html b/src/app/collection-page/collection-item-mapper/collection-item-mapper.component.html index 77f85d5f78..3ca9665b1c 100644 --- a/src/app/collection-page/collection-item-mapper/collection-item-mapper.component.html +++ b/src/app/collection-page/collection-item-mapper/collection-item-mapper.component.html @@ -28,14 +28,14 @@
- - +
diff --git a/src/app/collection-page/collection-page.component.html b/src/app/collection-page/collection-page.component.html index d5da37c12f..f9739a4aa9 100644 --- a/src/app/collection-page/collection-page.component.html +++ b/src/app/collection-page/collection-page.component.html @@ -17,10 +17,10 @@ - - +
- - +
@@ -55,7 +55,7 @@
- +
\ No newline at end of file diff --git a/src/app/collection-page/collection-page.component.ts b/src/app/collection-page/collection-page.component.ts index c015213ee1..5b5d0a84d7 100644 --- a/src/app/collection-page/collection-page.component.ts +++ b/src/app/collection-page/collection-page.component.ts @@ -54,7 +54,7 @@ import { ViewTrackerComponent } from '../statistics/angulartics/dspace/view-trac import { getCollectionPageRoute } from './collection-page-routing-paths'; @Component({ - selector: 'ds-collection-page', + selector: 'ds-base-collection-page', styleUrls: ['./collection-page.component.scss'], templateUrl: './collection-page.component.html', changeDetection: ChangeDetectionStrategy.OnPush, diff --git a/src/app/collection-page/create-collection-page/create-collection-page.component.html b/src/app/collection-page/create-collection-page/create-collection-page.component.html index f3f9785692..0907290ac3 100644 --- a/src/app/collection-page/create-collection-page/create-collection-page.component.html +++ b/src/app/collection-page/create-collection-page/create-collection-page.component.html @@ -1,10 +1,15 @@ -
-
-
-

{{'collection.create.sub-head' | translate:{ parent: dsoNameService.getName((parentRD$| async)?.payload) } }}

-
+
+
+
+

{{ 'collection.create.sub-head' | translate:{ parent: dsoNameService.getName((parentRD$| async)?.payload) } }}

- +
+ +
+ +
+
diff --git a/src/app/collection-page/create-collection-page/create-collection-page.component.ts b/src/app/collection-page/create-collection-page/create-collection-page.component.ts index a3594ef152..a26bc9d6f8 100644 --- a/src/app/collection-page/create-collection-page/create-collection-page.component.ts +++ b/src/app/collection-page/create-collection-page/create-collection-page.component.ts @@ -1,4 +1,7 @@ -import { AsyncPipe } from '@angular/common'; +import { + AsyncPipe, + NgIf, +} from '@angular/common'; import { Component } from '@angular/core'; import { Router } from '@angular/router'; import { @@ -13,6 +16,7 @@ import { RequestService } from '../../core/data/request.service'; import { RouteService } from '../../core/services/route.service'; import { Collection } from '../../core/shared/collection.model'; import { CreateComColPageComponent } from '../../shared/comcol/comcol-forms/create-comcol-page/create-comcol-page.component'; +import { ThemedLoadingComponent } from '../../shared/loading/themed-loading.component'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { CollectionFormComponent } from '../collection-form/collection-form.component'; @@ -27,6 +31,8 @@ import { CollectionFormComponent } from '../collection-form/collection-form.comp CollectionFormComponent, TranslateModule, AsyncPipe, + ThemedLoadingComponent, + NgIf, ], standalone: true, }) diff --git a/src/app/collection-page/edit-collection-page/collection-source/collection-source.component.html b/src/app/collection-page/edit-collection-page/collection-source/collection-source.component.html index 7b4ac97421..2294085096 100644 --- a/src/app/collection-page/edit-collection-page/collection-source/collection-source.component.html +++ b/src/app/collection-page/edit-collection-page/collection-source/collection-source.component.html @@ -25,7 +25,7 @@
- +

{{ 'collection.edit.tabs.source.form.head' | translate }}

diff --git a/src/app/collection-page/edit-item-template-page/edit-item-template-page.component.html b/src/app/collection-page/edit-item-template-page/edit-item-template-page.component.html index 8d095dd229..7f0b2efba2 100644 --- a/src/app/collection-page/edit-item-template-page/edit-item-template-page.component.html +++ b/src/app/collection-page/edit-item-template-page/edit-item-template-page.component.html @@ -3,10 +3,10 @@

{{ 'collection.edit.template.head' | translate:{ collection: dsoNameService.getName(collection) } }}

- +
- +
diff --git a/src/app/collection-page/edit-item-template-page/edit-item-template-page.component.spec.ts b/src/app/collection-page/edit-item-template-page/edit-item-template-page.component.spec.ts index 7fa29919f8..4d33e7d008 100644 --- a/src/app/collection-page/edit-item-template-page/edit-item-template-page.component.spec.ts +++ b/src/app/collection-page/edit-item-template-page/edit-item-template-page.component.spec.ts @@ -12,7 +12,6 @@ import { of as observableOf } from 'rxjs'; import { ItemTemplateDataService } from '../../core/data/item-template-data.service'; import { Collection } from '../../core/shared/collection.model'; -import { DsoEditMetadataComponent } from '../../dso-shared/dso-edit-metadata/dso-edit-metadata.component'; import { ThemedDsoEditMetadataComponent } from '../../dso-shared/dso-edit-metadata/themed-dso-edit-metadata.component'; import { getMockThemeService } from '../../shared/mocks/theme-service.mock'; import { NotificationsService } from '../../shared/notifications/notifications.service'; @@ -51,7 +50,7 @@ describe('EditItemTemplatePageComponent', () => { schemas: [NO_ERRORS_SCHEMA], }).overrideComponent(EditItemTemplatePageComponent, { remove: { - imports: [ThemedDsoEditMetadataComponent, DsoEditMetadataComponent], + imports: [ThemedDsoEditMetadataComponent], }, }).compileComponents(); })); diff --git a/src/app/collection-page/edit-item-template-page/edit-item-template-page.component.ts b/src/app/collection-page/edit-item-template-page/edit-item-template-page.component.ts index eb44ffdc9b..f7c5dc4b14 100644 --- a/src/app/collection-page/edit-item-template-page/edit-item-template-page.component.ts +++ b/src/app/collection-page/edit-item-template-page/edit-item-template-page.component.ts @@ -24,7 +24,6 @@ import { RemoteData } from '../../core/data/remote-data'; import { Collection } from '../../core/shared/collection.model'; import { Item } from '../../core/shared/item.model'; import { getFirstSucceededRemoteDataPayload } from '../../core/shared/operators'; -import { DsoEditMetadataComponent } from '../../dso-shared/dso-edit-metadata/dso-edit-metadata.component'; import { ThemedDsoEditMetadataComponent } from '../../dso-shared/dso-edit-metadata/themed-dso-edit-metadata.component'; import { AlertComponent } from '../../shared/alert/alert.component'; import { AlertType } from '../../shared/alert/alert-type'; @@ -33,11 +32,10 @@ import { VarDirective } from '../../shared/utils/var.directive'; import { getCollectionEditRoute } from '../collection-page-routing-paths'; @Component({ - selector: 'ds-edit-item-template-page', + selector: 'ds-base-edit-item-template-page', templateUrl: './edit-item-template-page.component.html', imports: [ ThemedDsoEditMetadataComponent, - DsoEditMetadataComponent, RouterLink, AsyncPipe, VarDirective, diff --git a/src/app/collection-page/edit-item-template-page/themed-edit-item-template-page.component.ts b/src/app/collection-page/edit-item-template-page/themed-edit-item-template-page.component.ts index 2dff557835..421049990a 100644 --- a/src/app/collection-page/edit-item-template-page/themed-edit-item-template-page.component.ts +++ b/src/app/collection-page/edit-item-template-page/themed-edit-item-template-page.component.ts @@ -4,10 +4,11 @@ import { ThemedComponent } from '../../shared/theme-support/themed.component'; import { EditItemTemplatePageComponent } from './edit-item-template-page.component'; @Component({ - selector: 'ds-themed-edit-item-template-page', + selector: 'ds-edit-item-template-page', styleUrls: [], templateUrl: '../../shared/theme-support/themed.component.html', standalone: true, + imports: [EditItemTemplatePageComponent], }) /** * Component for editing the item template of a collection diff --git a/src/app/collection-page/themed-collection-page.component.ts b/src/app/collection-page/themed-collection-page.component.ts index e095e6eb68..c84d7c5fb4 100644 --- a/src/app/collection-page/themed-collection-page.component.ts +++ b/src/app/collection-page/themed-collection-page.component.ts @@ -7,10 +7,11 @@ import { CollectionPageComponent } from './collection-page.component'; * Themed wrapper for CollectionPageComponent */ @Component({ - selector: 'ds-themed-collection-page', + selector: 'ds-collection-page', styleUrls: [], templateUrl: '../shared/theme-support/themed.component.html', standalone: true, + imports: [CollectionPageComponent], }) export class ThemedCollectionPageComponent extends ThemedComponent { protected getComponentName(): string { diff --git a/src/app/community-list-page/community-list-page.component.html b/src/app/community-list-page/community-list-page.component.html index 4392fb87d0..ca05201478 100644 --- a/src/app/community-list-page/community-list-page.component.html +++ b/src/app/community-list-page/community-list-page.component.html @@ -1,4 +1,4 @@

{{ 'communityList.title' | translate }}

- +
diff --git a/src/app/community-list-page/community-list-page.component.ts b/src/app/community-list-page/community-list-page.component.ts index aaf7bc2000..ca0db89f53 100644 --- a/src/app/community-list-page/community-list-page.component.ts +++ b/src/app/community-list-page/community-list-page.component.ts @@ -8,7 +8,7 @@ import { ThemedCommunityListComponent } from './community-list/themed-community- * navigated to with community-list.page.routing.module */ @Component({ - selector: 'ds-community-list-page', + selector: 'ds-base-community-list-page', templateUrl: './community-list-page.component.html', standalone: true, imports: [ThemedCommunityListComponent, TranslateModule], diff --git a/src/app/community-list-page/community-list/community-list.component.html b/src/app/community-list-page/community-list/community-list.component.html index 0d678299e1..82238ddd0d 100644 --- a/src/app/community-list-page/community-list/community-list.component.html +++ b/src/app/community-list-page/community-list/community-list.component.html @@ -1,4 +1,4 @@ - + {{ 'communityList.showMore' | translate }} - +
@@ -61,7 +61,7 @@ - +
diff --git a/src/app/community-list-page/community-list/community-list.component.ts b/src/app/community-list-page/community-list/community-list.component.ts index e0456ca700..5819471d7e 100644 --- a/src/app/community-list-page/community-list/community-list.component.ts +++ b/src/app/community-list-page/community-list/community-list.component.ts @@ -38,7 +38,7 @@ import { FlatNode } from '../flat-node.model'; * Which nodes were expanded is kept in the store, so this persists across pages. */ @Component({ - selector: 'ds-community-list', + selector: 'ds-base-community-list', templateUrl: './community-list.component.html', styleUrls: ['./community-list.component.scss'], standalone: true, diff --git a/src/app/community-list-page/community-list/themed-community-list.component.ts b/src/app/community-list-page/community-list/themed-community-list.component.ts index dc6c0aa345..5340384ed5 100644 --- a/src/app/community-list-page/community-list/themed-community-list.component.ts +++ b/src/app/community-list-page/community-list/themed-community-list.component.ts @@ -5,10 +5,11 @@ import { CommunityListComponent } from './community-list.component'; @Component({ - selector: 'ds-themed-community-list', + selector: 'ds-community-list', styleUrls: [], templateUrl: '../../shared/theme-support/themed.component.html', standalone: true, + imports: [CommunityListComponent], }) export class ThemedCommunityListComponent extends ThemedComponent { protected getComponentName(): string { diff --git a/src/app/community-list-page/themed-community-list-page.component.ts b/src/app/community-list-page/themed-community-list-page.component.ts index 4f0575c7db..d427b1bec1 100644 --- a/src/app/community-list-page/themed-community-list-page.component.ts +++ b/src/app/community-list-page/themed-community-list-page.component.ts @@ -7,10 +7,11 @@ import { CommunityListPageComponent } from './community-list-page.component'; * Themed wrapper for CommunityListPageComponent */ @Component({ - selector: 'ds-themed-community-list-page', + selector: 'ds-community-list-page', styleUrls: [], templateUrl: '../shared/theme-support/themed.component.html', standalone: true, + imports: [CommunityListPageComponent], }) export class ThemedCommunityListPageComponent extends ThemedComponent { protected getComponentName(): string { diff --git a/src/app/community-page/community-page.component.html b/src/app/community-page/community-page.component.html index b3e577af7d..a695e2019a 100644 --- a/src/app/community-page/community-page.component.html +++ b/src/app/community-page/community-page.component.html @@ -10,8 +10,8 @@ - - + + @@ -25,8 +25,8 @@
- - + +
@@ -39,5 +39,5 @@ - + diff --git a/src/app/community-page/community-page.component.ts b/src/app/community-page/community-page.component.ts index ce3d05aef9..bdb3a50c6b 100644 --- a/src/app/community-page/community-page.component.ts +++ b/src/app/community-page/community-page.component.ts @@ -47,7 +47,7 @@ import { ThemedCollectionPageSubCollectionListComponent } from './sections/sub-c import { ThemedCommunityPageSubCommunityListComponent } from './sections/sub-com-col-section/sub-community-list/themed-community-page-sub-community-list.component'; @Component({ - selector: 'ds-community-page', + selector: 'ds-base-community-page', styleUrls: ['./community-page.component.scss'], templateUrl: './community-page.component.html', changeDetection: ChangeDetectionStrategy.OnPush, diff --git a/src/app/community-page/create-community-page/create-community-page.component.html b/src/app/community-page/create-community-page/create-community-page.component.html index 57039040c2..4c634dab8e 100644 --- a/src/app/community-page/create-community-page/create-community-page.component.html +++ b/src/app/community-page/create-community-page/create-community-page.component.html @@ -1,9 +1,10 @@ -
+
-

{{ 'community.create.sub-head' | translate:{ parent: dsoNameService.getName(parent) } }}

+

{{ 'community.create.sub-head' | translate:{ parent: dsoNameService.getName(parent) } }}

@@ -11,3 +12,7 @@ (back)="navigateToHome()" (finish)="navigateToNewPage()">
+ +
+ +
diff --git a/src/app/community-page/create-community-page/create-community-page.component.ts b/src/app/community-page/create-community-page/create-community-page.component.ts index acc5279a5c..082f6c4f0b 100644 --- a/src/app/community-page/create-community-page/create-community-page.component.ts +++ b/src/app/community-page/create-community-page/create-community-page.component.ts @@ -15,6 +15,7 @@ import { RequestService } from '../../core/data/request.service'; import { RouteService } from '../../core/services/route.service'; import { Community } from '../../core/shared/community.model'; import { CreateComColPageComponent } from '../../shared/comcol/comcol-forms/create-comcol-page/create-comcol-page.component'; +import { ThemedLoadingComponent } from '../../shared/loading/themed-loading.component'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { VarDirective } from '../../shared/utils/var.directive'; import { CommunityFormComponent } from '../community-form/community-form.component'; @@ -32,6 +33,7 @@ import { CommunityFormComponent } from '../community-form/community-form.compone VarDirective, NgIf, AsyncPipe, + ThemedLoadingComponent, ], standalone: true, }) diff --git a/src/app/community-page/sections/sub-com-col-section/sub-collection-list/community-page-sub-collection-list.component.html b/src/app/community-page/sections/sub-com-col-section/sub-collection-list/community-page-sub-collection-list.component.html index b5fbf1a01d..59d7b3bb5e 100644 --- a/src/app/community-page/sections/sub-com-col-section/sub-collection-list/community-page-sub-collection-list.component.html +++ b/src/app/community-page/sections/sub-com-col-section/sub-collection-list/community-page-sub-collection-list.component.html @@ -9,5 +9,5 @@
- + diff --git a/src/app/community-page/sections/sub-com-col-section/sub-collection-list/community-page-sub-collection-list.component.ts b/src/app/community-page/sections/sub-com-col-section/sub-collection-list/community-page-sub-collection-list.component.ts index 2935f25595..1e8ff1d46c 100644 --- a/src/app/community-page/sections/sub-com-col-section/sub-collection-list/community-page-sub-collection-list.component.ts +++ b/src/app/community-page/sections/sub-com-col-section/sub-collection-list/community-page-sub-collection-list.component.ts @@ -36,7 +36,7 @@ import { PaginationComponentOptions } from '../../../../shared/pagination/pagina import { VarDirective } from '../../../../shared/utils/var.directive'; @Component({ - selector: 'ds-community-page-sub-collection-list', + selector: 'ds-base-community-page-sub-collection-list', styleUrls: ['./community-page-sub-collection-list.component.scss'], templateUrl: './community-page-sub-collection-list.component.html', animations: [fadeIn], diff --git a/src/app/community-page/sections/sub-com-col-section/sub-collection-list/themed-community-page-sub-collection-list.component.ts b/src/app/community-page/sections/sub-com-col-section/sub-collection-list/themed-community-page-sub-collection-list.component.ts index ff5d057b31..4a965bc926 100644 --- a/src/app/community-page/sections/sub-com-col-section/sub-collection-list/themed-community-page-sub-collection-list.component.ts +++ b/src/app/community-page/sections/sub-com-col-section/sub-collection-list/themed-community-page-sub-collection-list.component.ts @@ -8,10 +8,11 @@ import { ThemedComponent } from '../../../../shared/theme-support/themed.compone import { CommunityPageSubCollectionListComponent } from './community-page-sub-collection-list.component'; @Component({ - selector: 'ds-themed-community-page-sub-collection-list', + selector: 'ds-community-page-sub-collection-list', styleUrls: [], templateUrl: '../../../../shared/theme-support/themed.component.html', standalone: true, + imports: [CommunityPageSubCollectionListComponent], }) export class ThemedCollectionPageSubCollectionListComponent extends ThemedComponent { @Input() community: Community; diff --git a/src/app/community-page/sections/sub-com-col-section/sub-com-col-section.component.html b/src/app/community-page/sections/sub-com-col-section/sub-com-col-section.component.html index 515e08ffdf..a811014bcc 100644 --- a/src/app/community-page/sections/sub-com-col-section/sub-com-col-section.component.html +++ b/src/app/community-page/sections/sub-com-col-section/sub-com-col-section.component.html @@ -1,8 +1,8 @@ - - - + - + diff --git a/src/app/community-page/sections/sub-com-col-section/sub-community-list/community-page-sub-community-list.component.html b/src/app/community-page/sections/sub-com-col-section/sub-community-list/community-page-sub-community-list.component.html index 0834d08ba5..7f9840f6b7 100644 --- a/src/app/community-page/sections/sub-com-col-section/sub-community-list/community-page-sub-community-list.component.html +++ b/src/app/community-page/sections/sub-com-col-section/sub-community-list/community-page-sub-community-list.component.html @@ -9,5 +9,5 @@ - + diff --git a/src/app/community-page/sections/sub-com-col-section/sub-community-list/community-page-sub-community-list.component.ts b/src/app/community-page/sections/sub-com-col-section/sub-community-list/community-page-sub-community-list.component.ts index 4f74eff601..36bd9919bb 100644 --- a/src/app/community-page/sections/sub-com-col-section/sub-community-list/community-page-sub-community-list.component.ts +++ b/src/app/community-page/sections/sub-com-col-section/sub-community-list/community-page-sub-community-list.component.ts @@ -35,7 +35,7 @@ import { PaginationComponentOptions } from '../../../../shared/pagination/pagina import { VarDirective } from '../../../../shared/utils/var.directive'; @Component({ - selector: 'ds-community-page-sub-community-list', + selector: 'ds-base-community-page-sub-community-list', styleUrls: ['./community-page-sub-community-list.component.scss'], templateUrl: './community-page-sub-community-list.component.html', animations: [fadeIn], diff --git a/src/app/community-page/sections/sub-com-col-section/sub-community-list/themed-community-page-sub-community-list.component.ts b/src/app/community-page/sections/sub-com-col-section/sub-community-list/themed-community-page-sub-community-list.component.ts index 11b62d68e4..5988ad0f5e 100644 --- a/src/app/community-page/sections/sub-com-col-section/sub-community-list/themed-community-page-sub-community-list.component.ts +++ b/src/app/community-page/sections/sub-com-col-section/sub-community-list/themed-community-page-sub-community-list.component.ts @@ -8,10 +8,11 @@ import { ThemedComponent } from '../../../../shared/theme-support/themed.compone import { CommunityPageSubCommunityListComponent } from './community-page-sub-community-list.component'; @Component({ - selector: 'ds-themed-community-page-sub-community-list', + selector: 'ds-community-page-sub-community-list', styleUrls: [], templateUrl: '../../../../shared/theme-support/themed.component.html', standalone: true, + imports: [CommunityPageSubCommunityListComponent], }) export class ThemedCommunityPageSubCommunityListComponent extends ThemedComponent { diff --git a/src/app/community-page/themed-community-page.component.ts b/src/app/community-page/themed-community-page.component.ts index 41a2960719..b655452041 100644 --- a/src/app/community-page/themed-community-page.component.ts +++ b/src/app/community-page/themed-community-page.component.ts @@ -7,10 +7,11 @@ import { CommunityPageComponent } from './community-page.component'; * Themed wrapper for CommunityPageComponent */ @Component({ - selector: 'ds-themed-community-page', + selector: 'ds-community-page', styleUrls: [], templateUrl: '../shared/theme-support/themed.component.html', standalone: true, + imports: [CommunityPageComponent], }) export class ThemedCommunityPageComponent extends ThemedComponent { protected getComponentName(): string { diff --git a/src/app/core/auth/models/auth-status.model.ts b/src/app/core/auth/models/auth-status.model.ts index 1a6938887d..f25e5a4892 100644 --- a/src/app/core/auth/models/auth-status.model.ts +++ b/src/app/core/auth/models/auth-status.model.ts @@ -36,14 +36,14 @@ export class AuthStatus implements CacheableObject { * The unique identifier of this auth status */ @autoserialize - id: string; + id: string; /** * The type for this AuthStatus */ @excludeFromEquals @autoserialize - type: ResourceType; + type: ResourceType; /** * The UUID of this auth status @@ -57,19 +57,19 @@ export class AuthStatus implements CacheableObject { * True if REST API is up and running, should never return false */ @autoserialize - okay: boolean; + okay: boolean; /** * If the auth status represents an authenticated state */ @autoserialize - authenticated: boolean; + authenticated: boolean; /** * The {@link HALLink}s for this AuthStatus */ @deserialize - _links: { + _links: { self: HALLink; eperson: HALLink; specialGroups: HALLink; @@ -80,32 +80,32 @@ export class AuthStatus implements CacheableObject { * Will be undefined unless the eperson {@link HALLink} has been resolved. */ @link(EPERSON) - eperson?: Observable>; + eperson?: Observable>; /** * The SpecialGroup of this auth status * Will be undefined unless the SpecialGroup {@link HALLink} has been resolved. */ @link(GROUP, true) - specialGroups?: Observable>>; + specialGroups?: Observable>>; /** * True if the token is valid, false if there was no token or the token wasn't valid */ @autoserialize - token?: AuthTokenInfo; + token?: AuthTokenInfo; /** * Authentication error if there was one for this status */ // TODO should be refactored to use the RemoteData error @autoserialize - error?: AuthError; + error?: AuthError; /** * All authentication methods enabled at the backend */ @autoserialize - authMethods: AuthMethod[]; + authMethods: AuthMethod[]; } diff --git a/src/app/core/auth/models/short-lived-token.model.ts b/src/app/core/auth/models/short-lived-token.model.ts index d91a26e990..5e8587d02d 100644 --- a/src/app/core/auth/models/short-lived-token.model.ts +++ b/src/app/core/auth/models/short-lived-token.model.ts @@ -22,19 +22,19 @@ export class ShortLivedToken implements CacheableObject { */ @excludeFromEquals @autoserialize - type: ResourceType; + type: ResourceType; /** * The value for this ShortLivedToken */ @autoserializeAs('token') - value: string; + value: string; /** * The {@link HALLink}s for this ShortLivedToken */ @deserialize - _links: { + _links: { self: HALLink; }; } diff --git a/src/app/core/cache/models/self-link.model.ts b/src/app/core/cache/models/self-link.model.ts index 903a779495..a87acdd506 100644 --- a/src/app/core/cache/models/self-link.model.ts +++ b/src/app/core/cache/models/self-link.model.ts @@ -3,9 +3,9 @@ import { autoserialize } from 'cerialize'; export class SelfLink { @autoserialize - self: string; + self: string; @autoserialize - uuid: string; + uuid: string; } diff --git a/src/app/core/config/models/bulk-access-condition-options.model.ts b/src/app/core/config/models/bulk-access-condition-options.model.ts index c491343852..514c682b4e 100644 --- a/src/app/core/config/models/bulk-access-condition-options.model.ts +++ b/src/app/core/config/models/bulk-access-condition-options.model.ts @@ -25,19 +25,19 @@ export class BulkAccessConditionOptions extends ConfigObject { */ @excludeFromEquals @autoserialize - type: ResourceType; + type: ResourceType; @autoserializeAs(String, 'name') - uuid: string; + uuid: string; @autoserialize - id: string; + id: string; @autoserialize - itemAccessConditionOptions: AccessesConditionOption[]; + itemAccessConditionOptions: AccessesConditionOption[]; @autoserialize - bitstreamAccessConditionOptions: AccessesConditionOption[]; + bitstreamAccessConditionOptions: AccessesConditionOption[]; _links: { self: HALLink }; } diff --git a/src/app/core/config/models/config-submission-access.model.ts b/src/app/core/config/models/config-submission-access.model.ts index 4617dc4719..2e9a929183 100644 --- a/src/app/core/config/models/config-submission-access.model.ts +++ b/src/app/core/config/models/config-submission-access.model.ts @@ -22,25 +22,25 @@ export class SubmissionAccessModel extends ConfigObject { * A list of available item access conditions */ @autoserialize - accessConditionOptions: AccessesConditionOption[]; + accessConditionOptions: AccessesConditionOption[]; /** * Boolean that indicates whether the current item must be findable via search or browse. */ @autoserialize - discoverable: boolean; + discoverable: boolean; /** * Boolean that indicates whether or not the user can change the discoverable flag. */ @autoserialize - canChangeDiscoverable: boolean; + canChangeDiscoverable: boolean; /** * The links to all related resources returned by the rest api. */ @deserialize - _links: { + _links: { self: HALLink }; diff --git a/src/app/core/config/models/config-submission-definition.model.ts b/src/app/core/config/models/config-submission-definition.model.ts index 2d6b1ad604..eda4f54340 100644 --- a/src/app/core/config/models/config-submission-definition.model.ts +++ b/src/app/core/config/models/config-submission-definition.model.ts @@ -23,20 +23,20 @@ export class SubmissionDefinitionModel extends ConfigObject { * A boolean representing if this submission definition is the default or not */ @autoserialize - isDefault: boolean; + isDefault: boolean; /** * A list of SubmissionSectionModel that are present in this submission definition */ // TODO refactor using remotedata @deserialize - sections: PaginatedList; + sections: PaginatedList; /** * The links to all related resources returned by the rest api. */ @deserialize - _links: { + _links: { self: HALLink, collections: HALLink, sections: HALLink diff --git a/src/app/core/config/models/config-submission-form.model.ts b/src/app/core/config/models/config-submission-form.model.ts index f6011adc76..c524a83916 100644 --- a/src/app/core/config/models/config-submission-form.model.ts +++ b/src/app/core/config/models/config-submission-form.model.ts @@ -27,5 +27,5 @@ export class SubmissionFormModel extends ConfigObject { * An array of [FormRowModel] that are present in this form */ @autoserialize - rows: FormRowModel[]; + rows: FormRowModel[]; } diff --git a/src/app/core/config/models/config-submission-section.model.ts b/src/app/core/config/models/config-submission-section.model.ts index 0d4ae9aa10..feb4fc9cb0 100644 --- a/src/app/core/config/models/config-submission-section.model.ts +++ b/src/app/core/config/models/config-submission-section.model.ts @@ -27,31 +27,31 @@ export class SubmissionSectionModel extends ConfigObject { * The header for this section */ @autoserialize - header: string; + header: string; /** * A boolean representing if this submission section is the mandatory or not */ @autoserialize - mandatory: boolean; + mandatory: boolean; /** * A string representing the kind of section object */ @autoserialize - sectionType: SectionsType; + sectionType: SectionsType; /** * The [SubmissionSectionVisibility] object for this section */ @autoserialize - visibility: SubmissionSectionVisibility; + visibility: SubmissionSectionVisibility; /** * The {@link HALLink}s for this SubmissionSectionModel */ @deserialize - _links: { + _links: { self: HALLink; config: HALLink; }; diff --git a/src/app/core/config/models/config-submission-upload.model.ts b/src/app/core/config/models/config-submission-upload.model.ts index edc4626f83..cabc84d0f5 100644 --- a/src/app/core/config/models/config-submission-upload.model.ts +++ b/src/app/core/config/models/config-submission-upload.model.ts @@ -27,22 +27,22 @@ export class SubmissionUploadModel extends ConfigObject { * A list of available bitstream access conditions */ @autoserialize - accessConditionOptions: AccessConditionOption[]; + accessConditionOptions: AccessConditionOption[]; /** * An object representing the configuration describing the bitstream metadata form */ @link(SUBMISSION_FORMS_TYPE) - metadata?: Observable>; + metadata?: Observable>; @autoserialize - required: boolean; + required: boolean; @autoserialize - maxSize: number; + maxSize: number; @deserialize - _links: { + _links: { metadata: HALLink self: HALLink }; diff --git a/src/app/core/config/models/config.model.ts b/src/app/core/config/models/config.model.ts index c1db44e891..74d090b89d 100644 --- a/src/app/core/config/models/config.model.ts +++ b/src/app/core/config/models/config.model.ts @@ -27,13 +27,13 @@ export abstract class ConfigObject implements CacheableObject { */ @excludeFromEquals @autoserialize - type: ResourceType; + type: ResourceType; /** * The links to all related resources returned by the rest api. */ @deserialize - _links: { + _links: { self: HALLink, [name: string]: HALLink }; diff --git a/src/app/core/data/feature-authorization/authorization-utils.ts b/src/app/core/data/feature-authorization/authorization-utils.ts index f763b1a38d..cd4bc452a5 100644 --- a/src/app/core/data/feature-authorization/authorization-utils.ts +++ b/src/app/core/data/feature-authorization/authorization-utils.ts @@ -91,5 +91,5 @@ export const oneAuthorizationMatchesFeature = (featureID: FeatureID) => return observableOf([]); } }), - map((features: Feature[]) => features.filter((feature: Feature) => feature.id === featureID).length > 0), + map((features: Feature[]) => features.filter((feature: Feature) => feature.id === featureID.valueOf()).length > 0), ); diff --git a/src/app/core/data/paginated-list.model.ts b/src/app/core/data/paginated-list.model.ts index e412af4986..ef2819afd8 100644 --- a/src/app/core/data/paginated-list.model.ts +++ b/src/app/core/data/paginated-list.model.ts @@ -73,13 +73,13 @@ export class PaginatedList extends CacheableObject { * The type of the list */ @excludeFromEquals - type = PAGINATED_LIST; + type = PAGINATED_LIST; /** * The type of objects in the list */ @autoserialize - objectType?: ResourceType; + objectType?: ResourceType; /** * The list of objects that represents the current page @@ -90,13 +90,13 @@ export class PaginatedList extends CacheableObject { * the {@link PageInfo} object */ @autoserialize - pageInfo?: PageInfo; + pageInfo?: PageInfo; /** * The {@link HALLink}s for this PaginatedList */ @deserialize - _links: { + _links: { self: HALLink; page: HALLink[]; first?: HALLink; diff --git a/src/app/core/data/root.model.ts b/src/app/core/data/root.model.ts index 46a276fa1f..ced115b055 100644 --- a/src/app/core/data/root.model.ts +++ b/src/app/core/data/root.model.ts @@ -22,37 +22,37 @@ export class Root implements CacheableObject { */ @excludeFromEquals @autoserialize - type: ResourceType; + type: ResourceType; /** * The url for the dspace UI */ @autoserialize - dspaceUI: string; + dspaceUI: string; /** * The repository Name */ @autoserialize - dspaceName: string; + dspaceName: string; /** * The url for the rest api */ @autoserialize - dspaceServer: string; + dspaceServer: string; /** * The current DSpace version */ @autoserialize - dspaceVersion: string; + dspaceVersion: string; /** * The {@link HALLink}s for the root object */ @deserialize - _links: { + _links: { self: HALLink; [k: string]: HALLink | HALLink[]; }; diff --git a/src/app/core/dspace-rest/dspace-rest.service.ts b/src/app/core/dspace-rest/dspace-rest.service.ts index 67d74e9570..a75cef53bf 100644 --- a/src/app/core/dspace-rest/dspace-rest.service.ts +++ b/src/app/core/dspace-rest/dspace-rest.service.ts @@ -151,7 +151,7 @@ export class DspaceRestService { return form; } - protected handleHttpError(err: unknown): RequestError | unknown { + protected handleHttpError(err: unknown): unknown { if (err instanceof HttpErrorResponse) { const error = new RequestError( (isNotEmpty(err?.error?.message)) ? err.error.message : err.message, diff --git a/src/app/core/dspace-rest/dspace.serializer.spec.ts b/src/app/core/dspace-rest/dspace.serializer.spec.ts index 9a6635bc98..169dd97a57 100644 --- a/src/app/core/dspace-rest/dspace.serializer.spec.ts +++ b/src/app/core/dspace-rest/dspace.serializer.spec.ts @@ -9,13 +9,13 @@ import { DSpaceSerializer } from './dspace.serializer'; class TestModel implements HALResource { @autoserialize - id: string; + id: string; @autoserialize - name: string; + name: string; @deserialize - _links: { + _links: { self: HALLink; parents: HALLink; }; diff --git a/src/app/core/eperson/models/group.model.ts b/src/app/core/eperson/models/group.model.ts index bed55af0a2..71923d6c55 100644 --- a/src/app/core/eperson/models/group.model.ts +++ b/src/app/core/eperson/models/group.model.ts @@ -48,7 +48,7 @@ export class Group extends DSpaceObject { * The {@link HALLink}s for this Group */ @deserialize - _links: { + _links: { self: HALLink; subgroups: HALLink; epersons: HALLink; diff --git a/src/app/core/metadata/metadata-field.model.ts b/src/app/core/metadata/metadata-field.model.ts index 1084b4009f..11b6e1c74e 100644 --- a/src/app/core/metadata/metadata-field.model.ts +++ b/src/app/core/metadata/metadata-field.model.ts @@ -32,37 +32,37 @@ export class MetadataField extends ListableObject implements HALResource { */ @excludeFromEquals @autoserialize - type: ResourceType; + type: ResourceType; /** * The identifier of this metadata field */ @autoserialize - id: number; + id: number; /** * The element of this metadata field */ @autoserialize - element: string; + element: string; /** * The qualifier of this metadata field */ @autoserialize - qualifier: string; + qualifier: string; /** * The scope note of this metadata field */ @autoserialize - scopeNote: string; + scopeNote: string; /** * The {@link HALLink}s for this MetadataField */ @deserialize - _links: { + _links: { self: HALLink, schema: HALLink }; @@ -72,7 +72,7 @@ export class MetadataField extends ListableObject implements HALResource { * Will be undefined unless the schema {@link HALLink} has been resolved. */ @link(METADATA_SCHEMA) - schema?: Observable>; + schema?: Observable>; /** * Method to print this metadata field as a string without the schema diff --git a/src/app/core/metadata/metadata-schema.model.ts b/src/app/core/metadata/metadata-schema.model.ts index 85faf5d2ce..656638b4c6 100644 --- a/src/app/core/metadata/metadata-schema.model.ts +++ b/src/app/core/metadata/metadata-schema.model.ts @@ -23,29 +23,29 @@ export class MetadataSchema extends ListableObject implements HALResource { * The unique identifier for this metadata schema */ @autoserialize - id: number; + id: number; /** * The object type */ @excludeFromEquals @autoserialize - type: ResourceType; + type: ResourceType; /** * A unique prefix that defines this schema */ @autoserialize - prefix: string; + prefix: string; /** * The namespace of this metadata schema */ @autoserialize - namespace: string; + namespace: string; @deserialize - _links: { + _links: { self: HALLink, }; diff --git a/src/app/core/notifications/models/suggestion-source.model.ts b/src/app/core/notifications/models/suggestion-source.model.ts index f8e7c70cf5..1a757a5aa0 100644 --- a/src/app/core/notifications/models/suggestion-source.model.ts +++ b/src/app/core/notifications/models/suggestion-source.model.ts @@ -24,26 +24,26 @@ export class SuggestionSource implements CacheableObject { * The Suggestion Target id */ @autoserialize - id: string; + id: string; /** * The total number of suggestions provided by Suggestion Target for */ @autoserialize - total: number; + total: number; /** * The type of this ConfigObject */ @excludeFromEquals @autoserialize - type: ResourceType; + type: ResourceType; /** * The links to all related resources returned by the rest api. */ @deserialize - _links: { + _links: { self: HALLink, suggestiontargets: HALLink }; diff --git a/src/app/core/notifications/models/suggestion-target.model.ts b/src/app/core/notifications/models/suggestion-target.model.ts index 1cbd78ae6b..cdc78eb466 100644 --- a/src/app/core/notifications/models/suggestion-target.model.ts +++ b/src/app/core/notifications/models/suggestion-target.model.ts @@ -24,38 +24,38 @@ export class SuggestionTarget implements CacheableObject { * The Suggestion Target id */ @autoserialize - id: string; + id: string; /** * The Suggestion Target name to display */ @autoserialize - display: string; + display: string; /** * The Suggestion Target source to display */ @autoserialize - source: string; + source: string; /** * The total number of suggestions provided by Suggestion Target for */ @autoserialize - total: number; + total: number; /** * The type of this ConfigObject */ @excludeFromEquals @autoserialize - type: ResourceType; + type: ResourceType; /** * The links to all related resources returned by the rest api. */ @deserialize - _links: { + _links: { self: HALLink, suggestions: HALLink, target: HALLink diff --git a/src/app/core/notifications/models/suggestion.model.ts b/src/app/core/notifications/models/suggestion.model.ts index a84fe9ffe2..bd4015b499 100644 --- a/src/app/core/notifications/models/suggestion.model.ts +++ b/src/app/core/notifications/models/suggestion.model.ts @@ -38,57 +38,57 @@ export class Suggestion implements CacheableObject { * The Suggestion id */ @autoserialize - id: string; + id: string; /** * The Suggestion name to display */ @autoserialize - display: string; + display: string; /** * The Suggestion source to display */ @autoserialize - source: string; + source: string; /** * The Suggestion external source uri */ @autoserialize - externalSourceUri: string; + externalSourceUri: string; /** * The Total Score of the suggestion */ @autoserialize - score: string; + score: string; /** * The total number of suggestions provided by Suggestion Target for */ @autoserialize - evidences: SuggestionEvidences; + evidences: SuggestionEvidences; /** * All metadata of this suggestion object */ @excludeFromEquals @autoserializeAs(MetadataMapSerializer) - metadata: MetadataMap; + metadata: MetadataMap; /** * The type of this ConfigObject */ @excludeFromEquals @autoserialize - type: ResourceType; + type: ResourceType; /** * The links to all related resources returned by the rest api. */ @deserialize - _links: { + _links: { self: HALLink, target: HALLink }; diff --git a/src/app/core/notifications/qa/models/quality-assurance-event.model.ts b/src/app/core/notifications/qa/models/quality-assurance-event.model.ts index 9210d8f6f9..e015dd01d9 100644 --- a/src/app/core/notifications/qa/models/quality-assurance-event.model.ts +++ b/src/app/core/notifications/qa/models/quality-assurance-event.model.ts @@ -104,62 +104,62 @@ export class QualityAssuranceEventObject implements CacheableObject { * The Quality Assurance event uuid inside DSpace */ @autoserialize - id: string; + id: string; /** * The universally unique identifier of this Quality Assurance event */ @autoserializeAs(String, 'id') - uuid: string; + uuid: string; /** * The Quality Assurance event original id (ex.: the source archive OAI-PMH identifier) */ @autoserialize - originalId: string; + originalId: string; /** * The title of the article to which the suggestion refers */ @autoserialize - title: string; + title: string; /** * Reliability of the suggestion (of the data inside 'message') */ @autoserialize - trust: number; + trust: number; /** * The timestamp Quality Assurance event was saved in DSpace */ @autoserialize - eventDate: string; + eventDate: string; /** * The Quality Assurance event status (ACCEPTED, REJECTED, DISCARDED, PENDING) */ @autoserialize - status: string; + status: string; /** * The suggestion data. Data may vary depending on the source */ @autoserialize - message: SourceQualityAssuranceEventMessageObject; + message: SourceQualityAssuranceEventMessageObject; /** * The type of this ConfigObject */ @excludeFromEquals @autoserialize - type: ResourceType; + type: ResourceType; /** * The links to all related resources returned by the rest api. */ @deserialize - _links: { + _links: { self: HALLink, target: HALLink, related: HALLink @@ -170,12 +170,12 @@ export class QualityAssuranceEventObject implements CacheableObject { * Will be undefined unless the {@item HALLink} has been resolved. */ @link(ITEM) - target?: Observable>; + target?: Observable>; /** * The related project for this Event * Will be undefined unless the {@related HALLink} has been resolved. */ @link(ITEM) - related?: Observable>; + related?: Observable>; } diff --git a/src/app/core/notifications/qa/models/quality-assurance-source.model.ts b/src/app/core/notifications/qa/models/quality-assurance-source.model.ts index cc5f874119..56c674b031 100644 --- a/src/app/core/notifications/qa/models/quality-assurance-source.model.ts +++ b/src/app/core/notifications/qa/models/quality-assurance-source.model.ts @@ -24,32 +24,32 @@ export class QualityAssuranceSourceObject implements CacheableObject { * The Quality Assurance source id */ @autoserialize - id: string; + id: string; /** * The date of the last udate from Notifications */ @autoserialize - lastEvent: string; + lastEvent: string; /** * The total number of suggestions provided by Notifications for this source */ @autoserialize - totalEvents: number; + totalEvents: number; /** * The type of this ConfigObject */ @excludeFromEquals @autoserialize - type: ResourceType; + type: ResourceType; /** * The links to all related resources returned by the rest api. */ @deserialize - _links: { + _links: { self: HALLink, }; } diff --git a/src/app/core/notifications/qa/models/quality-assurance-topic.model.ts b/src/app/core/notifications/qa/models/quality-assurance-topic.model.ts index ddc027d194..0d4235a531 100644 --- a/src/app/core/notifications/qa/models/quality-assurance-topic.model.ts +++ b/src/app/core/notifications/qa/models/quality-assurance-topic.model.ts @@ -24,38 +24,38 @@ export class QualityAssuranceTopicObject implements CacheableObject { * The Quality Assurance topic id */ @autoserialize - id: string; + id: string; /** * The Quality Assurance topic name to display */ @autoserialize - name: string; + name: string; /** * The date of the last udate from Notifications */ @autoserialize - lastEvent: string; + lastEvent: string; /** * The total number of suggestions provided by Notifications for this topic */ @autoserialize - totalEvents: number; + totalEvents: number; /** * The type of this ConfigObject */ @excludeFromEquals @autoserialize - type: ResourceType; + type: ResourceType; /** * The links to all related resources returned by the rest api. */ @deserialize - _links: { + _links: { self: HALLink, }; } diff --git a/src/app/core/orcid/model/orcid-history.model.ts b/src/app/core/orcid/model/orcid-history.model.ts index 62f5353ed2..aa8c4b41ba 100644 --- a/src/app/core/orcid/model/orcid-history.model.ts +++ b/src/app/core/orcid/model/orcid-history.model.ts @@ -23,49 +23,49 @@ export class OrcidHistory extends CacheableObject { */ @excludeFromEquals @autoserialize - type: ResourceType; + type: ResourceType; /** * The identifier of this Orcid History record */ @autoserialize - id: number; + id: number; /** * The name of the related entity */ @autoserialize - entityName: string; + entityName: string; /** * The identifier of the profileItem of this Orcid History record. */ @autoserialize - profileItemId: string; + profileItemId: string; /** * The identifier of the entity related to this Orcid History record. */ @autoserialize - entityId: string; + entityId: string; /** * The type of the entity related to this Orcid History record. */ @autoserialize - entityType: string; + entityType: string; /** * The response status coming from ORCID api. */ @autoserialize - status: number; + status: number; /** * The putCode assigned by ORCID to the entity. */ @autoserialize - putCode: string; + putCode: string; /** * The last send attempt timestamp. @@ -86,7 +86,7 @@ export class OrcidHistory extends CacheableObject { * The {@link HALLink}s for this Orcid History record */ @deserialize - _links: { + _links: { self: HALLink, }; diff --git a/src/app/core/orcid/model/orcid-queue.model.ts b/src/app/core/orcid/model/orcid-queue.model.ts index fedb078cc5..2735114f22 100644 --- a/src/app/core/orcid/model/orcid-queue.model.ts +++ b/src/app/core/orcid/model/orcid-queue.model.ts @@ -23,49 +23,49 @@ export class OrcidQueue extends CacheableObject { */ @excludeFromEquals @autoserialize - type: ResourceType; + type: ResourceType; /** * The identifier of this Orcid Queue record */ @autoserialize - id: number; + id: number; /** * The record description. */ @autoserialize - description: string; + description: string; /** * The identifier of the profileItem of this Orcid Queue record. */ @autoserialize - profileItemId: string; + profileItemId: string; /** * The identifier of the entity related to this Orcid Queue record. */ @autoserialize - entityId: string; + entityId: string; /** * The type of this Orcid Queue record. */ @autoserialize - recordType: string; + recordType: string; /** * The operation related to this Orcid Queue record. */ @autoserialize - operation: string; + operation: string; /** * The {@link HALLink}s for this Orcid Queue record */ @deserialize - _links: { + _links: { self: HALLink, }; diff --git a/src/app/core/profile/model/researcher-profile.model.ts b/src/app/core/profile/model/researcher-profile.model.ts index d26156123c..28ffd09a7b 100644 --- a/src/app/core/profile/model/researcher-profile.model.ts +++ b/src/app/core/profile/model/researcher-profile.model.ts @@ -31,28 +31,28 @@ export class ResearcherProfile extends CacheableObject { */ @excludeFromEquals @autoserialize - type: ResourceType; + type: ResourceType; /** * The identifier of this Researcher Profile */ @autoserialize - id: string; + id: string; @deserializeAs('id') - uuid: string; + uuid: string; /** * The visibility of this Researcher Profile */ @autoserialize - visible: boolean; + visible: boolean; /** * The {@link HALLink}s for this Researcher Profile */ @deserialize - _links: { + _links: { self: HALLink, item: HALLink, eperson: HALLink @@ -63,6 +63,6 @@ export class ResearcherProfile extends CacheableObject { * Will be undefined unless the item {@link HALLink} has been resolved. */ @link(ITEM) - item?: Observable>; + item?: Observable>; } diff --git a/src/app/core/resource-policy/models/resource-policy.model.ts b/src/app/core/resource-policy/models/resource-policy.model.ts index d4558cfe3a..669844690b 100644 --- a/src/app/core/resource-policy/models/resource-policy.model.ts +++ b/src/app/core/resource-policy/models/resource-policy.model.ts @@ -34,50 +34,50 @@ export class ResourcePolicy implements CacheableObject { * The identifier for this Resource Policy */ @autoserialize - id: string; + id: string; /** * The name for this Resource Policy */ @autoserialize - name: string; + name: string; /** * The description for this Resource Policy */ @autoserialize - description: string; + description: string; /** * The classification or this Resource Policy */ @autoserialize - policyType: PolicyType; + policyType: PolicyType; /** * The action that is allowed by this Resource Policy */ @autoserialize - action: ActionType; + action: ActionType; /** * The first day of validity of the policy (format YYYY-MM-DD) */ @autoserialize - startDate: string; + startDate: string; /** * The last day of validity of the policy (format YYYY-MM-DD) */ @autoserialize - endDate: string; + endDate: string; /** * The object type */ @excludeFromEquals @autoserialize - type: ResourceType; + type: ResourceType; /** * The universally unique identifier for this Resource Policy @@ -91,7 +91,7 @@ export class ResourcePolicy implements CacheableObject { * The {@link HALLink}s for this ResourcePolicy */ @deserialize - _links: { + _links: { eperson: HALLink, group: HALLink, self: HALLink, @@ -102,12 +102,12 @@ export class ResourcePolicy implements CacheableObject { * Will be undefined unless the version {@link HALLink} has been resolved. */ @link(EPERSON) - eperson?: Observable>; + eperson?: Observable>; /** * The group linked by this resource policy * Will be undefined unless the version {@link HALLink} has been resolved. */ @link(GROUP) - group?: Observable>; + group?: Observable>; } diff --git a/src/app/core/shared/authorization.model.ts b/src/app/core/shared/authorization.model.ts index 0031b054f6..1712db9a07 100644 --- a/src/app/core/shared/authorization.model.ts +++ b/src/app/core/shared/authorization.model.ts @@ -31,10 +31,10 @@ export class Authorization extends DSpaceObject { * Unique identifier for this authorization */ @autoserialize - id: string; + id: string; @deserialize - _links: { + _links: { self: HALLink; eperson: HALLink; feature: HALLink; @@ -46,17 +46,17 @@ export class Authorization extends DSpaceObject { * Null if the authorization grants access to anonymous users */ @link(EPERSON) - eperson?: Observable>; + eperson?: Observable>; /** * The Feature enabled by this Authorization */ @link(FEATURE) - feature?: Observable>; + feature?: Observable>; /** * The Object this authorization applies to */ @link(ITEM) - object?: Observable>; + object?: Observable>; } diff --git a/src/app/core/shared/bitstream-format.model.ts b/src/app/core/shared/bitstream-format.model.ts index 354edb4cbd..44ba85c974 100644 --- a/src/app/core/shared/bitstream-format.model.ts +++ b/src/app/core/shared/bitstream-format.model.ts @@ -25,43 +25,43 @@ export class BitstreamFormat implements CacheableObject { */ @excludeFromEquals @autoserialize - type: ResourceType; + type: ResourceType; /** * Short description of this Bitstream Format */ @autoserialize - shortDescription: string; + shortDescription: string; /** * Description of this Bitstream Format */ @autoserialize - description: string; + description: string; /** * String representing the MIME type of this Bitstream Format */ @autoserialize - mimetype: string; + mimetype: string; /** * The level of support the system offers for this Bitstream Format */ @autoserialize - supportLevel: BitstreamFormatSupportLevel; + supportLevel: BitstreamFormatSupportLevel; /** * True if the Bitstream Format is used to store system information, rather than the content of items in the system */ @autoserialize - internal: boolean; + internal: boolean; /** * String representing this Bitstream Format's file extension */ @autoserialize - extensions: string[]; + extensions: string[]; /** * Universally unique identifier for this Bitstream Format @@ -77,13 +77,13 @@ export class BitstreamFormat implements CacheableObject { * but might not be unique across different object types */ @autoserialize - id: string; + id: string; /** * The {@link HALLink}s for this BitstreamFormat */ @deserialize - _links: { + _links: { self: HALLink; }; } diff --git a/src/app/core/shared/bitstream.model.ts b/src/app/core/shared/bitstream.model.ts index d1b821e8cd..73e5e04b36 100644 --- a/src/app/core/shared/bitstream.model.ts +++ b/src/app/core/shared/bitstream.model.ts @@ -28,25 +28,25 @@ export class Bitstream extends DSpaceObject implements ChildHALResource { * The size of this bitstream in bytes */ @autoserialize - sizeBytes: number; + sizeBytes: number; /** * The description of this Bitstream */ @autoserialize - description: string; + description: string; /** * The name of the Bundle this Bitstream is part of */ @autoserialize - bundleName: string; + bundleName: string; /** * The {@link HALLink}s for this Bitstream */ @deserialize - _links: { + _links: { self: HALLink; bundle: HALLink; format: HALLink; @@ -59,21 +59,21 @@ export class Bitstream extends DSpaceObject implements ChildHALResource { * Will be undefined unless the thumbnail {@link HALLink} has been resolved. */ @link(BITSTREAM, false, 'thumbnail') - thumbnail?: Observable>; + thumbnail?: Observable>; /** * The BitstreamFormat of this Bitstream * Will be undefined unless the format {@link HALLink} has been resolved. */ @link(BITSTREAM_FORMAT, false, 'format') - format?: Observable>; + format?: Observable>; /** * The owning bundle for this Bitstream * Will be undefined unless the bundle{@link HALLink} has been resolved. */ @link(BUNDLE) - bundle?: Observable>; + bundle?: Observable>; getParentLinkKey(): keyof this['_links'] { return 'format'; diff --git a/src/app/core/shared/browse-definition.model.ts b/src/app/core/shared/browse-definition.model.ts index 8e50a57bb5..6de81727fa 100644 --- a/src/app/core/shared/browse-definition.model.ts +++ b/src/app/core/shared/browse-definition.model.ts @@ -9,7 +9,7 @@ import { CacheableObject } from '../cache/cacheable-object.model'; export abstract class BrowseDefinition extends CacheableObject { @autoserialize - id: string; + id: string; /** * Get the render type of the BrowseDefinition model diff --git a/src/app/core/shared/browse-entry.model.ts b/src/app/core/shared/browse-entry.model.ts index 03e3ab3f5b..746333872e 100644 --- a/src/app/core/shared/browse-entry.model.ts +++ b/src/app/core/shared/browse-entry.model.ts @@ -25,41 +25,41 @@ export class BrowseEntry extends ListableObject implements TypedObject { */ @excludeFromEquals @autoserialize - type: ResourceType; + type: ResourceType; /** * The authority string of this browse entry */ @autoserialize - authority: string; + authority: string; /** * The value of this browse entry */ @autoserialize - value: string; + value: string; /** * The language of the value of this browse entry */ @autoserializeAs('valueLang') - language: string; + language: string; /** * Thumbnail link used when browsing items with showThumbs config enabled. */ @autoserializeAs('thumbnail') - thumbnail: string; + thumbnail: string; /** * The count of this browse entry */ @excludeFromEquals @autoserialize - count: number; + count: number; @deserialize - _links: { + _links: { self: HALLink; entries: HALLink; thumbnail: HALLink; diff --git a/src/app/core/shared/bundle.model.ts b/src/app/core/shared/bundle.model.ts index e5b92799d4..d9ea6d83dc 100644 --- a/src/app/core/shared/bundle.model.ts +++ b/src/app/core/shared/bundle.model.ts @@ -27,7 +27,7 @@ export class Bundle extends DSpaceObject { * The {@link HALLink}s for this Bundle */ @deserialize - _links: { + _links: { self: HALLink; primaryBitstream: HALLink; bitstreams: HALLink; @@ -39,19 +39,19 @@ export class Bundle extends DSpaceObject { * Will be undefined unless the primaryBitstream {@link HALLink} has been resolved. */ @link(BITSTREAM) - primaryBitstream?: Observable>; + primaryBitstream?: Observable>; /** * The list of Bitstreams that are direct children of this Bundle * Will be undefined unless the bitstreams {@link HALLink} has been resolved. */ @link(BITSTREAM, true) - bitstreams?: Observable>>; + bitstreams?: Observable>>; /** * The owning item for this Bundle * Will be undefined unless the Item{@link HALLink} has been resolved. */ @link(ITEM) - item?: Observable>; + item?: Observable>; } diff --git a/src/app/core/shared/collection.model.ts b/src/app/core/shared/collection.model.ts index 468b0ccc64..b929e54ccb 100644 --- a/src/app/core/shared/collection.model.ts +++ b/src/app/core/shared/collection.model.ts @@ -33,13 +33,13 @@ export class Collection extends DSpaceObject implements ChildHALResource, Handle @excludeFromEquals @autoserialize - archivedItemsCount: number; + archivedItemsCount: number; /** * The {@link HALLink}s for this Collection */ @deserialize - _links: { + _links: { license: HALLink; harvester: HALLink; mappedItems: HALLink; @@ -60,28 +60,28 @@ export class Collection extends DSpaceObject implements ChildHALResource, Handle * Will be undefined unless the license {@link HALLink} has been resolved. */ @link(LICENSE) - license?: Observable>; + license?: Observable>; /** * The logo for this Collection * Will be undefined unless the logo {@link HALLink} has been resolved. */ @link(BITSTREAM) - logo?: Observable>; + logo?: Observable>; /** * The default access conditions for this Collection * Will be undefined unless the defaultAccessConditions {@link HALLink} has been resolved. */ @link(RESOURCE_POLICY, true) - defaultAccessConditions?: Observable>>; + defaultAccessConditions?: Observable>>; /** * The Community that is a direct parent of this Collection * Will be undefined unless the parent community HALLink has been resolved. */ @link(COMMUNITY, false) - parentCommunity?: Observable>; + parentCommunity?: Observable>; /** * A string representing the unique handle of this Collection diff --git a/src/app/core/shared/community.model.ts b/src/app/core/shared/community.model.ts index 764b15e2ed..31b00398ff 100644 --- a/src/app/core/shared/community.model.ts +++ b/src/app/core/shared/community.model.ts @@ -29,13 +29,13 @@ export class Community extends DSpaceObject implements ChildHALResource, HandleO @excludeFromEquals @autoserialize - archivedItemsCount: number; + archivedItemsCount: number; /** * The {@link HALLink}s for this Community */ @deserialize - _links: { + _links: { collections: HALLink; logo: HALLink; subcommunities: HALLink; @@ -49,28 +49,28 @@ export class Community extends DSpaceObject implements ChildHALResource, HandleO * Will be undefined unless the logo {@link HALLink} has been resolved. */ @link(BITSTREAM) - logo?: Observable>; + logo?: Observable>; /** * The list of Collections that are direct children of this Community * Will be undefined unless the collections {@link HALLink} has been resolved. */ @link(COLLECTION, true) - collections?: Observable>>; + collections?: Observable>>; /** * The list of Communities that are direct children of this Community * Will be undefined unless the subcommunities {@link HALLink} has been resolved. */ @link(COMMUNITY, true) - subcommunities?: Observable>>; + subcommunities?: Observable>>; /** * The Community that is a direct parent of this Community * Will be undefined unless the parent community HALLink has been resolved. */ @link(COMMUNITY, false) - parentCommunity?: Observable>; + parentCommunity?: Observable>; /** * A string representing the unique handle of this Community diff --git a/src/app/core/shared/configuration-property.model.ts b/src/app/core/shared/configuration-property.model.ts index 2b6ab606c9..e15f8a5b85 100644 --- a/src/app/core/shared/configuration-property.model.ts +++ b/src/app/core/shared/configuration-property.model.ts @@ -23,31 +23,31 @@ export class ConfigurationProperty implements CacheableObject { */ @excludeFromEquals @autoserialize - type: ResourceType; + type: ResourceType; /** * The uuid of the configuration property * The name is used as id for configuration properties */ @autoserializeAs(String, 'name') - uuid: string; + uuid: string; /** * The name of the configuration property */ @autoserialize - name: string; + name: string; /** * The values of the configuration property */ @autoserialize - values: string[]; + values: string[]; /** * The links of the configuration property */ @deserialize - _links: { self: HALLink }; + _links: { self: HALLink }; } diff --git a/src/app/core/shared/content-source.model.ts b/src/app/core/shared/content-source.model.ts index ee535191d0..cfc78f223f 100644 --- a/src/app/core/shared/content-source.model.ts +++ b/src/app/core/shared/content-source.model.ts @@ -38,20 +38,20 @@ export class ContentSource extends CacheableObject { * and we need a custom responseparser for ContentSource responses */ @excludeFromEquals - type: ResourceType = CONTENT_SOURCE; + type: ResourceType = CONTENT_SOURCE; /** * Unique identifier, this is necessary to store the ContentSource in FieldUpdates * Because the ContentSource coming from the REST API doesn't have a UUID, we're using the selflink */ @deserializeAs('self') - uuid: string; + uuid: string; /** * OAI Provider / Source */ @autoserializeAs('oai_source') - oaiSource: string; + oaiSource: string; /** * OAI Specific set ID @@ -64,14 +64,14 @@ export class ContentSource extends CacheableObject { * The ID of the metadata format used */ @autoserializeAs('metadata_config_id') - metadataConfigId: string; + metadataConfigId: string; /** * Type of content being harvested * Defaults to 'NONE', meaning the collection doesn't harvest its content from an external source */ @autoserializeAs('harvest_type') - harvestType = ContentSourceHarvestType.None; + harvestType = ContentSourceHarvestType.None; /** * The available metadata configurations @@ -82,31 +82,31 @@ export class ContentSource extends CacheableObject { * The current harvest status */ @autoserializeAs('harvest_status') - harvestStatus: string; + harvestStatus: string; /** * The last's harvest start time */ @autoserializeAs('harvest_start_time') - harvestStartTime: string; + harvestStartTime: string; /** * When the collection was last harvested */ @autoserializeAs('last_harvested') - lastHarvested: string; + lastHarvested: string; /** * The current harvest message */ @autoserializeAs('harvest_message') - message: string; + message: string; /** * The {@link HALLink}s for this ContentSource */ @deserialize - _links: { + _links: { self: HALLink }; } diff --git a/src/app/core/shared/dspace-object.model.ts b/src/app/core/shared/dspace-object.model.ts index dc9b7f5fca..7bc05b1d3a 100644 --- a/src/app/core/shared/dspace-object.model.ts +++ b/src/app/core/shared/dspace-object.model.ts @@ -46,20 +46,20 @@ export class DSpaceObject extends ListableObject implements CacheableObject { */ @excludeFromEquals @autoserializeAs(String, 'uuid') - id: string; + id: string; /** * The universally unique ide ntifier of this DSpaceObject */ @autoserializeAs(String) - uuid: string; + uuid: string; /** * A string representing the kind of DSpaceObject, e.g. community, item, … */ @excludeFromEquals @autoserialize - type: ResourceType; + type: ResourceType; /** * A shorthand to get this DSpaceObject's self link @@ -100,10 +100,10 @@ export class DSpaceObject extends ListableObject implements CacheableObject { */ @excludeFromEquals @autoserializeAs(MetadataMapSerializer) - metadata: MetadataMap; + metadata: MetadataMap; @deserialize - _links: { + _links: { self: HALLink; }; diff --git a/src/app/core/shared/external-source-entry.model.ts b/src/app/core/shared/external-source-entry.model.ts index d743a92310..01e52d30d5 100644 --- a/src/app/core/shared/external-source-entry.model.ts +++ b/src/app/core/shared/external-source-entry.model.ts @@ -27,44 +27,44 @@ export class ExternalSourceEntry extends ListableObject { * Unique identifier */ @autoserialize - id: string; + id: string; /** * The object type */ @excludeFromEquals @autoserialize - type: ResourceType; + type: ResourceType; /** * The value to display */ @autoserialize - display: string; + display: string; /** * The value to store the entry with */ @autoserialize - value: string; + value: string; /** * The ID of the external source this entry originates from */ @autoserialize - externalSource: string; + externalSource: string; /** * Metadata of the entry */ @autoserializeAs(MetadataMapSerializer) - metadata: MetadataMap; + metadata: MetadataMap; /** * The {@link HALLink}s for this ExternalSourceEntry */ @deserialize - _links: { + _links: { self: HALLink; }; diff --git a/src/app/core/shared/external-source.model.ts b/src/app/core/shared/external-source.model.ts index 70766e0494..40661917e2 100644 --- a/src/app/core/shared/external-source.model.ts +++ b/src/app/core/shared/external-source.model.ts @@ -30,38 +30,38 @@ export class ExternalSource extends CacheableObject { */ @excludeFromEquals @autoserialize - type: ResourceType; + type: ResourceType; /** * Unique identifier */ @autoserialize - id: string; + id: string; /** * The name of this external source */ @autoserialize - name: string; + name: string; /** * Is the source hierarchical? */ @autoserialize - hierarchical: boolean; + hierarchical: boolean; /** * The list of entity types that are compatible with this external source * Will be undefined unless the entityTypes {@link HALLink} has been resolved. */ @link(ITEM_TYPE, true) - entityTypes?: Observable>>; + entityTypes?: Observable>>; /** * The {@link HALLink}s for this ExternalSource */ @deserialize - _links: { + _links: { self: HALLink; entries: HALLink; entityTypes: HALLink; diff --git a/src/app/core/shared/feature.model.ts b/src/app/core/shared/feature.model.ts index 270eb2de9d..1c18d1478f 100644 --- a/src/app/core/shared/feature.model.ts +++ b/src/app/core/shared/feature.model.ts @@ -21,22 +21,22 @@ export class Feature extends DSpaceObject { * Unique identifier for this feature */ @autoserialize - id: string; + id: string; /** * A human readable description of the feature's purpose */ @autoserialize - description: string; + description: string; /** * A list of resource types this feature applies to */ @autoserialize - resourcetypes: string[]; + resourcetypes: string[]; @deserialize - _links: { + _links: { self: HALLink; }; } diff --git a/src/app/core/shared/flat-browse-definition.model.ts b/src/app/core/shared/flat-browse-definition.model.ts index d9d59d1114..74166f937c 100644 --- a/src/app/core/shared/flat-browse-definition.model.ts +++ b/src/app/core/shared/flat-browse-definition.model.ts @@ -23,14 +23,14 @@ export class FlatBrowseDefinition extends NonHierarchicalBrowseDefinition { * The object type */ @excludeFromEquals - type: ResourceType = FLAT_BROWSE_DEFINITION; + type: ResourceType = FLAT_BROWSE_DEFINITION; get self(): string { return this._links.self.href; } @deserialize - _links: { + _links: { self: HALLink; items: HALLink; }; diff --git a/src/app/core/shared/hal-resource.model.ts b/src/app/core/shared/hal-resource.model.ts index 5f9d107a54..a27d448bd6 100644 --- a/src/app/core/shared/hal-resource.model.ts +++ b/src/app/core/shared/hal-resource.model.ts @@ -13,7 +13,7 @@ export class HALResource { * The {@link HALLink}s for this {@link HALResource} */ @deserialize - _links: { + _links: { /** * The {@link HALLink} that refers to this {@link HALResource} diff --git a/src/app/core/shared/hierarchical-browse-definition.model.ts b/src/app/core/shared/hierarchical-browse-definition.model.ts index 97d50c3ca6..e7c06a5372 100644 --- a/src/app/core/shared/hierarchical-browse-definition.model.ts +++ b/src/app/core/shared/hierarchical-browse-definition.model.ts @@ -25,23 +25,23 @@ export class HierarchicalBrowseDefinition extends BrowseDefinition { * The object type */ @excludeFromEquals - type: ResourceType = HIERARCHICAL_BROWSE_DEFINITION; + type: ResourceType = HIERARCHICAL_BROWSE_DEFINITION; @autoserialize - facetType: string; + facetType: string; @autoserialize - vocabulary: string; + vocabulary: string; @autoserializeAs('metadata') - metadataKeys: string[]; + metadataKeys: string[]; get self(): string { return this._links.self.href; } @deserialize - _links: { + _links: { self: HALLink; vocabulary: HALLink; }; diff --git a/src/app/core/shared/item-relationships/item-type.model.ts b/src/app/core/shared/item-relationships/item-type.model.ts index 1d8ca6df98..bbf1cb3be0 100644 --- a/src/app/core/shared/item-relationships/item-type.model.ts +++ b/src/app/core/shared/item-relationships/item-type.model.ts @@ -24,16 +24,16 @@ export class ItemType implements CacheableObject { */ @excludeFromEquals @autoserialize - type: ResourceType; + type: ResourceType; /** * The identifier of this ItemType */ @autoserialize - id: string; + id: string; @autoserialize - label: string; + label: string; /** * The universally unique identifier of this ItemType @@ -47,7 +47,7 @@ export class ItemType implements CacheableObject { * The {@link HALLink}s for this ItemType */ @deserialize - _links: { + _links: { self: HALLink, }; } diff --git a/src/app/core/shared/item-relationships/relationship-type.model.ts b/src/app/core/shared/item-relationships/relationship-type.model.ts index 75d5d70af6..3a86ee34f8 100644 --- a/src/app/core/shared/item-relationships/relationship-type.model.ts +++ b/src/app/core/shared/item-relationships/relationship-type.model.ts @@ -31,19 +31,19 @@ export class RelationshipType implements CacheableObject { */ @excludeFromEquals @autoserialize - type: ResourceType; + type: ResourceType; /** * The label that describes this RelationshipType */ @autoserialize - label: string; + label: string; /** * The identifier of this RelationshipType */ @autoserialize - id: string; + id: string; /** * The universally unique identifier of this RelationshipType @@ -57,43 +57,43 @@ export class RelationshipType implements CacheableObject { * The label that describes the Relation to the left of this RelationshipType */ @autoserialize - leftwardType: string; + leftwardType: string; /** * The maximum amount of Relationships allowed to the left of this RelationshipType */ @autoserialize - leftMaxCardinality: number; + leftMaxCardinality: number; /** * The minimum amount of Relationships allowed to the left of this RelationshipType */ @autoserialize - leftMinCardinality: number; + leftMinCardinality: number; /** * The label that describes the Relation to the right of this RelationshipType */ @autoserialize - rightwardType: string; + rightwardType: string; /** * The maximum amount of Relationships allowed to the right of this RelationshipType */ @autoserialize - rightMaxCardinality: number; + rightMaxCardinality: number; /** * The minimum amount of Relationships allowed to the right of this RelationshipType */ @autoserialize - rightMinCardinality: number; + rightMinCardinality: number; /** * The {@link HALLink}s for this RelationshipType */ @deserialize - _links: { + _links: { self: HALLink; leftType: HALLink; rightType: HALLink; @@ -104,12 +104,12 @@ export class RelationshipType implements CacheableObject { * Will be undefined unless the leftType {@link HALLink} has been resolved. */ @link(ITEM_TYPE) - leftType?: Observable>; + leftType?: Observable>; /** * The type of Item found on the right side of this RelationshipType * Will be undefined unless the rightType {@link HALLink} has been resolved. */ @link(ITEM_TYPE) - rightType?: Observable>; + rightType?: Observable>; } diff --git a/src/app/core/shared/item-relationships/relationship.model.ts b/src/app/core/shared/item-relationships/relationship.model.ts index 4f6fe2796d..77fdff8651 100644 --- a/src/app/core/shared/item-relationships/relationship.model.ts +++ b/src/app/core/shared/item-relationships/relationship.model.ts @@ -33,7 +33,7 @@ export class Relationship implements CacheableObject { */ @excludeFromEquals @autoserialize - type: ResourceType; + type: ResourceType; /** * The universally unique identifier of this Relationship @@ -47,37 +47,37 @@ export class Relationship implements CacheableObject { * The identifier of this Relationship */ @autoserialize - id: string; + id: string; /** * The place of the Item to the left side of this Relationship */ @autoserialize - leftPlace: number; + leftPlace: number; /** * The place of the Item to the right side of this Relationship */ @autoserialize - rightPlace: number; + rightPlace: number; /** * The name variant of the Item to the left side of this Relationship */ @autoserialize - leftwardValue: string; + leftwardValue: string; /** * The name variant of the Item to the right side of this Relationship */ @autoserialize - rightwardValue: string; + rightwardValue: string; /** * The {@link HALLink}s for this Relationship */ @deserialize - _links: { + _links: { self: HALLink; leftItem: HALLink; rightItem: HALLink; @@ -89,20 +89,20 @@ export class Relationship implements CacheableObject { * Will be undefined unless the leftItem {@link HALLink} has been resolved. */ @link(ITEM) - leftItem?: Observable>; + leftItem?: Observable>; /** * The item on the right side of this relationship * Will be undefined unless the rightItem {@link HALLink} has been resolved. */ @link(ITEM) - rightItem?: Observable>; + rightItem?: Observable>; /** * The RelationshipType for this Relationship * Will be undefined unless the relationshipType {@link HALLink} has been resolved. */ @link(RELATIONSHIP_TYPE) - relationshipType?: Observable>; + relationshipType?: Observable>; } diff --git a/src/app/core/shared/item-request.model.ts b/src/app/core/shared/item-request.model.ts index 8b1ef94512..5a4f912363 100644 --- a/src/app/core/shared/item-request.model.ts +++ b/src/app/core/shared/item-request.model.ts @@ -22,70 +22,70 @@ export class ItemRequest implements CacheableObject { */ @excludeFromEquals @autoserialize - type: ResourceType; + type: ResourceType; /** * opaque string which uniquely identifies this request */ @autoserialize - token: string; + token: string; /** * true if the request is for all bitstreams of the item. */ @autoserialize - allfiles: boolean; + allfiles: boolean; /** * email address of the person requesting the files. */ @autoserialize - requestEmail: string; + requestEmail: string; /** * Human-readable name of the person requesting the files. */ @autoserialize - requestName: string; + requestName: string; /** * arbitrary message provided by the person requesting the files. */ @autoserialize - requestMessage: string; + requestMessage: string; /** * date that the request was recorded. */ @autoserialize - requestDate: string; + requestDate: string; /** * true if the request has been granted. */ @autoserialize - acceptRequest: boolean; + acceptRequest: boolean; /** * date that the request was granted or denied. */ @autoserialize - decisionDate: string; + decisionDate: string; /** * date on which the request is considered expired. */ @autoserialize - expires: string; + expires: string; /** * UUID of the requested Item. */ @autoserialize - itemId: string; + itemId: string; /** * UUID of the requested bitstream. */ @autoserialize - bitstreamId: string; + bitstreamId: string; /** * The {@link HALLink}s for this ItemRequest */ @deserialize - _links: { + _links: { self: HALLink; item: HALLink; bitstream: HALLink; diff --git a/src/app/core/shared/item.model.ts b/src/app/core/shared/item.model.ts index 33a9f3bb38..b13cf25f03 100644 --- a/src/app/core/shared/item.model.ts +++ b/src/app/core/shared/item.model.ts @@ -48,37 +48,37 @@ export class Item extends DSpaceObject implements ChildHALResource, HandleObject * A string representing the unique handle of this Item */ @autoserialize - handle: string; + handle: string; /** * The Date of the last modification of this Item */ @deserializeAs(Date) - lastModified: Date; + lastModified: Date; /** * A boolean representing if this Item is currently archived or not */ @autoserializeAs(Boolean, 'inArchive') - isArchived: boolean; + isArchived: boolean; /** * A boolean representing if this Item is currently discoverable or not */ @autoserializeAs(Boolean, 'discoverable') - isDiscoverable: boolean; + isDiscoverable: boolean; /** * A boolean representing if this Item is currently withdrawn or not */ @autoserializeAs(Boolean, 'withdrawn') - isWithdrawn: boolean; + isWithdrawn: boolean; /** * The {@link HALLink}s for this Item */ @deserialize - _links: { + _links: { mappedCollections: HALLink; relationships: HALLink; bundles: HALLink; @@ -96,35 +96,35 @@ export class Item extends DSpaceObject implements ChildHALResource, HandleObject * Will be undefined unless the owningCollection {@link HALLink} has been resolved. */ @link(COLLECTION) - owningCollection?: Observable>; + owningCollection?: Observable>; /** * The version this item represents in its history * Will be undefined unless the version {@link HALLink} has been resolved. */ @link(VERSION) - version?: Observable>; + version?: Observable>; /** * The list of Bundles inside this Item * Will be undefined unless the bundles {@link HALLink} has been resolved. */ @link(BUNDLE, true) - bundles?: Observable>>; + bundles?: Observable>>; /** * The list of Relationships this Item has with others * Will be undefined unless the relationships {@link HALLink} has been resolved. */ @link(RELATIONSHIP, true) - relationships?: Observable>>; + relationships?: Observable>>; /** * The thumbnail for this Item * Will be undefined unless the thumbnail {@link HALLink} has been resolved. */ @link(BITSTREAM, false, 'thumbnail') - thumbnail?: Observable>; + thumbnail?: Observable>; /** * The access status for this Item @@ -138,7 +138,7 @@ export class Item extends DSpaceObject implements ChildHALResource, HandleObject * Will be undefined unless the identifiers {@link HALLink} has been resolved. */ @link(IDENTIFIERS, false, 'identifiers') - identifiers?: Observable>; + identifiers?: Observable>; /** * Method that returns as which type of object this object should be rendered diff --git a/src/app/core/shared/license.model.ts b/src/app/core/shared/license.model.ts index af85c31eea..0cb1e70b6b 100644 --- a/src/app/core/shared/license.model.ts +++ b/src/app/core/shared/license.model.ts @@ -16,11 +16,11 @@ export class License extends DSpaceObject { * Is the license custom? */ @autoserialize - custom: boolean; + custom: boolean; /** * The text of the license */ @autoserialize - text: string; + text: string; } diff --git a/src/app/core/shared/metadata.models.ts b/src/app/core/shared/metadata.models.ts index 4ba78e39f7..0f26d7ff59 100644 --- a/src/app/core/shared/metadata.models.ts +++ b/src/app/core/shared/metadata.models.ts @@ -37,26 +37,26 @@ export class MetadataValue implements MetadataValueInterface { /** The language. */ @autoserialize - language: string; + language: string; /** The string value. */ @autoserialize - value: string; + value: string; /** * The place of this MetadataValue within its list of metadata * This is used to render metadata in a specific custom order */ @autoserialize - place: number; + place: number; /** The authority key used for authority-controlled metadata */ @autoserialize - authority: string; + authority: string; /** The authority confidence value */ @autoserialize - confidence: number; + confidence: number; /** * Returns true if this Metadatum's authority key starts with 'virtual::' diff --git a/src/app/core/shared/non-hierarchical-browse-definition.ts b/src/app/core/shared/non-hierarchical-browse-definition.ts index 769d70629d..7f8c6d0e5f 100644 --- a/src/app/core/shared/non-hierarchical-browse-definition.ts +++ b/src/app/core/shared/non-hierarchical-browse-definition.ts @@ -16,14 +16,14 @@ import { SortOption } from './sort-option.model'; export abstract class NonHierarchicalBrowseDefinition extends BrowseDefinition { @autoserialize - sortOptions: SortOption[]; + sortOptions: SortOption[]; @autoserializeAs('order') - defaultSortOrder: string; + defaultSortOrder: string; @autoserializeAs('metadata') - metadataKeys: string[]; + metadataKeys: string[]; @autoserialize - dataType: BrowseByDataType; + dataType: BrowseByDataType; } diff --git a/src/app/core/shared/page-info.model.ts b/src/app/core/shared/page-info.model.ts index 5594e58309..c9557abcb6 100644 --- a/src/app/core/shared/page-info.model.ts +++ b/src/app/core/shared/page-info.model.ts @@ -17,31 +17,31 @@ export class PageInfo implements HALResource { * The number of elements on a page */ @autoserializeAs(Number, 'size') - elementsPerPage: number; + elementsPerPage: number; /** * The total number of elements in the entire set */ @autoserialize - totalElements: number; + totalElements: number; /** * The total number of pages */ @autoserialize - totalPages: number; + totalPages: number; /** * The number of the current page, zero-based */ @autoserializeAs(Number, 'number') - currentPage: number; + currentPage: number; /** * The {@link HALLink}s for this PageInfo */ @deserialize - _links: { + _links: { first: HALLink; prev: HALLink; next: HALLink; diff --git a/src/app/core/shared/search/search-filters/search-config.model.ts b/src/app/core/shared/search/search-filters/search-config.model.ts index ccc9827e59..2ebe5e8455 100644 --- a/src/app/core/shared/search/search-filters/search-config.model.ts +++ b/src/app/core/shared/search/search-filters/search-config.model.ts @@ -3,6 +3,7 @@ import { deserialize, } from 'cerialize'; +import { FilterType } from '../../../../shared/search/models/filter-type.model'; import { typedObject } from '../../../cache/builders/build-decorators'; import { CacheableObject } from '../../../cache/cacheable-object.model'; import { HALLink } from '../../hal-link.model'; @@ -20,31 +21,31 @@ export class SearchConfig implements CacheableObject { * The id of this search configuration. */ @autoserialize - id: string; + id: string; /** * The configured filters. */ @autoserialize - filters: FilterConfig[]; + filters: FilterConfig[]; /** * The configured sort options. */ @autoserialize - sortOptions: SortConfig[]; + sortOptions: SortConfig[]; /** * The object type. */ @autoserialize - type: ResourceType; + type: ResourceType; /** * The {@link HALLink}s for this Item */ @deserialize - _links: { + _links: { facets: HALLink; objects: HALLink; self: HALLink; @@ -60,7 +61,7 @@ export interface FilterConfig { operators: OperatorConfig[]; openByDefault: boolean; pageSize: number; - type: string; + type: FilterType; } /** diff --git a/src/app/core/shared/sort-option.model.ts b/src/app/core/shared/sort-option.model.ts index b179d159b6..c735e87b9a 100644 --- a/src/app/core/shared/sort-option.model.ts +++ b/src/app/core/shared/sort-option.model.ts @@ -2,8 +2,8 @@ import { autoserialize } from 'cerialize'; export class SortOption { @autoserialize - name: string; + name: string; @autoserialize - metadata: string; + metadata: string; } diff --git a/src/app/core/shared/template-item.model.ts b/src/app/core/shared/template-item.model.ts index 4c33a2bd92..a536dea49e 100644 --- a/src/app/core/shared/template-item.model.ts +++ b/src/app/core/shared/template-item.model.ts @@ -23,6 +23,6 @@ export class TemplateItem extends Item { * The Collection that this item is a template for */ @link(COLLECTION) - templateItemOf: Observable>; + templateItemOf: Observable>; } diff --git a/src/app/core/shared/value-list-browse-definition.model.ts b/src/app/core/shared/value-list-browse-definition.model.ts index 80ad73e761..ad9fc60d1c 100644 --- a/src/app/core/shared/value-list-browse-definition.model.ts +++ b/src/app/core/shared/value-list-browse-definition.model.ts @@ -23,14 +23,14 @@ export class ValueListBrowseDefinition extends NonHierarchicalBrowseDefinition { * The object type */ @excludeFromEquals - type: ResourceType = VALUE_LIST_BROWSE_DEFINITION; + type: ResourceType = VALUE_LIST_BROWSE_DEFINITION; get self(): string { return this._links.self.href; } @deserialize - _links: { + _links: { self: HALLink; entries: HALLink; }; diff --git a/src/app/core/shared/version-history.model.ts b/src/app/core/shared/version-history.model.ts index 9792a20ff3..58d9259a04 100644 --- a/src/app/core/shared/version-history.model.ts +++ b/src/app/core/shared/version-history.model.ts @@ -27,7 +27,7 @@ export class VersionHistory extends DSpaceObject { static type = VERSION_HISTORY; @deserialize - _links: { + _links: { self: HALLink; versions: HALLink; draftVersion: HALLink; @@ -37,30 +37,30 @@ export class VersionHistory extends DSpaceObject { * The identifier of this Version History */ @autoserialize - id: string; + id: string; /** * The summary of this Version History */ @autoserialize - summary: string; + summary: string; /** * The name of the submitter of this Version History */ @autoserialize - submitterName: string; + submitterName: string; /** * Whether exist a workspace item */ @autoserialize - draftVersion: boolean; + draftVersion: boolean; /** * The list of versions within this history */ @excludeFromEquals @link(VERSION, true) - versions: Observable>>; + versions: Observable>>; } diff --git a/src/app/core/shared/version.model.ts b/src/app/core/shared/version.model.ts index f66bc8938b..105ba74416 100644 --- a/src/app/core/shared/version.model.ts +++ b/src/app/core/shared/version.model.ts @@ -30,7 +30,7 @@ export class Version extends DSpaceObject { static type = VERSION; @deserialize - _links: { + _links: { self: HALLink; item: HALLink; versionhistory: HALLink; @@ -41,50 +41,50 @@ export class Version extends DSpaceObject { * The identifier of this Version */ @autoserialize - id: string; + id: string; /** * The version number of the version's history this version represents */ @autoserialize - version: number; + version: number; /** * The summary for the changes made in this version */ @autoserialize - summary: string; + summary: string; /** * The name of the submitter of this version */ @autoserialize - submitterName: string; + submitterName: string; /** * The Date this version was created */ @deserialize - created: Date; + created: Date; /** * The full version history this version is apart of */ @excludeFromEquals @link(VERSION_HISTORY) - versionhistory: Observable>; + versionhistory: Observable>; /** * The item this version represents */ @excludeFromEquals @link(ITEM) - item: Observable>; + item: Observable>; /** * The e-person who created this version */ @excludeFromEquals @link(EPERSON) - eperson: Observable>; + eperson: Observable>; } diff --git a/src/app/core/statistics/models/usage-report.model.ts b/src/app/core/statistics/models/usage-report.model.ts index b8a415667d..a4924f3a3d 100644 --- a/src/app/core/statistics/models/usage-report.model.ts +++ b/src/app/core/statistics/models/usage-report.model.ts @@ -26,19 +26,19 @@ export class UsageReport extends HALResource { */ @excludeFromEquals @autoserialize - type: ResourceType; + type: ResourceType; @autoserialize - id: string; + id: string; @autoserializeAs('report-type') reportType: string; @autoserialize - points: Point[]; + points: Point[]; @deserialize - _links: { + _links: { self: HALLink; }; } diff --git a/src/app/core/submission/models/correctiontype.model.ts b/src/app/core/submission/models/correctiontype.model.ts index 0b9dd75545..4b5bd1ddd6 100644 --- a/src/app/core/submission/models/correctiontype.model.ts +++ b/src/app/core/submission/models/correctiontype.model.ts @@ -22,7 +22,7 @@ export class CorrectionType extends CacheableObject { */ @excludeFromEquals @autoserialize - type: ResourceType; + type: ResourceType; @autoserialize /** * The unique identifier for the correction type mode. diff --git a/src/app/core/submission/models/submission-cc-license-url.model.ts b/src/app/core/submission/models/submission-cc-license-url.model.ts index 85fa1d0896..b1e53aa206 100644 --- a/src/app/core/submission/models/submission-cc-license-url.model.ts +++ b/src/app/core/submission/models/submission-cc-license-url.model.ts @@ -20,8 +20,8 @@ export class SubmissionCcLicenceUrl extends HALResource { */ @excludeFromEquals @autoserialize - type: ResourceType; + type: ResourceType; @autoserialize - url: string; + url: string; } diff --git a/src/app/core/submission/models/submission-cc-license.model.ts b/src/app/core/submission/models/submission-cc-license.model.ts index c730aa44fc..8ab235941f 100644 --- a/src/app/core/submission/models/submission-cc-license.model.ts +++ b/src/app/core/submission/models/submission-cc-license.model.ts @@ -20,16 +20,16 @@ export class SubmissionCcLicence extends HALResource { */ @excludeFromEquals @autoserialize - type: ResourceType; + type: ResourceType; @autoserialize - id: string; + id: string; @autoserialize - name: string; + name: string; @autoserialize - fields: Field[]; + fields: Field[]; } export interface Field { diff --git a/src/app/core/submission/models/submission-object.model.ts b/src/app/core/submission/models/submission-object.model.ts index 882fafb467..d4836f6dcf 100644 --- a/src/app/core/submission/models/submission-object.model.ts +++ b/src/app/core/submission/models/submission-object.model.ts @@ -35,38 +35,38 @@ export abstract class SubmissionObject extends DSpaceObject implements Cacheable @excludeFromEquals @autoserialize - id: string; + id: string; /** * The SubmissionObject last modified date */ @autoserialize - lastModified: Date; + lastModified: Date; /** * The collection this submission applies to * Will be undefined unless the collection {@link HALLink} has been resolved. */ @link(COLLECTION) - collection?: Observable> | Collection; + collection?: Observable> | Collection; /** * The SubmissionObject's last section's data */ @autoserialize - sections: WorkspaceitemSectionsObject; + sections: WorkspaceitemSectionsObject; /** * The SubmissionObject's last section's errors */ @autoserialize - errors: SubmissionObjectError[]; + errors: SubmissionObjectError[]; /** * The {@link HALLink}s for this SubmissionObject */ @deserialize - _links: { + _links: { self: HALLink; collection: HALLink; item: HALLink; @@ -92,14 +92,14 @@ export abstract class SubmissionObject extends DSpaceObject implements Cacheable * Will be undefined unless the submissionDefinition {@link HALLink} has been resolved. */ @link(SubmissionDefinitionsModel.type) - submissionDefinition?: Observable> | SubmissionDefinitionsModel; + submissionDefinition?: Observable> | SubmissionDefinitionsModel; /** * The submitter for this SubmissionObject * Will be undefined unless the submitter {@link HALLink} has been resolved. */ @link(EPERSON) - submitter?: Observable> | EPerson; + submitter?: Observable> | EPerson; /** * The submission supervision order diff --git a/src/app/core/submission/vocabularies/models/vocabulary-entry-detail.model.ts b/src/app/core/submission/vocabularies/models/vocabulary-entry-detail.model.ts index 3dcb0d374a..f7dff16a40 100644 --- a/src/app/core/submission/vocabularies/models/vocabulary-entry-detail.model.ts +++ b/src/app/core/submission/vocabularies/models/vocabulary-entry-detail.model.ts @@ -21,19 +21,19 @@ export class VocabularyEntryDetail extends VocabularyEntry { * The unique id of the entry */ @autoserialize - id: string; + id: string; /** * In an hierarchical vocabulary representing if entry is selectable as value */ @autoserialize - selectable: boolean; + selectable: boolean; /** * The {@link HALLink}s for this ExternalSourceEntry */ @deserialize - _links: { + _links: { self: HALLink; vocabulary: HALLink; parent: HALLink; diff --git a/src/app/core/submission/vocabularies/models/vocabulary-entry.model.ts b/src/app/core/submission/vocabularies/models/vocabulary-entry.model.ts index 323db57de1..85b310bad8 100644 --- a/src/app/core/submission/vocabularies/models/vocabulary-entry.model.ts +++ b/src/app/core/submission/vocabularies/models/vocabulary-entry.model.ts @@ -24,25 +24,25 @@ export class VocabularyEntry extends ListableObject { * The identifier of this vocabulary entry */ @autoserialize - authority: string; + authority: string; /** * The display value of this vocabulary entry */ @autoserialize - display: string; + display: string; /** * The value of this vocabulary entry */ @autoserialize - value: string; + value: string; /** * An object containing additional information related to this vocabulary entry */ @autoserialize - otherInformation: OtherInformation; + otherInformation: OtherInformation; /** * A string representing the kind of vocabulary entry @@ -55,7 +55,7 @@ export class VocabularyEntry extends ListableObject { * The {@link HALLink}s for this ExternalSourceEntry */ @deserialize - _links: { + _links: { self: HALLink; vocabularyEntryDetail?: HALLink; }; diff --git a/src/app/core/submission/vocabularies/models/vocabulary.model.ts b/src/app/core/submission/vocabularies/models/vocabulary.model.ts index 9026ce677a..9da5ade326 100644 --- a/src/app/core/submission/vocabularies/models/vocabulary.model.ts +++ b/src/app/core/submission/vocabularies/models/vocabulary.model.ts @@ -29,32 +29,32 @@ export class Vocabulary implements CacheableObject { * The identifier of this Vocabulary */ @autoserialize - id: string; + id: string; /** * The name of this Vocabulary */ @autoserialize - name: string; + name: string; /** * True if it is possible to scroll all the entries in the vocabulary without providing a filter parameter */ @autoserialize - scrollable: boolean; + scrollable: boolean; /** * True if the vocabulary exposes a tree structure where some entries are parent of others */ @autoserialize - hierarchical: boolean; + hierarchical: boolean; /** * For hierarchical vocabularies express the preference to preload the tree at a specific * level of depth (0 only the top nodes are shown, 1 also their children are preloaded and so on) */ @autoserialize - preloadLevel: any; + preloadLevel: any; /** * A string representing the kind of Vocabulary model @@ -64,13 +64,13 @@ export class Vocabulary implements CacheableObject { public type: any; @link(VOCABULARY_ENTRY, true) - entries?: Observable>>; + entries?: Observable>>; /** * The {@link HALLink}s for this Vocabulary */ @deserialize - _links: { + _links: { self: HALLink, entries: HALLink }; diff --git a/src/app/core/supervision-order/models/supervision-order.model.ts b/src/app/core/supervision-order/models/supervision-order.model.ts index 64087c8a21..6426f62aec 100644 --- a/src/app/core/supervision-order/models/supervision-order.model.ts +++ b/src/app/core/supervision-order/models/supervision-order.model.ts @@ -32,21 +32,21 @@ export class SupervisionOrder implements CacheableObject { * The identifier for this Supervision Order */ @autoserialize - id: string; + id: string; /** * The object type */ @excludeFromEquals @autoserialize - type: ResourceType; + type: ResourceType; /** * The object type */ @excludeFromEquals @autoserialize - ordertype: string; + ordertype: string; /** * The universally unique identifier for this Supervision Order @@ -60,7 +60,7 @@ export class SupervisionOrder implements CacheableObject { * The {@link HALLink}s for this SupervisionOrder */ @deserialize - _links: { + _links: { item: HALLink, group: HALLink, self: HALLink, @@ -71,12 +71,12 @@ export class SupervisionOrder implements CacheableObject { * Will be undefined unless the item {@link HALLink} has been resolved. */ @link(ITEM) - item?: Observable>; + item?: Observable>; /** * The group linked by this supervision order * Will be undefined unless the version {@link HALLink} has been resolved. */ @link(GROUP) - group?: Observable>; + group?: Observable>; } diff --git a/src/app/core/tasks/models/advanced-workflow-info.model.ts b/src/app/core/tasks/models/advanced-workflow-info.model.ts index 6e5feec357..87991a375c 100644 --- a/src/app/core/tasks/models/advanced-workflow-info.model.ts +++ b/src/app/core/tasks/models/advanced-workflow-info.model.ts @@ -6,6 +6,6 @@ import { autoserialize } from 'cerialize'; export abstract class AdvancedWorkflowInfo { @autoserialize - id: string; + id: string; } diff --git a/src/app/core/tasks/models/rating-advanced-workflow-info.model.ts b/src/app/core/tasks/models/rating-advanced-workflow-info.model.ts index 892806f7d1..6ecb0e6dbf 100644 --- a/src/app/core/tasks/models/rating-advanced-workflow-info.model.ts +++ b/src/app/core/tasks/models/rating-advanced-workflow-info.model.ts @@ -21,12 +21,12 @@ export class RatingAdvancedWorkflowInfo extends AdvancedWorkflowInfo { * Whether the description is required. */ @autoserialize - descriptionRequired: boolean; + descriptionRequired: boolean; /** * The maximum value. */ @autoserialize - maxValue: number; + maxValue: number; } diff --git a/src/app/core/tasks/models/select-reviewer-advanced-workflow-info.model.ts b/src/app/core/tasks/models/select-reviewer-advanced-workflow-info.model.ts index d202593efb..307caeaa1f 100644 --- a/src/app/core/tasks/models/select-reviewer-advanced-workflow-info.model.ts +++ b/src/app/core/tasks/models/select-reviewer-advanced-workflow-info.model.ts @@ -18,6 +18,6 @@ export class SelectReviewerAdvancedWorkflowInfo extends AdvancedWorkflowInfo { static type: ResourceType = SELECT_REVIEWER_ADVANCED_WORKFLOW_INFO; @autoserialize - group: string; + group: string; } diff --git a/src/app/core/tasks/models/task-object.model.ts b/src/app/core/tasks/models/task-object.model.ts index cea886a1c6..0e5022d579 100644 --- a/src/app/core/tasks/models/task-object.model.ts +++ b/src/app/core/tasks/models/task-object.model.ts @@ -34,19 +34,19 @@ export class TaskObject extends DSpaceObject implements CacheableObject { * The task identifier */ @autoserialize - id: string; + id: string; /** * The workflow step */ @autoserialize - step: string; + step: string; /** * The {@link HALLink}s for this TaskObject */ @deserialize - _links: { + _links: { self: HALLink; owner: HALLink; group: HALLink; @@ -59,14 +59,14 @@ export class TaskObject extends DSpaceObject implements CacheableObject { * Will be undefined unless the eperson {@link HALLink} has been resolved. */ @link(EPERSON, false, 'owner') - eperson?: Observable>; + eperson?: Observable>; /** * The Group for this task * Will be undefined unless the group {@link HALLink} has been resolved. */ @link(GROUP) - group?: Observable>; + group?: Observable>; /** * The WorkflowItem for this task @@ -81,6 +81,6 @@ export class TaskObject extends DSpaceObject implements CacheableObject { * Will be undefined unless the group {@link HALLink} has been resolved. */ @link(WORKFLOW_ACTION, false, 'action') - action: Observable>; + action: Observable>; } diff --git a/src/app/core/tasks/models/workflow-action-object.model.ts b/src/app/core/tasks/models/workflow-action-object.model.ts index 53ae4249c8..a101000885 100644 --- a/src/app/core/tasks/models/workflow-action-object.model.ts +++ b/src/app/core/tasks/models/workflow-action-object.model.ts @@ -20,30 +20,30 @@ export class WorkflowAction extends DSpaceObject { * The workflow action's identifier */ @autoserialize - id: string; + id: string; /** * The options available for this workflow action */ @autoserialize - options: string[]; + options: string[]; /** * Whether this action has advanced options */ @autoserialize - advanced: boolean; + advanced: boolean; /** * The advanced options that the user can select at this action */ @autoserialize - advancedOptions: string[]; + advancedOptions: string[]; /** * The advanced info required by the advanced options */ @autoserialize - advancedInfo: AdvancedWorkflowInfo[]; + advancedInfo: AdvancedWorkflowInfo[]; } diff --git a/src/app/core/utilities/equatable.spec.ts b/src/app/core/utilities/equatable.spec.ts index 808c21b39f..cea3c35b3d 100644 --- a/src/app/core/utilities/equatable.spec.ts +++ b/src/app/core/utilities/equatable.spec.ts @@ -21,7 +21,7 @@ class Dog extends EquatableObject { class Owner extends EquatableObject { @excludeFromEquals - favouriteFood: string; + favouriteFood: string; constructor( public name: string, diff --git a/src/app/curation-form/curation-form.component.ts b/src/app/curation-form/curation-form.component.ts index 0580193418..8e1c1b26c8 100644 --- a/src/app/curation-form/curation-form.component.ts +++ b/src/app/curation-form/curation-form.component.ts @@ -62,7 +62,7 @@ export class CurationFormComponent implements OnDestroy, OnInit { form: UntypedFormGroup; @Input() - dsoHandle: string; + dsoHandle: string; subs: Subscription[] = []; diff --git a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.html b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.html index b79b185b40..0c1088e3fd 100644 --- a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.html +++ b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.html @@ -55,7 +55,7 @@
diff --git a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.spec.ts b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.spec.ts index ccfcfca93c..fbbfe982aa 100644 --- a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.spec.ts +++ b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.spec.ts @@ -221,7 +221,7 @@ describe('DsoEditMetadataValueComponent', () => { it('should not show a badge', () => { expect( - fixture.debugElement.query(By.css('ds-themed-type-badge')), + fixture.debugElement.query(By.css('ds-type-badge')), ).toBeNull(); }); @@ -289,7 +289,7 @@ describe('DsoEditMetadataValueComponent', () => { it('should show a badge', () => { expect( - fixture.debugElement.query(By.css('ds-themed-type-badge')), + fixture.debugElement.query(By.css('ds-type-badge')), ).toBeTruthy(); }); diff --git a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata.component.spec.ts b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata.component.spec.ts index 32822f1832..32e57e003d 100644 --- a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata.component.spec.ts +++ b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata.component.spec.ts @@ -22,7 +22,7 @@ import { Item } from '../../core/shared/item.model'; import { ITEM } from '../../core/shared/item.resource-type'; import { MetadataValue } from '../../core/shared/metadata.models'; import { AlertComponent } from '../../shared/alert/alert.component'; -import { LoadingComponent } from '../../shared/loading/loading.component'; +import { ThemedLoadingComponent } from '../../shared/loading/themed-loading.component'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { TestDataService } from '../../shared/testing/test-data-service.mock'; import { VarDirective } from '../../shared/utils/var.directive'; @@ -112,7 +112,7 @@ describe('DsoEditMetadataComponent', () => { DsoEditMetadataValueHeadersComponent, DsoEditMetadataFieldValuesComponent, AlertComponent, - LoadingComponent, + ThemedLoadingComponent, ], }, }) diff --git a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata.component.ts b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata.component.ts index 7704abc4ea..677a601b9d 100644 --- a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata.component.ts +++ b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata.component.ts @@ -52,7 +52,7 @@ import { hasValue, isNotEmpty, } from '../../shared/empty.util'; -import { LoadingComponent } from '../../shared/loading/loading.component'; +import { ThemedLoadingComponent } from '../../shared/loading/themed-loading.component'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { DsoEditMetadataFieldValuesComponent } from './dso-edit-metadata-field-values/dso-edit-metadata-field-values.component'; import { DsoEditMetadataForm } from './dso-edit-metadata-form'; @@ -62,11 +62,11 @@ import { DsoEditMetadataValueHeadersComponent } from './dso-edit-metadata-value- import { MetadataFieldSelectorComponent } from './metadata-field-selector/metadata-field-selector.component'; @Component({ - selector: 'ds-dso-edit-metadata', + selector: 'ds-base-dso-edit-metadata', styleUrls: ['./dso-edit-metadata.component.scss'], templateUrl: './dso-edit-metadata.component.html', standalone: true, - imports: [NgIf, DsoEditMetadataHeadersComponent, MetadataFieldSelectorComponent, DsoEditMetadataValueHeadersComponent, DsoEditMetadataValueComponent, NgFor, DsoEditMetadataFieldValuesComponent, AlertComponent, LoadingComponent, AsyncPipe, TranslateModule], + imports: [NgIf, DsoEditMetadataHeadersComponent, MetadataFieldSelectorComponent, DsoEditMetadataValueHeadersComponent, DsoEditMetadataValueComponent, NgFor, DsoEditMetadataFieldValuesComponent, AlertComponent, ThemedLoadingComponent, AsyncPipe, TranslateModule], }) /** * Component showing a table of all metadata on a DSpaceObject and options to modify them diff --git a/src/app/dso-shared/dso-edit-metadata/themed-dso-edit-metadata.component.ts b/src/app/dso-shared/dso-edit-metadata/themed-dso-edit-metadata.component.ts index 9de9c539a2..063263b670 100644 --- a/src/app/dso-shared/dso-edit-metadata/themed-dso-edit-metadata.component.ts +++ b/src/app/dso-shared/dso-edit-metadata/themed-dso-edit-metadata.component.ts @@ -9,10 +9,11 @@ import { ThemedComponent } from '../../shared/theme-support/themed.component'; import { DsoEditMetadataComponent } from './dso-edit-metadata.component'; @Component({ - selector: 'ds-themed-dso-edit-metadata', + selector: 'ds-dso-edit-metadata', styleUrls: [], templateUrl: './../../shared/theme-support/themed.component.html', standalone: true, + imports: [DsoEditMetadataComponent], }) export class ThemedDsoEditMetadataComponent extends ThemedComponent { diff --git a/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-issue/journal-issue-search-result-grid-element.component.html b/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-issue/journal-issue-search-result-grid-element.component.html index 33ce615305..bca1a1b3b3 100644 --- a/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-issue/journal-issue-search-result-grid-element.component.html +++ b/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-issue/journal-issue-search-result-grid-element.component.html @@ -8,18 +8,18 @@ [attr.rel]="(linkType === linkTypes.ExternalLink) ? 'noopener noreferrer' : null" [routerLink]="[itemPageRoute]" class="card-img-top full-width" [attr.title]="'search.results.view-result' | translate">
- - + +
- - + +
- +

diff --git a/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-volume/journal-volume-search-result-grid-element.component.html b/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-volume/journal-volume-search-result-grid-element.component.html index de80448e8d..3b14a1d8b0 100644 --- a/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-volume/journal-volume-search-result-grid-element.component.html +++ b/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-volume/journal-volume-search-result-grid-element.component.html @@ -8,18 +8,18 @@ [attr.rel]="(linkType === linkTypes.ExternalLink) ? 'noopener noreferrer' : null" [routerLink]="[itemPageRoute]" class="card-img-top full-width" [attr.title]="'search.results.view-result' | translate">
- - + +
- - + +
- +

diff --git a/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal/journal-search-result-grid-element.component.html b/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal/journal-search-result-grid-element.component.html index b537fb60d9..20084f21ba 100644 --- a/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal/journal-search-result-grid-element.component.html +++ b/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal/journal-search-result-grid-element.component.html @@ -8,18 +8,18 @@ [attr.rel]="(linkType === linkTypes.ExternalLink) ? 'noopener noreferrer' : null" [routerLink]="[itemPageRoute]" class="card-img-top full-width" [attr.title]="'search.results.view-result' | translate">
- - + +
- - + +
- +

diff --git a/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component.html b/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component.html index 545e8c67a6..59d2ea4dc9 100644 --- a/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component.html +++ b/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component.html @@ -12,7 +12,7 @@
- + { }).overrideComponent(JournalIssueSearchResultListElementComponent, { add: { changeDetection: ChangeDetectionStrategy.Default } , remove: { - imports: [ThumbnailComponent, ThemedBadgesComponent, TruncatableComponent, TruncatablePartComponent], + imports: [ThemedThumbnailComponent, ThemedBadgesComponent, TruncatableComponent, TruncatablePartComponent], } }, ).compileComponents(); })); diff --git a/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component.ts b/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component.ts index ca12acaa10..f3f8d79809 100644 --- a/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component.ts +++ b/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component.ts @@ -13,7 +13,7 @@ import { listableObjectComponent } from '../../../../../shared/object-collection import { ItemSearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component'; import { TruncatableComponent } from '../../../../../shared/truncatable/truncatable.component'; import { TruncatablePartComponent } from '../../../../../shared/truncatable/truncatable-part/truncatable-part.component'; -import { ThumbnailComponent } from '../../../../../thumbnail/thumbnail.component'; +import { ThemedThumbnailComponent } from '../../../../../thumbnail/themed-thumbnail.component'; @listableObjectComponent('JournalIssueSearchResult', ViewMode.ListElement) @Component({ @@ -21,7 +21,7 @@ import { ThumbnailComponent } from '../../../../../thumbnail/thumbnail.component styleUrls: ['./journal-issue-search-result-list-element.component.scss'], templateUrl: './journal-issue-search-result-list-element.component.html', standalone: true, - imports: [NgIf, RouterLink, ThumbnailComponent, NgClass, ThemedBadgesComponent, TruncatableComponent, TruncatablePartComponent, NgFor, AsyncPipe], + imports: [NgIf, RouterLink, ThemedThumbnailComponent, NgClass, ThemedBadgesComponent, TruncatableComponent, TruncatablePartComponent, NgFor, AsyncPipe], }) /** * The component for displaying a list element for an item search result of the type Journal Issue diff --git a/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-volume/journal-volume-search-result-list-element.component.html b/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-volume/journal-volume-search-result-list-element.component.html index 2ccbbe1095..4a6ffb412d 100644 --- a/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-volume/journal-volume-search-result-list-element.component.html +++ b/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-volume/journal-volume-search-result-list-element.component.html @@ -12,7 +12,7 @@
- + { add: { changeDetection: ChangeDetectionStrategy.Default }, remove: { imports: [ - ThumbnailComponent, NgClass, ThemedBadgesComponent, TruncatableComponent, TruncatablePartComponent, + ThemedThumbnailComponent, NgClass, ThemedBadgesComponent, TruncatableComponent, TruncatablePartComponent, ], }, }).compileComponents(); @@ -196,7 +196,7 @@ describe('JournalVolumeSearchResultListElementComponent', () => { add: { changeDetection: ChangeDetectionStrategy.Default }, remove: { imports: [ - ThumbnailComponent, + ThemedThumbnailComponent, ThemedBadgesComponent, TruncatableComponent, TruncatablePartComponent, diff --git a/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-volume/journal-volume-search-result-list-element.component.ts b/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-volume/journal-volume-search-result-list-element.component.ts index b44a4896b7..4aee64c4f5 100644 --- a/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-volume/journal-volume-search-result-list-element.component.ts +++ b/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-volume/journal-volume-search-result-list-element.component.ts @@ -13,7 +13,7 @@ import { listableObjectComponent } from '../../../../../shared/object-collection import { ItemSearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component'; import { TruncatableComponent } from '../../../../../shared/truncatable/truncatable.component'; import { TruncatablePartComponent } from '../../../../../shared/truncatable/truncatable-part/truncatable-part.component'; -import { ThumbnailComponent } from '../../../../../thumbnail/thumbnail.component'; +import { ThemedThumbnailComponent } from '../../../../../thumbnail/themed-thumbnail.component'; @listableObjectComponent('JournalVolumeSearchResult', ViewMode.ListElement) @Component({ @@ -21,7 +21,7 @@ import { ThumbnailComponent } from '../../../../../thumbnail/thumbnail.component styleUrls: ['./journal-volume-search-result-list-element.component.scss'], templateUrl: './journal-volume-search-result-list-element.component.html', standalone: true, - imports: [NgIf, RouterLink, ThumbnailComponent, NgClass, ThemedBadgesComponent, TruncatableComponent, TruncatablePartComponent, NgFor, AsyncPipe], + imports: [NgIf, RouterLink, ThemedThumbnailComponent, NgClass, ThemedBadgesComponent, TruncatableComponent, TruncatablePartComponent, NgFor, AsyncPipe], }) /** * The component for displaying a list element for an item search result of the type Journal Volume diff --git a/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal/journal-search-result-list-element.component.html b/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal/journal-search-result-list-element.component.html index 3074d53df6..bd119493d3 100644 --- a/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal/journal-search-result-list-element.component.html +++ b/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal/journal-search-result-list-element.component.html @@ -11,7 +11,7 @@
- + { add: { changeDetection: ChangeDetectionStrategy.Default }, remove: { imports: [ - ThumbnailComponent, + ThemedThumbnailComponent, ThemedBadgesComponent, TruncatableComponent, TruncatablePartComponent, diff --git a/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal/journal-search-result-list-element.component.ts b/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal/journal-search-result-list-element.component.ts index d931feece7..8ef14120e0 100644 --- a/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal/journal-search-result-list-element.component.ts +++ b/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal/journal-search-result-list-element.component.ts @@ -13,7 +13,7 @@ import { listableObjectComponent } from '../../../../../shared/object-collection import { ItemSearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component'; import { TruncatableComponent } from '../../../../../shared/truncatable/truncatable.component'; import { TruncatablePartComponent } from '../../../../../shared/truncatable/truncatable-part/truncatable-part.component'; -import { ThumbnailComponent } from '../../../../../thumbnail/thumbnail.component'; +import { ThemedThumbnailComponent } from '../../../../../thumbnail/themed-thumbnail.component'; @listableObjectComponent('JournalSearchResult', ViewMode.ListElement) @Component({ @@ -21,7 +21,7 @@ import { ThumbnailComponent } from '../../../../../thumbnail/thumbnail.component styleUrls: ['./journal-search-result-list-element.component.scss'], templateUrl: './journal-search-result-list-element.component.html', standalone: true, - imports: [NgIf, RouterLink, ThumbnailComponent, NgClass, ThemedBadgesComponent, TruncatableComponent, TruncatablePartComponent, NgFor, AsyncPipe], + imports: [NgIf, RouterLink, ThemedThumbnailComponent, NgClass, ThemedBadgesComponent, TruncatableComponent, TruncatablePartComponent, NgFor, AsyncPipe], }) /** * The component for displaying a list element for an item search result of the type Journal diff --git a/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.html b/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.html index 3648b53b51..e3dec92c52 100644 --- a/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.html +++ b/src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.html @@ -1,13 +1,13 @@ - +
- - + +
- + +
- - + +
- + +
- - + +
- +
- - + +
- - + +
- +

diff --git a/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/person/person-search-result-grid-element.component.html b/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/person/person-search-result-grid-element.component.html index 976d2c55e5..7b444d261c 100644 --- a/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/person/person-search-result-grid-element.component.html +++ b/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/person/person-search-result-grid-element.component.html @@ -8,18 +8,18 @@ [attr.rel]="(linkType === linkTypes.ExternalLink) ? 'noopener noreferrer' : null" [routerLink]="[itemPageRoute]" class="card-img-top full-width" [attr.title]="'search.results.view-result' | translate">
- - + +
- - + +
- +

diff --git a/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/project/project-search-result-grid-element.component.html b/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/project/project-search-result-grid-element.component.html index 1025b4c35d..2181a4eb07 100644 --- a/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/project/project-search-result-grid-element.component.html +++ b/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/project/project-search-result-grid-element.component.html @@ -8,18 +8,18 @@ [attr.rel]="(linkType === linkTypes.ExternalLink) ? 'noopener noreferrer' : null" [routerLink]="[itemPageRoute]" class="card-img-top full-width" [attr.title]="'search.results.view-result' | translate">
- - + +
- - + +
- +

diff --git a/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/org-unit/org-unit-search-result-list-element.component.html b/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/org-unit/org-unit-search-result-list-element.component.html index 60251c15b8..ed1181a407 100644 --- a/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/org-unit/org-unit-search-result-list-element.component.html +++ b/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/org-unit/org-unit-search-result-list-element.component.html @@ -18,7 +18,7 @@
- +
- +
- + { schemas: [NO_ERRORS_SCHEMA], }).overrideComponent(ProjectSearchResultListElementComponent, { add: { changeDetection: ChangeDetectionStrategy.Default }, - remove: { imports: [ThumbnailComponent, TruncatableComponent, ThemedBadgesComponent] }, + remove: { imports: [ThemedThumbnailComponent, TruncatableComponent, ThemedBadgesComponent] }, }).compileComponents(); })); diff --git a/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/project/project-search-result-list-element.component.ts b/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/project/project-search-result-list-element.component.ts index fc6e031f40..825f7be387 100644 --- a/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/project/project-search-result-list-element.component.ts +++ b/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/project/project-search-result-list-element.component.ts @@ -11,7 +11,7 @@ import { ThemedBadgesComponent } from '../../../../../shared/object-collection/s import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator'; import { ItemSearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component'; import { TruncatableComponent } from '../../../../../shared/truncatable/truncatable.component'; -import { ThumbnailComponent } from '../../../../../thumbnail/thumbnail.component'; +import { ThemedThumbnailComponent } from '../../../../../thumbnail/themed-thumbnail.component'; @listableObjectComponent('ProjectSearchResult', ViewMode.ListElement) @Component({ @@ -19,7 +19,7 @@ import { ThumbnailComponent } from '../../../../../thumbnail/thumbnail.component styleUrls: ['./project-search-result-list-element.component.scss'], templateUrl: './project-search-result-list-element.component.html', standalone: true, - imports: [NgIf, RouterLink, ThumbnailComponent, NgClass, TruncatableComponent, ThemedBadgesComponent, AsyncPipe], + imports: [NgIf, RouterLink, ThemedThumbnailComponent, NgClass, TruncatableComponent, ThemedBadgesComponent, AsyncPipe], }) /** * The component for displaying a list element for an item search result of the type Project diff --git a/src/app/entity-groups/research-entities/item-pages/org-unit/org-unit.component.html b/src/app/entity-groups/research-entities/item-pages/org-unit/org-unit.component.html index f28550a4e0..126845e36f 100644 --- a/src/app/entity-groups/research-entities/item-pages/org-unit/org-unit.component.html +++ b/src/app/entity-groups/research-entities/item-pages/org-unit/org-unit.component.html @@ -1,18 +1,18 @@ - +
- - + +
- - + +
- - + +
- - + +
- - + +
- - + - - + diff --git a/src/app/entity-groups/research-entities/submission/item-list-elements/person/person-search-result-list-submission-element.component.ts b/src/app/entity-groups/research-entities/submission/item-list-elements/person/person-search-result-list-submission-element.component.ts index 49d6ecb59c..1145fb78e3 100644 --- a/src/app/entity-groups/research-entities/submission/item-list-elements/person/person-search-result-list-submission-element.component.ts +++ b/src/app/entity-groups/research-entities/submission/item-list-elements/person/person-search-result-list-submission-element.component.ts @@ -29,7 +29,7 @@ import { listableObjectComponent } from '../../../../../shared/object-collection import { SearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/search-result-list-element.component'; import { SelectableListService } from '../../../../../shared/object-list/selectable-list/selectable-list.service'; import { TruncatableService } from '../../../../../shared/truncatable/truncatable.service'; -import { ThumbnailComponent } from '../../../../../thumbnail/thumbnail.component'; +import { ThemedThumbnailComponent } from '../../../../../thumbnail/themed-thumbnail.component'; import { NameVariantModalComponent } from '../../name-variant-modal/name-variant-modal.component'; import { PersonInputSuggestionsComponent } from './person-suggestions/person-input-suggestions.component'; @@ -39,7 +39,7 @@ import { PersonInputSuggestionsComponent } from './person-suggestions/person-inp styleUrls: ['./person-search-result-list-submission-element.component.scss'], templateUrl: './person-search-result-list-submission-element.component.html', standalone: true, - imports: [NgIf, ThumbnailComponent, NgClass, PersonInputSuggestionsComponent, FormsModule, NgFor, AsyncPipe], + imports: [NgIf, ThemedThumbnailComponent, NgClass, PersonInputSuggestionsComponent, FormsModule, NgFor, AsyncPipe], }) /** diff --git a/src/app/footer/footer.component.ts b/src/app/footer/footer.component.ts index f9fdde7834..99e74ed4aa 100644 --- a/src/app/footer/footer.component.ts +++ b/src/app/footer/footer.component.ts @@ -27,7 +27,7 @@ import { KlaroService } from '../shared/cookies/klaro.service'; import { hasValue } from '../shared/empty.util'; @Component({ - selector: 'ds-footer', + selector: 'ds-base-footer', styleUrls: ['footer.component.scss'], templateUrl: 'footer.component.html', standalone: true, diff --git a/src/app/footer/themed-footer.component.ts b/src/app/footer/themed-footer.component.ts index 1c3ae83026..a09484ebca 100644 --- a/src/app/footer/themed-footer.component.ts +++ b/src/app/footer/themed-footer.component.ts @@ -7,10 +7,11 @@ import { FooterComponent } from './footer.component'; * Themed wrapper for FooterComponent */ @Component({ - selector: 'ds-themed-footer', + selector: 'ds-footer', styleUrls: [], templateUrl: '../shared/theme-support/themed.component.html', standalone: true, + imports: [FooterComponent], }) export class ThemedFooterComponent extends ThemedComponent { protected getComponentName(): string { diff --git a/src/app/forbidden/forbidden.component.ts b/src/app/forbidden/forbidden.component.ts index 03441f1bff..74cfdf31ce 100644 --- a/src/app/forbidden/forbidden.component.ts +++ b/src/app/forbidden/forbidden.component.ts @@ -12,7 +12,7 @@ import { ServerResponseService } from '../core/services/server-response.service' * This component representing the `Forbidden` DSpace page. */ @Component({ - selector: 'ds-forbidden', + selector: 'ds-base-forbidden', templateUrl: './forbidden.component.html', styleUrls: ['./forbidden.component.scss'], standalone: true, diff --git a/src/app/forbidden/themed-forbidden.component.ts b/src/app/forbidden/themed-forbidden.component.ts index 85efec18ee..4d1b6d6fb7 100644 --- a/src/app/forbidden/themed-forbidden.component.ts +++ b/src/app/forbidden/themed-forbidden.component.ts @@ -7,10 +7,11 @@ import { ForbiddenComponent } from './forbidden.component'; * Themed wrapper for ForbiddenComponent */ @Component({ - selector: 'ds-themed-forbidden', + selector: 'ds-forbidden', styleUrls: [], templateUrl: '../shared/theme-support/themed.component.html', standalone: true, + imports: [ForbiddenComponent], }) export class ThemedForbiddenComponent extends ThemedComponent { protected getComponentName(): string { diff --git a/src/app/forgot-password/forgot-password-email/forgot-email.component.html b/src/app/forgot-password/forgot-password-email/forgot-email.component.html index aaa0c27b46..995108cdbc 100644 --- a/src/app/forgot-password/forgot-password-email/forgot-email.component.html +++ b/src/app/forgot-password/forgot-password-email/forgot-email.component.html @@ -1,3 +1,3 @@ - - + diff --git a/src/app/forgot-password/forgot-password-email/forgot-email.component.spec.ts b/src/app/forgot-password/forgot-password-email/forgot-email.component.spec.ts index 5ddeae8051..b9e625caac 100644 --- a/src/app/forgot-password/forgot-password-email/forgot-email.component.spec.ts +++ b/src/app/forgot-password/forgot-password-email/forgot-email.component.spec.ts @@ -8,7 +8,6 @@ import { import { ReactiveFormsModule } from '@angular/forms'; import { TranslateModule } from '@ngx-translate/core'; -import { RegisterEmailFormComponent } from '../../register-email-form/register-email-form.component'; import { ThemedRegisterEmailFormComponent } from '../../register-email-form/themed-registry-email-form.component'; import { ForgotEmailComponent } from './forgot-email.component'; @@ -23,7 +22,7 @@ describe('ForgotEmailComponent', () => { }) .overrideComponent(ForgotEmailComponent, { remove: { - imports: [RegisterEmailFormComponent, ThemedRegisterEmailFormComponent], + imports: [ThemedRegisterEmailFormComponent], }, }) .compileComponents(); diff --git a/src/app/forgot-password/forgot-password-email/forgot-email.component.ts b/src/app/forgot-password/forgot-password-email/forgot-email.component.ts index a7455c4ca9..2ab05e6518 100644 --- a/src/app/forgot-password/forgot-password-email/forgot-email.component.ts +++ b/src/app/forgot-password/forgot-password-email/forgot-email.component.ts @@ -1,17 +1,14 @@ import { Component } from '@angular/core'; import { ThemedRegisterEmailFormComponent } from 'src/app/register-email-form/themed-registry-email-form.component'; -import { - RegisterEmailFormComponent, - TYPE_REQUEST_FORGOT, -} from '../../register-email-form/register-email-form.component'; +import { TYPE_REQUEST_FORGOT } from '../../register-email-form/register-email-form.component'; @Component({ - selector: 'ds-forgot-email', + selector: 'ds-base-forgot-email', styleUrls: ['./forgot-email.component.scss'], templateUrl: './forgot-email.component.html', imports: [ - RegisterEmailFormComponent, ThemedRegisterEmailFormComponent, + ThemedRegisterEmailFormComponent, ], standalone: true, }) diff --git a/src/app/forgot-password/forgot-password-email/themed-forgot-email.component.ts b/src/app/forgot-password/forgot-password-email/themed-forgot-email.component.ts index 936815a49c..af9f557fbb 100644 --- a/src/app/forgot-password/forgot-password-email/themed-forgot-email.component.ts +++ b/src/app/forgot-password/forgot-password-email/themed-forgot-email.component.ts @@ -7,10 +7,11 @@ import { ForgotEmailComponent } from './forgot-email.component'; * Themed wrapper for ForgotEmailComponent */ @Component({ - selector: 'ds-themed-forgot-email', + selector: 'ds-forgot-email', styleUrls: [], templateUrl: './../../shared/theme-support/themed.component.html', standalone: true, + imports: [ForgotEmailComponent], }) export class ThemedForgotEmailComponent extends ThemedComponent { protected getComponentName(): string { diff --git a/src/app/forgot-password/forgot-password-form/forgot-password-form.component.ts b/src/app/forgot-password/forgot-password-form/forgot-password-form.component.ts index 3d8d6e3cd7..442e4bf9fa 100644 --- a/src/app/forgot-password/forgot-password-form/forgot-password-form.component.ts +++ b/src/app/forgot-password/forgot-password-form/forgot-password-form.component.ts @@ -30,7 +30,7 @@ import { NotificationsService } from '../../shared/notifications/notifications.s import { BrowserOnlyPipe } from '../../shared/utils/browser-only.pipe'; @Component({ - selector: 'ds-forgot-password-form', + selector: 'ds-base-forgot-password-form', styleUrls: ['./forgot-password-form.component.scss'], templateUrl: './forgot-password-form.component.html', imports: [ diff --git a/src/app/forgot-password/forgot-password-form/themed-forgot-password-form.component.ts b/src/app/forgot-password/forgot-password-form/themed-forgot-password-form.component.ts index e74fed2f36..956568e2bf 100644 --- a/src/app/forgot-password/forgot-password-form/themed-forgot-password-form.component.ts +++ b/src/app/forgot-password/forgot-password-form/themed-forgot-password-form.component.ts @@ -7,10 +7,11 @@ import { ForgotPasswordFormComponent } from './forgot-password-form.component'; * Themed wrapper for ForgotPasswordFormComponent */ @Component({ - selector: 'ds-themed-forgot-password-form', + selector: 'ds-forgot-password-form', styleUrls: [], templateUrl: './../../shared/theme-support/themed.component.html', standalone: true, + imports: [ForgotPasswordFormComponent], }) export class ThemedForgotPasswordFormComponent extends ThemedComponent { protected getComponentName(): string { diff --git a/src/app/header-nav-wrapper/header-navbar-wrapper.component.html b/src/app/header-nav-wrapper/header-navbar-wrapper.component.html index ba3060ff69..60b38dcabf 100644 --- a/src/app/header-nav-wrapper/header-navbar-wrapper.component.html +++ b/src/app/header-nav-wrapper/header-navbar-wrapper.component.html @@ -1,4 +1,4 @@
- - + +
diff --git a/src/app/header-nav-wrapper/header-navbar-wrapper.component.ts b/src/app/header-nav-wrapper/header-navbar-wrapper.component.ts index 862173b9bb..53f1057531 100644 --- a/src/app/header-nav-wrapper/header-navbar-wrapper.component.ts +++ b/src/app/header-nav-wrapper/header-navbar-wrapper.component.ts @@ -21,7 +21,7 @@ import { MenuID } from '../shared/menu/menu-id.model'; * This component represents a wrapper for the horizontal navbar and the header */ @Component({ - selector: 'ds-header-navbar-wrapper', + selector: 'ds-base-header-navbar-wrapper', styleUrls: ['header-navbar-wrapper.component.scss'], templateUrl: 'header-navbar-wrapper.component.html', standalone: true, diff --git a/src/app/header-nav-wrapper/themed-header-navbar-wrapper.component.ts b/src/app/header-nav-wrapper/themed-header-navbar-wrapper.component.ts index 5895530e8a..64d36edae3 100644 --- a/src/app/header-nav-wrapper/themed-header-navbar-wrapper.component.ts +++ b/src/app/header-nav-wrapper/themed-header-navbar-wrapper.component.ts @@ -7,10 +7,11 @@ import { HeaderNavbarWrapperComponent } from './header-navbar-wrapper.component' * Themed wrapper for {@link HeaderNavbarWrapperComponent} */ @Component({ - selector: 'ds-themed-header-navbar-wrapper', + selector: 'ds-header-navbar-wrapper', styleUrls: [], templateUrl: '../shared/theme-support/themed.component.html', standalone: true, + imports: [HeaderNavbarWrapperComponent], }) export class ThemedHeaderNavbarWrapperComponent extends ThemedComponent { protected getComponentName(): string { diff --git a/src/app/header/header.component.html b/src/app/header/header.component.html index e98959d162..e59086134a 100644 --- a/src/app/header/header.component.html +++ b/src/app/header/header.component.html @@ -6,10 +6,10 @@
- +
diff --git a/src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/paginated-drag-and-drop-bitstream-list/paginated-drag-and-drop-bitstream-list.component.html b/src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/paginated-drag-and-drop-bitstream-list/paginated-drag-and-drop-bitstream-list.component.html index 20b5dc708f..c3d6ebc823 100644 --- a/src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/paginated-drag-and-drop-bitstream-list/paginated-drag-and-drop-bitstream-list.component.html +++ b/src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/paginated-drag-and-drop-bitstream-list/paginated-drag-and-drop-bitstream-list.component.html @@ -28,5 +28,5 @@
- + diff --git a/src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream/item-edit-bitstream.component.ts b/src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream/item-edit-bitstream.component.ts index 5706805661..81950d3321 100644 --- a/src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream/item-edit-bitstream.component.ts +++ b/src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream/item-edit-bitstream.component.ts @@ -145,7 +145,7 @@ export class ItemEditBitstreamComponent implements OnChanges, OnDestroy, OnInit * Check if a user should be allowed to cancel the update to this field */ canUndo(): boolean { - return this.fieldUpdate.changeType >= 0; + return this.fieldUpdate.changeType?.valueOf() >= 0; } } diff --git a/src/app/item-page/edit-item-page/item-collection-mapper/item-collection-mapper.component.html b/src/app/item-page/edit-item-page/item-collection-mapper/item-collection-mapper.component.html index 57287fdfe2..9e298edcc6 100644 --- a/src/app/item-page/edit-item-page/item-collection-mapper/item-collection-mapper.component.html +++ b/src/app/item-page/edit-item-page/item-collection-mapper/item-collection-mapper.component.html @@ -27,13 +27,13 @@
- - +
diff --git a/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.html b/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.html index 5015c8e1e5..0a99a5820f 100644 --- a/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.html +++ b/src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.html @@ -30,5 +30,5 @@
{{"item.edit.relationships.no-relationships" | translate}}
- + diff --git a/src/app/item-page/edit-item-page/item-relationships/edit-relationship/edit-relationship.component.ts b/src/app/item-page/edit-item-page/item-relationships/edit-relationship/edit-relationship.component.ts index 8341070537..5f213327e3 100644 --- a/src/app/item-page/edit-item-page/item-relationships/edit-relationship/edit-relationship.component.ts +++ b/src/app/item-page/edit-item-page/item-relationships/edit-relationship/edit-relationship.component.ts @@ -199,6 +199,6 @@ export class EditRelationshipComponent implements OnChanges { * Check if a user should be allowed to cancel the update to this field */ canUndo(): boolean { - return this.fieldUpdate.changeType >= 0; + return this.fieldUpdate.changeType?.valueOf() >= 0; } } diff --git a/src/app/item-page/edit-item-page/item-relationships/item-relationships.component.html b/src/app/item-page/edit-item-page/item-relationships/item-relationships.component.html index 3a0a33d1a0..c3556962d4 100644 --- a/src/app/item-page/edit-item-page/item-relationships/item-relationships.component.html +++ b/src/app/item-page/edit-item-page/item-relationships/item-relationships.component.html @@ -32,7 +32,7 @@ >
- +
diff --git a/src/app/item-page/edit-item-page/item-status/item-status.component.ts b/src/app/item-page/edit-item-page/item-status/item-status.component.ts index f330c2e457..bbd7b99a97 100644 --- a/src/app/item-page/edit-item-page/item-status/item-status.component.ts +++ b/src/app/item-page/edit-item-page/item-status/item-status.component.ts @@ -58,7 +58,7 @@ import { ItemOperationComponent } from '../item-operation/item-operation.compone import { ItemOperation } from '../item-operation/itemOperation.model'; @Component({ - selector: 'ds-item-status', + selector: 'ds-base-item-status', templateUrl: './item-status.component.html', changeDetection: ChangeDetectionStrategy.Default, animations: [ diff --git a/src/app/item-page/edit-item-page/item-status/themed-item-status.component.ts b/src/app/item-page/edit-item-page/item-status/themed-item-status.component.ts index 705ef6d8ad..c3ba17a4b5 100644 --- a/src/app/item-page/edit-item-page/item-status/themed-item-status.component.ts +++ b/src/app/item-page/edit-item-page/item-status/themed-item-status.component.ts @@ -4,10 +4,11 @@ import { ThemedComponent } from '../../../shared/theme-support/themed.component' import { ItemStatusComponent } from './item-status.component'; @Component({ - selector: 'ds-themed-item-status', + selector: 'ds-item-status', styleUrls: [], templateUrl: '../../../shared/theme-support/themed.component.html', standalone: true, + imports: [ItemStatusComponent], }) export class ThemedItemStatusComponent extends ThemedComponent { protected getComponentName(): string { diff --git a/src/app/item-page/field-components/metadata-values/metadata-values.component.ts b/src/app/item-page/field-components/metadata-values/metadata-values.component.ts index abc26c0d82..1a73d692eb 100644 --- a/src/app/item-page/field-components/metadata-values/metadata-values.component.ts +++ b/src/app/item-page/field-components/metadata-values/metadata-values.component.ts @@ -117,6 +117,8 @@ export class MetadataValuesComponent implements OnChanges { */ getQueryParams(value) { const queryParams = { startsWith: value }; + // todo: should compare with type instead? + // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison if (this.browseDefinition.getRenderType() === VALUE_LIST_BROWSE_DEFINITION.value) { return { value: value }; } diff --git a/src/app/item-page/full/field-components/file-section/full-file-section.component.html b/src/app/item-page/full/field-components/file-section/full-file-section.component.html index d4b58742c4..8c534e6630 100644 --- a/src/app/item-page/full/field-components/file-section/full-file-section.component.html +++ b/src/app/item-page/full/field-components/file-section/full-file-section.component.html @@ -12,7 +12,7 @@
- +
@@ -33,9 +33,9 @@
- + {{"item.page.filesection.download" | translate}} - +
@@ -54,7 +54,7 @@
- +
@@ -73,9 +73,9 @@
- + {{"item.page.filesection.download" | translate}} - +
diff --git a/src/app/item-page/full/field-components/file-section/full-file-section.component.ts b/src/app/item-page/full/field-components/file-section/full-file-section.component.ts index 8f8b01eb26..f038b18bfc 100644 --- a/src/app/item-page/full/field-components/file-section/full-file-section.component.ts +++ b/src/app/item-page/full/field-components/file-section/full-file-section.component.ts @@ -52,7 +52,7 @@ import { FileSectionComponent } from '../../../simple/field-components/file-sect */ @Component({ - selector: 'ds-item-page-full-file-section', + selector: 'ds-base-item-page-full-file-section', styleUrls: ['./full-file-section.component.scss'], templateUrl: './full-file-section.component.html', imports: [ diff --git a/src/app/item-page/full/field-components/file-section/themed-full-file-section.component.ts b/src/app/item-page/full/field-components/file-section/themed-full-file-section.component.ts index 38a4879715..425b0cffd8 100644 --- a/src/app/item-page/full/field-components/file-section/themed-full-file-section.component.ts +++ b/src/app/item-page/full/field-components/file-section/themed-full-file-section.component.ts @@ -11,10 +11,11 @@ import { FullFileSectionComponent } from './full-file-section.component'; * Themed wrapper for {@link FullFileSectionComponent} */ @Component({ - selector: 'ds-themed-item-page-full-file-section', + selector: 'ds-item-page-full-file-section', styleUrls: [], templateUrl: './../../../../shared/theme-support/themed.component.html', standalone: true, + imports: [FullFileSectionComponent], }) export class ThemedFullFileSectionComponent extends ThemedComponent { diff --git a/src/app/item-page/full/full-item-page.component.html b/src/app/item-page/full/full-item-page.component.html index 1d83181395..b6e1d1e6ed 100644 --- a/src/app/item-page/full/full-item-page.component.html +++ b/src/app/item-page/full/full-item-page.component.html @@ -1,12 +1,12 @@
- +
- +
- +
@@ -40,5 +40,5 @@
- +
diff --git a/src/app/item-page/full/full-item-page.component.ts b/src/app/item-page/full/full-item-page.component.ts index fba1d00e9b..c041d80d1c 100644 --- a/src/app/item-page/full/full-item-page.component.ts +++ b/src/app/item-page/full/full-item-page.component.ts @@ -60,7 +60,7 @@ import { ThemedFullFileSectionComponent } from './field-components/file-section/ */ @Component({ - selector: 'ds-full-item-page', + selector: 'ds-base-full-item-page', styleUrls: ['./full-item-page.component.scss'], templateUrl: './full-item-page.component.html', changeDetection: ChangeDetectionStrategy.OnPush, diff --git a/src/app/item-page/full/themed-full-item-page.component.ts b/src/app/item-page/full/themed-full-item-page.component.ts index f29947b075..f5034d3042 100644 --- a/src/app/item-page/full/themed-full-item-page.component.ts +++ b/src/app/item-page/full/themed-full-item-page.component.ts @@ -7,10 +7,11 @@ import { FullItemPageComponent } from './full-item-page.component'; * Themed wrapper for FullItemPageComponent */ @Component({ - selector: 'ds-themed-full-item-page', + selector: 'ds-full-item-page', styleUrls: [], templateUrl: './../../shared/theme-support/themed.component.html', standalone: true, + imports: [FullItemPageComponent], }) export class ThemedFullItemPageComponent extends ThemedComponent { protected getComponentName(): string { diff --git a/src/app/item-page/media-viewer/media-viewer-image/media-viewer-image.component.ts b/src/app/item-page/media-viewer/media-viewer-image/media-viewer-image.component.ts index 4523068118..f2191f074d 100644 --- a/src/app/item-page/media-viewer/media-viewer-image/media-viewer-image.component.ts +++ b/src/app/item-page/media-viewer/media-viewer-image/media-viewer-image.component.ts @@ -20,7 +20,7 @@ import { MediaViewerItem } from '../../../core/shared/media-viewer-item.model'; * This componenet render an image gallery for the image viewer */ @Component({ - selector: 'ds-media-viewer-image', + selector: 'ds-base-media-viewer-image', templateUrl: './media-viewer-image.component.html', styleUrls: ['./media-viewer-image.component.scss'], imports: [ diff --git a/src/app/item-page/media-viewer/media-viewer-image/themed-media-viewer-image.component.ts b/src/app/item-page/media-viewer/media-viewer-image/themed-media-viewer-image.component.ts index 84ffeaca5c..d7df6da629 100644 --- a/src/app/item-page/media-viewer/media-viewer-image/themed-media-viewer-image.component.ts +++ b/src/app/item-page/media-viewer/media-viewer-image/themed-media-viewer-image.component.ts @@ -11,10 +11,11 @@ import { MediaViewerImageComponent } from './media-viewer-image.component'; * Themed wrapper for {@link MediaViewerImageComponent}. */ @Component({ - selector: 'ds-themed-media-viewer-image', + selector: 'ds-media-viewer-image', styleUrls: [], templateUrl: '../../../shared/theme-support/themed.component.html', standalone: true, + imports: [MediaViewerImageComponent], }) export class ThemedMediaViewerImageComponent extends ThemedComponent { diff --git a/src/app/item-page/media-viewer/media-viewer-video/media-viewer-video.component.ts b/src/app/item-page/media-viewer/media-viewer-video/media-viewer-video.component.ts index 7523e0109f..700431ff3f 100644 --- a/src/app/item-page/media-viewer/media-viewer-video/media-viewer-video.component.ts +++ b/src/app/item-page/media-viewer/media-viewer-video/media-viewer-video.component.ts @@ -19,7 +19,7 @@ import { languageHelper } from './language-helper'; * This component renders a video viewer and playlist for the media viewer */ @Component({ - selector: 'ds-media-viewer-video', + selector: 'ds-base-media-viewer-video', templateUrl: './media-viewer-video.component.html', styleUrls: ['./media-viewer-video.component.scss'], imports: [ diff --git a/src/app/item-page/media-viewer/media-viewer-video/themed-media-viewer-video.component.ts b/src/app/item-page/media-viewer/media-viewer-video/themed-media-viewer-video.component.ts index 797780d3b8..800034835c 100644 --- a/src/app/item-page/media-viewer/media-viewer-video/themed-media-viewer-video.component.ts +++ b/src/app/item-page/media-viewer/media-viewer-video/themed-media-viewer-video.component.ts @@ -12,10 +12,11 @@ import { MediaViewerVideoComponent } from './media-viewer-video.component'; * Themed wrapper for {@link MediaViewerVideoComponent}. */ @Component({ - selector: 'ds-themed-media-viewer-video', + selector: 'ds-media-viewer-video', styleUrls: [], templateUrl: '../../../shared/theme-support/themed.component.html', standalone: true, + imports: [MediaViewerVideoComponent], }) export class ThemedMediaViewerVideoComponent extends ThemedComponent { diff --git a/src/app/item-page/media-viewer/media-viewer.component.html b/src/app/item-page/media-viewer/media-viewer.component.html index c8a02e039c..a76ee73963 100644 --- a/src/app/item-page/media-viewer/media-viewer.component.html +++ b/src/app/item-page/media-viewer/media-viewer.component.html @@ -1,25 +1,25 @@ - + >
- - + +
- + > diff --git a/src/app/item-page/media-viewer/media-viewer.component.spec.ts b/src/app/item-page/media-viewer/media-viewer.component.spec.ts index 4d02307452..7649e17b71 100644 --- a/src/app/item-page/media-viewer/media-viewer.component.spec.ts +++ b/src/app/item-page/media-viewer/media-viewer.component.spec.ts @@ -124,7 +124,7 @@ describe('MediaViewerComponent', () => { }); it('should display a loading component', () => { - const loading = fixture.debugElement.query(By.css('ds-themed-loading')); + const loading = fixture.debugElement.query(By.css('ds-loading')); expect(loading.nativeElement).toBeDefined(); }); }); @@ -152,7 +152,7 @@ describe('MediaViewerComponent', () => { it('should display a default, thumbnail', () => { const defaultThumbnail = fixture.debugElement.query( - By.css('ds-themed-media-viewer-image'), + By.css('ds-media-viewer-image'), ); expect(defaultThumbnail.nativeElement).toBeDefined(); }); diff --git a/src/app/item-page/media-viewer/media-viewer.component.ts b/src/app/item-page/media-viewer/media-viewer.component.ts index fbeadee905..7513f5dada 100644 --- a/src/app/item-page/media-viewer/media-viewer.component.ts +++ b/src/app/item-page/media-viewer/media-viewer.component.ts @@ -34,7 +34,7 @@ import { hasValue } from '../../shared/empty.util'; import { ThemedLoadingComponent } from '../../shared/loading/themed-loading.component'; import { followLink } from '../../shared/utils/follow-link-config.model'; import { VarDirective } from '../../shared/utils/var.directive'; -import { ThumbnailComponent } from '../../thumbnail/thumbnail.component'; +import { ThemedThumbnailComponent } from '../../thumbnail/themed-thumbnail.component'; import { ThemedMediaViewerImageComponent } from './media-viewer-image/themed-media-viewer-image.component'; import { ThemedMediaViewerVideoComponent } from './media-viewer-video/themed-media-viewer-video.component'; @@ -42,12 +42,12 @@ import { ThemedMediaViewerVideoComponent } from './media-viewer-video/themed-med * This component renders the media viewers */ @Component({ - selector: 'ds-media-viewer', + selector: 'ds-base-media-viewer', templateUrl: './media-viewer.component.html', styleUrls: ['./media-viewer.component.scss'], imports: [ ThemedMediaViewerImageComponent, - ThumbnailComponent, + ThemedThumbnailComponent, AsyncPipe, NgIf, ThemedMediaViewerVideoComponent, diff --git a/src/app/item-page/media-viewer/themed-media-viewer.component.ts b/src/app/item-page/media-viewer/themed-media-viewer.component.ts index da790fbd4a..0fa5657094 100644 --- a/src/app/item-page/media-viewer/themed-media-viewer.component.ts +++ b/src/app/item-page/media-viewer/themed-media-viewer.component.ts @@ -12,10 +12,11 @@ import { MediaViewerComponent } from './media-viewer.component'; * Themed wrapper for {@link MediaViewerComponent}. */ @Component({ - selector: 'ds-themed-media-viewer', + selector: 'ds-media-viewer', styleUrls: [], templateUrl: '../../shared/theme-support/themed.component.html', standalone: true, + imports: [MediaViewerComponent], }) export class ThemedMediaViewerComponent extends ThemedComponent { diff --git a/src/app/item-page/orcid-page/orcid-page.component.ts b/src/app/item-page/orcid-page/orcid-page.component.ts index 1277d0d341..a3c31e791d 100644 --- a/src/app/item-page/orcid-page/orcid-page.component.ts +++ b/src/app/item-page/orcid-page/orcid-page.component.ts @@ -38,7 +38,7 @@ import { import { AlertComponent } from '../../shared/alert/alert.component'; import { AlertType } from '../../shared/alert/alert-type'; import { isNotEmpty } from '../../shared/empty.util'; -import { LoadingComponent } from '../../shared/loading/loading.component'; +import { ThemedLoadingComponent } from '../../shared/loading/themed-loading.component'; import { getItemPageRoute } from '../item-page-routing-paths'; import { OrcidAuthComponent } from './orcid-auth/orcid-auth.component'; import { OrcidQueueComponent } from './orcid-queue/orcid-queue.component'; @@ -53,7 +53,7 @@ import { OrcidSyncSettingsComponent } from './orcid-sync-settings/orcid-sync-set styleUrls: ['./orcid-page.component.scss'], imports: [ CommonModule, - LoadingComponent, + ThemedLoadingComponent, AlertComponent, OrcidAuthComponent, OrcidSyncSettingsComponent, diff --git a/src/app/item-page/orcid-page/orcid-queue/orcid-queue.component.ts b/src/app/item-page/orcid-page/orcid-queue/orcid-queue.component.ts index c783e592c6..0f02d7083c 100644 --- a/src/app/item-page/orcid-page/orcid-queue/orcid-queue.component.ts +++ b/src/app/item-page/orcid-page/orcid-queue/orcid-queue.component.ts @@ -37,7 +37,7 @@ import { getFirstCompletedRemoteData } from '../../../core/shared/operators'; import { AlertComponent } from '../../../shared/alert/alert.component'; import { AlertType } from '../../../shared/alert/alert-type'; import { hasValue } from '../../../shared/empty.util'; -import { LoadingComponent } from '../../../shared/loading/loading.component'; +import { ThemedLoadingComponent } from '../../../shared/loading/themed-loading.component'; import { NotificationsService } from '../../../shared/notifications/notifications.service'; import { PaginationComponent } from '../../../shared/pagination/pagination.component'; import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; @@ -50,7 +50,7 @@ import { PaginationComponentOptions } from '../../../shared/pagination/paginatio CommonModule, NgbTooltipModule, TranslateModule, - LoadingComponent, + ThemedLoadingComponent, AlertComponent, PaginationComponent, ], diff --git a/src/app/item-page/simple/field-components/file-section/file-section.component.html b/src/app/item-page/simple/field-components/file-section/file-section.component.html index e1748e8bcf..44c83b8626 100644 --- a/src/app/item-page/simple/field-components/file-section/file-section.component.html +++ b/src/app/item-page/simple/field-components/file-section/file-section.component.html @@ -1,15 +1,15 @@
- + {{ 'item.page.bitstreams.primary' | translate }} {{ dsoNameService.getName(file) }} ({{(file?.sizeBytes) | dsFileSize }}) - - + +
diff --git a/src/app/item-page/simple/field-components/file-section/file-section.component.spec.ts b/src/app/item-page/simple/field-components/file-section/file-section.component.spec.ts index b2fb2bf29f..9ead81c337 100644 --- a/src/app/item-page/simple/field-components/file-section/file-section.component.spec.ts +++ b/src/app/item-page/simple/field-components/file-section/file-section.component.spec.ts @@ -132,7 +132,7 @@ describe('FileSectionComponent', () => { }); it('should display a loading component', () => { - const loading = fixture.debugElement.query(By.css('ds-themed-loading')); + const loading = fixture.debugElement.query(By.css('ds-loading')); expect(loading.nativeElement).toBeDefined(); }); }); @@ -155,7 +155,7 @@ describe('FileSectionComponent', () => { it('one bitstream should be on the page', () => { const viewMore = fixture.debugElement.query(By.css('.bitstream-view-more')); viewMore.triggerEventHandler('click', null); - const fileDownloadLink = fixture.debugElement.queryAll(By.css('ds-themed-file-download-link')); + const fileDownloadLink = fixture.debugElement.queryAll(By.css('ds-file-download-link')); expect(fileDownloadLink.length).toEqual(1); }); @@ -168,7 +168,7 @@ describe('FileSectionComponent', () => { }); it('should contain another bitstream', () => { - const fileDownloadLink = fixture.debugElement.queryAll(By.css('ds-themed-file-download-link')); + const fileDownloadLink = fixture.debugElement.queryAll(By.css('ds-file-download-link')); expect(fileDownloadLink.length).toEqual(2); }); }); diff --git a/src/app/item-page/simple/field-components/file-section/file-section.component.ts b/src/app/item-page/simple/field-components/file-section/file-section.component.ts index 8a88989a05..9df34e0d13 100644 --- a/src/app/item-page/simple/field-components/file-section/file-section.component.ts +++ b/src/app/item-page/simple/field-components/file-section/file-section.component.ts @@ -35,7 +35,7 @@ import { VarDirective } from '../../../../shared/utils/var.directive'; * inside a 'ds-metadata-field-wrapper' component. */ @Component({ - selector: 'ds-item-page-file-section', + selector: 'ds-base-item-page-file-section', templateUrl: './file-section.component.html', imports: [ CommonModule, diff --git a/src/app/item-page/simple/field-components/file-section/themed-file-section.component.ts b/src/app/item-page/simple/field-components/file-section/themed-file-section.component.ts index 7a7229715d..2dd8a1df73 100644 --- a/src/app/item-page/simple/field-components/file-section/themed-file-section.component.ts +++ b/src/app/item-page/simple/field-components/file-section/themed-file-section.component.ts @@ -8,9 +8,10 @@ import { ThemedComponent } from '../../../../shared/theme-support/themed.compone import { FileSectionComponent } from './file-section.component'; @Component({ - selector: 'ds-themed-item-page-file-section', + selector: 'ds-item-page-file-section', templateUrl: '../../../../shared/theme-support/themed.component.html', standalone: true, + imports: [FileSectionComponent], }) export class ThemedFileSectionComponent extends ThemedComponent { diff --git a/src/app/item-page/simple/field-components/specific-field/title/item-page-title-field.component.ts b/src/app/item-page/simple/field-components/specific-field/title/item-page-title-field.component.ts index 7923805462..27d7fc585e 100644 --- a/src/app/item-page/simple/field-components/specific-field/title/item-page-title-field.component.ts +++ b/src/app/item-page/simple/field-components/specific-field/title/item-page-title-field.component.ts @@ -9,7 +9,7 @@ import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service import { Item } from '../../../../../core/shared/item.model'; @Component({ - selector: 'ds-item-page-title-field', + selector: 'ds-base-item-page-title-field', templateUrl: './item-page-title-field.component.html', standalone: true, imports: [NgIf, TranslateModule], diff --git a/src/app/item-page/simple/field-components/specific-field/title/themed-item-page-field.component.ts b/src/app/item-page/simple/field-components/specific-field/title/themed-item-page-field.component.ts index 8e4eb532fc..385a98d47f 100644 --- a/src/app/item-page/simple/field-components/specific-field/title/themed-item-page-field.component.ts +++ b/src/app/item-page/simple/field-components/specific-field/title/themed-item-page-field.component.ts @@ -11,10 +11,11 @@ import { ItemPageTitleFieldComponent } from './item-page-title-field.component'; * Themed wrapper for {@link ItemPageTitleFieldComponent} */ @Component({ - selector: 'ds-themed-item-page-title-field', + selector: 'ds-item-page-title-field', styleUrls: [], templateUrl: '../../../../../shared/theme-support/themed.component.html', standalone: true, + imports: [ItemPageTitleFieldComponent], }) export class ThemedItemPageTitleFieldComponent extends ThemedComponent { diff --git a/src/app/item-page/simple/item-page.component.html b/src/app/item-page/simple/item-page.component.html index dc8ed87a86..912115c961 100644 --- a/src/app/item-page/simple/item-page.component.html +++ b/src/app/item-page/simple/item-page.component.html @@ -1,7 +1,7 @@
- + @@ -11,5 +11,5 @@
- +
diff --git a/src/app/item-page/simple/item-page.component.spec.ts b/src/app/item-page/simple/item-page.component.spec.ts index 2766bee3a5..827af05b65 100644 --- a/src/app/item-page/simple/item-page.component.spec.ts +++ b/src/app/item-page/simple/item-page.component.spec.ts @@ -182,7 +182,7 @@ describe('ItemPageComponent', () => { }); it('should display a loading component', () => { - const loading = fixture.debugElement.query(By.css('ds-themed-loading')); + const loading = fixture.debugElement.query(By.css('ds-loading')); expect(loading.nativeElement).toBeDefined(); }); }); diff --git a/src/app/item-page/simple/item-page.component.ts b/src/app/item-page/simple/item-page.component.ts index cb6da792a1..f1155fd10f 100644 --- a/src/app/item-page/simple/item-page.component.ts +++ b/src/app/item-page/simple/item-page.component.ts @@ -64,7 +64,7 @@ import { QaEventNotificationComponent } from './qa-event-notification/qa-event-n * All fields of the item that should be displayed, are defined in its template. */ @Component({ - selector: 'ds-item-page', + selector: 'ds-base-item-page', styleUrls: ['./item-page.component.scss'], templateUrl: './item-page.component.html', changeDetection: ChangeDetectionStrategy.OnPush, diff --git a/src/app/item-page/simple/item-types/publication/publication.component.html b/src/app/item-page/simple/item-types/publication/publication.component.html index d09b560042..5f96682be2 100644 --- a/src/app/item-page/simple/item-types/publication/publication.component.html +++ b/src/app/item-page/simple/item-types/publication/publication.component.html @@ -1,4 +1,4 @@ - +
- - + +
- +
- +
- + - - + diff --git a/src/app/item-page/simple/item-types/untyped-item/untyped-item.component.html b/src/app/item-page/simple/item-types/untyped-item/untyped-item.component.html index 35a00dc7b7..5ab42556e2 100644 --- a/src/app/item-page/simple/item-types/untyped-item/untyped-item.component.html +++ b/src/app/item-page/simple/item-types/untyped-item/untyped-item.component.html @@ -1,4 +1,4 @@ - +
- - + +
- +
- +
- + - - + diff --git a/src/app/item-page/simple/metadata-representation-list/metadata-representation-list.component.html b/src/app/item-page/simple/metadata-representation-list/metadata-representation-list.component.html index efbe9206d1..424c2324fa 100644 --- a/src/app/item-page/simple/metadata-representation-list/metadata-representation-list.component.html +++ b/src/app/item-page/simple/metadata-representation-list/metadata-representation-list.component.html @@ -4,7 +4,7 @@ - +
diff --git a/src/app/login-page/login-page.component.ts b/src/app/login-page/login-page.component.ts index 286de60424..fad35b84b6 100644 --- a/src/app/login-page/login-page.component.ts +++ b/src/app/login-page/login-page.component.ts @@ -28,18 +28,17 @@ import { hasValue, isNotEmpty, } from '../shared/empty.util'; -import { LogInComponent } from '../shared/log-in/log-in.component'; import { ThemedLogInComponent } from '../shared/log-in/themed-log-in.component'; /** * This component represents the login page */ @Component({ - selector: 'ds-login-page', + selector: 'ds-base-login-page', styleUrls: ['./login-page.component.scss'], templateUrl: './login-page.component.html', standalone: true, - imports: [LogInComponent, ThemedLogInComponent, TranslateModule], + imports: [ThemedLogInComponent, TranslateModule], }) export class LoginPageComponent implements OnDestroy, OnInit { diff --git a/src/app/login-page/themed-login-page.component.ts b/src/app/login-page/themed-login-page.component.ts index a9956751b4..1584c479d5 100644 --- a/src/app/login-page/themed-login-page.component.ts +++ b/src/app/login-page/themed-login-page.component.ts @@ -7,10 +7,11 @@ import { LoginPageComponent } from './login-page.component'; * Themed wrapper for LoginPageComponent */ @Component({ - selector: 'ds-themed-login-page', + selector: 'ds-login-page', styleUrls: [], templateUrl: './../shared/theme-support/themed.component.html', standalone: true, + imports: [LoginPageComponent], }) export class ThemedLoginPageComponent extends ThemedComponent { protected getComponentName(): string { diff --git a/src/app/logout-page/logout-page.component.ts b/src/app/logout-page/logout-page.component.ts index b4623ee215..8859a92a8b 100644 --- a/src/app/logout-page/logout-page.component.ts +++ b/src/app/logout-page/logout-page.component.ts @@ -4,7 +4,7 @@ import { TranslateModule } from '@ngx-translate/core'; import { LogOutComponent } from '../shared/log-out/log-out.component'; @Component({ - selector: 'ds-logout-page', + selector: 'ds-base-logout-page', styleUrls: ['./logout-page.component.scss'], templateUrl: './logout-page.component.html', standalone: true, diff --git a/src/app/logout-page/themed-logout-page.component.ts b/src/app/logout-page/themed-logout-page.component.ts index 3c89cb0165..72be4a053b 100644 --- a/src/app/logout-page/themed-logout-page.component.ts +++ b/src/app/logout-page/themed-logout-page.component.ts @@ -7,10 +7,11 @@ import { LogoutPageComponent } from './logout-page.component'; * Themed wrapper for LogoutPageComponent */ @Component({ - selector: 'ds-themed-logout-page', + selector: 'ds-logout-page', styleUrls: [], templateUrl: './../shared/theme-support/themed.component.html', standalone: true, + imports: [LogoutPageComponent], }) export class ThemedLogoutPageComponent extends ThemedComponent { protected getComponentName(): string { diff --git a/src/app/lookup-by-id/objectnotfound/objectnotfound.component.ts b/src/app/lookup-by-id/objectnotfound/objectnotfound.component.ts index 4e67f6e8bf..e8ab615e9e 100644 --- a/src/app/lookup-by-id/objectnotfound/objectnotfound.component.ts +++ b/src/app/lookup-by-id/objectnotfound/objectnotfound.component.ts @@ -15,7 +15,7 @@ import { ServerResponseService } from 'src/app/core/services/server-response.ser * This component representing the `PageNotFound` DSpace page. */ @Component({ - selector: 'ds-objnotfound', + selector: 'ds-base-objnotfound', styleUrls: ['./objectnotfound.component.scss'], templateUrl: './objectnotfound.component.html', changeDetection: ChangeDetectionStrategy.Default, diff --git a/src/app/lookup-by-id/objectnotfound/themed-objectnotfound.component.ts b/src/app/lookup-by-id/objectnotfound/themed-objectnotfound.component.ts index 4474a9724f..9b46f0efd2 100644 --- a/src/app/lookup-by-id/objectnotfound/themed-objectnotfound.component.ts +++ b/src/app/lookup-by-id/objectnotfound/themed-objectnotfound.component.ts @@ -7,10 +7,11 @@ import { ObjectNotFoundComponent } from './objectnotfound.component'; * Themed wrapper for ObjectNotFoundComponent */ @Component({ - selector: 'ds-themed-objnotfound', + selector: 'ds-objnotfound', styleUrls: [], templateUrl: '../../shared/theme-support/themed.component.html', standalone: true, + imports: [ObjectNotFoundComponent], }) export class ThemedObjectNotFoundComponent extends ThemedComponent { protected getComponentName(): string { diff --git a/src/app/my-dspace-page/collection-selector/collection-selector.component.html b/src/app/my-dspace-page/collection-selector/collection-selector.component.html index 6e2a1925c5..a87118fc4e 100644 --- a/src/app/my-dspace-page/collection-selector/collection-selector.component.html +++ b/src/app/my-dspace-page/collection-selector/collection-selector.component.html @@ -5,7 +5,7 @@
diff --git a/src/app/my-dspace-page/collection-selector/collection-selector.component.spec.ts b/src/app/my-dspace-page/collection-selector/collection-selector.component.spec.ts index 76a61aa3f9..3740e6fa57 100644 --- a/src/app/my-dspace-page/collection-selector/collection-selector.component.spec.ts +++ b/src/app/my-dspace-page/collection-selector/collection-selector.component.spec.ts @@ -2,7 +2,6 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, - Component, ElementRef, NO_ERRORS_SCHEMA, } from '@angular/core'; @@ -129,14 +128,13 @@ describe('CollectionSelectorComponent', () => { }, }), CollectionSelectorComponent, - // CollectionDropdownComponent, + CollectionDropdownComponent, ], providers: [ { provide: CollectionDataService, useValue: collectionDataServiceMock }, { provide: ElementRef, useClass: MockElementRef }, { provide: NgbActiveModal, useValue: modal }, { provide: ActivatedRoute, useValue: {} }, - { provide: CollectionDropdownComponent, useClass: CollectionDropdownStubComponent }, ChangeDetectorRef, ], schemas: [NO_ERRORS_SCHEMA], @@ -153,7 +151,7 @@ describe('CollectionSelectorComponent', () => { scheduler = getTestScheduler(); fixture = TestBed.overrideComponent(CollectionSelectorComponent, { set: { - template: '', + template: '', }, }).createComponent(CollectionSelectorComponent); component = fixture.componentInstance; @@ -180,19 +178,3 @@ describe('CollectionSelectorComponent', () => { expect((component as any).activeModal.close).toHaveBeenCalled(); }); }); - -@Component({ - selector: 'ds-collection-dropdown', - template: ` - `, - standalone: true, -}) -export class CollectionDropdownStubComponent { - test() { - return 'test'; - } -} diff --git a/src/app/my-dspace-page/my-dspace-new-submission/my-dspace-new-submission-dropdown/my-dspace-new-submission-dropdown.component.ts b/src/app/my-dspace-page/my-dspace-new-submission/my-dspace-new-submission-dropdown/my-dspace-new-submission-dropdown.component.ts index c816a3aa2c..8604c3322c 100644 --- a/src/app/my-dspace-page/my-dspace-new-submission/my-dspace-new-submission-dropdown/my-dspace-new-submission-dropdown.component.ts +++ b/src/app/my-dspace-page/my-dspace-new-submission/my-dspace-new-submission-dropdown/my-dspace-new-submission-dropdown.component.ts @@ -28,7 +28,7 @@ import { FindListOptions } from '../../../core/data/find-list-options.model'; import { PaginatedList } from '../../../core/data/paginated-list.model'; import { RemoteData } from '../../../core/data/remote-data'; import { ItemType } from '../../../core/shared/item-relationships/item-type.model'; -import { CreateItemParentSelectorComponent } from '../../../shared/dso-selector/modal-wrappers/create-item-parent-selector/create-item-parent-selector.component'; +import { ThemedCreateItemParentSelectorComponent } from '../../../shared/dso-selector/modal-wrappers/create-item-parent-selector/themed-create-item-parent-selector.component'; import { hasValue } from '../../../shared/empty.util'; import { EntityDropdownComponent } from '../../../shared/entity-dropdown/entity-dropdown.component'; import { BrowserOnlyPipe } from '../../../shared/utils/browser-only.pipe'; @@ -124,7 +124,7 @@ export class MyDSpaceNewSubmissionDropdownComponent implements OnInit, OnDestroy * select a collection. */ openDialog(entity: ItemType) { - const modalRef = this.modalService.open(CreateItemParentSelectorComponent); + const modalRef = this.modalService.open(ThemedCreateItemParentSelectorComponent); modalRef.componentInstance.entityType = entity.label; } diff --git a/src/app/my-dspace-page/my-dspace-page.component.html b/src/app/my-dspace-page/my-dspace-page.component.html index cfae8e07a8..9e5cadc12b 100644 --- a/src/app/my-dspace-page/my-dspace-page.component.html +++ b/src/app/my-dspace-page/my-dspace-page.component.html @@ -4,9 +4,9 @@
- +> diff --git a/src/app/my-dspace-page/my-dspace-page.component.ts b/src/app/my-dspace-page/my-dspace-page.component.ts index dceb1c31d3..0607b27fef 100644 --- a/src/app/my-dspace-page/my-dspace-page.component.ts +++ b/src/app/my-dspace-page/my-dspace-page.component.ts @@ -34,7 +34,7 @@ export const MYDSPACE_ROUTE = '/mydspace'; * This component represents the whole mydspace page */ @Component({ - selector: 'ds-my-dspace-page', + selector: 'ds-base-my-dspace-page', styleUrls: ['./my-dspace-page.component.scss'], templateUrl: './my-dspace-page.component.html', changeDetection: ChangeDetectionStrategy.OnPush, diff --git a/src/app/my-dspace-page/themed-my-dspace-page.component.ts b/src/app/my-dspace-page/themed-my-dspace-page.component.ts index 1198360fbd..d6979b3956 100644 --- a/src/app/my-dspace-page/themed-my-dspace-page.component.ts +++ b/src/app/my-dspace-page/themed-my-dspace-page.component.ts @@ -7,10 +7,11 @@ import { MyDSpacePageComponent } from './my-dspace-page.component'; * Themed wrapper for MyDSpacePageComponent */ @Component({ - selector: 'ds-themed-my-dspace-page', + selector: 'ds-my-dspace-page', styleUrls: [], templateUrl: './../shared/theme-support/themed.component.html', standalone: true, + imports: [MyDSpacePageComponent], }) export class ThemedMyDSpacePageComponent extends ThemedComponent { diff --git a/src/app/navbar/expandable-navbar-section/expandable-navbar-section.component.ts b/src/app/navbar/expandable-navbar-section/expandable-navbar-section.component.ts index 1ddc519869..92da978af7 100644 --- a/src/app/navbar/expandable-navbar-section/expandable-navbar-section.component.ts +++ b/src/app/navbar/expandable-navbar-section/expandable-navbar-section.component.ts @@ -26,7 +26,7 @@ import { NavbarSectionComponent } from '../navbar-section/navbar-section.compone * Represents an expandable section in the navbar */ @Component({ - selector: 'ds-expandable-navbar-section', + selector: 'ds-base-expandable-navbar-section', templateUrl: './expandable-navbar-section.component.html', styleUrls: ['./expandable-navbar-section.component.scss'], animations: [slide], diff --git a/src/app/navbar/expandable-navbar-section/themed-expandable-navbar-section.component.ts b/src/app/navbar/expandable-navbar-section/themed-expandable-navbar-section.component.ts index 2345ef7ab3..7b0efb8123 100644 --- a/src/app/navbar/expandable-navbar-section/themed-expandable-navbar-section.component.ts +++ b/src/app/navbar/expandable-navbar-section/themed-expandable-navbar-section.component.ts @@ -7,10 +7,11 @@ import { ExpandableNavbarSectionComponent } from './expandable-navbar-section.co * Themed wrapper for ExpandableNavbarSectionComponent */ @Component({ - selector: 'ds-themed-expandable-navbar-section', + selector: 'ds-expandable-navbar-section', styleUrls: [], templateUrl: '../../shared/theme-support/themed.component.html', standalone: true, + imports: [ExpandableNavbarSectionComponent], }) export class ThemedExpandableNavbarSectionComponent extends ThemedComponent { protected getComponentName(): string { diff --git a/src/app/navbar/navbar.component.html b/src/app/navbar/navbar.component.html index 63938617f5..90cf07caa4 100644 --- a/src/app/navbar/navbar.component.html +++ b/src/app/navbar/navbar.component.html @@ -7,7 +7,7 @@
- + - - +
diff --git a/src/app/notifications/qa/project-entry-import-modal/project-entry-import-modal.component.spec.ts b/src/app/notifications/qa/project-entry-import-modal/project-entry-import-modal.component.spec.ts index 8b098a89ee..dada55a423 100644 --- a/src/app/notifications/qa/project-entry-import-modal/project-entry-import-modal.component.spec.ts +++ b/src/app/notifications/qa/project-entry-import-modal/project-entry-import-modal.component.spec.ts @@ -19,7 +19,7 @@ import { Item } from '../../../core/shared/item.model'; import { PageInfo } from '../../../core/shared/page-info.model'; import { SearchService } from '../../../core/shared/search/search.service'; import { AlertComponent } from '../../../shared/alert/alert.component'; -import { LoadingComponent } from '../../../shared/loading/loading.component'; +import { ThemedLoadingComponent } from '../../../shared/loading/themed-loading.component'; import { ItemMockPid10, NotificationsMockDspaceObject, @@ -106,7 +106,7 @@ describe('ProjectEntryImportModalComponent test suite', () => { .overrideComponent(ProjectEntryImportModalComponent, { remove: { imports: [ - LoadingComponent, + ThemedLoadingComponent, ThemedSearchResultsComponent, AlertComponent, ], diff --git a/src/app/notifications/qa/project-entry-import-modal/project-entry-import-modal.component.ts b/src/app/notifications/qa/project-entry-import-modal/project-entry-import-modal.component.ts index 08e89fec05..4b31e2316a 100644 --- a/src/app/notifications/qa/project-entry-import-modal/project-entry-import-modal.component.ts +++ b/src/app/notifications/qa/project-entry-import-modal/project-entry-import-modal.component.ts @@ -33,7 +33,7 @@ import { hasValue, isNotEmpty, } from '../../../shared/empty.util'; -import { LoadingComponent } from '../../../shared/loading/loading.component'; +import { ThemedLoadingComponent } from '../../../shared/loading/themed-loading.component'; import { CollectionElementLinkType } from '../../../shared/object-collection/collection-element-link.type'; import { ListableObject } from '../../../shared/object-collection/shared/listable-object.model'; import { SelectableListService } from '../../../shared/object-list/selectable-list/selectable-list.service'; @@ -104,7 +104,7 @@ export interface QualityAssuranceEventData { styleUrls: ['./project-entry-import-modal.component.scss'], templateUrl: './project-entry-import-modal.component.html', standalone: true, - imports: [RouterLink, NgIf, FormsModule, LoadingComponent, ThemedSearchResultsComponent, AlertComponent, AsyncPipe, TranslateModule], + imports: [RouterLink, NgIf, FormsModule, ThemedLoadingComponent, ThemedSearchResultsComponent, AlertComponent, AsyncPipe, TranslateModule], }) /** * Component to display a modal window for linking a project to an Quality Assurance event diff --git a/src/app/notifications/qa/source/quality-assurance-source.component.spec.ts b/src/app/notifications/qa/source/quality-assurance-source.component.spec.ts index 220bf3f74a..36309fd937 100644 --- a/src/app/notifications/qa/source/quality-assurance-source.component.spec.ts +++ b/src/app/notifications/qa/source/quality-assurance-source.component.spec.ts @@ -16,7 +16,7 @@ import { of as observableOf } from 'rxjs'; import { PaginationService } from '../../../core/pagination/pagination.service'; import { AlertComponent } from '../../../shared/alert/alert.component'; -import { LoadingComponent } from '../../../shared/loading/loading.component'; +import { ThemedLoadingComponent } from '../../../shared/loading/themed-loading.component'; import { getMockNotificationsStateService, qualityAssuranceSourceObjectMoreAbstract, @@ -61,7 +61,7 @@ describe('QualityAssuranceSourceComponent test suite', () => { remove: { imports: [ AlertComponent, - LoadingComponent, + ThemedLoadingComponent, PaginationComponent, ], }, diff --git a/src/app/notifications/qa/source/quality-assurance-source.component.ts b/src/app/notifications/qa/source/quality-assurance-source.component.ts index b43fd597d5..a918b8c3b6 100644 --- a/src/app/notifications/qa/source/quality-assurance-source.component.ts +++ b/src/app/notifications/qa/source/quality-assurance-source.component.ts @@ -25,7 +25,7 @@ import { PaginationService } from '../../../core/pagination/pagination.service'; import { QualityAssuranceSourcePageParams } from '../../../quality-assurance-notifications-pages/quality-assurance-source-page-component/quality-assurance-source-page-resolver.service'; import { AlertComponent } from '../../../shared/alert/alert.component'; import { hasValue } from '../../../shared/empty.util'; -import { LoadingComponent } from '../../../shared/loading/loading.component'; +import { ThemedLoadingComponent } from '../../../shared/loading/themed-loading.component'; import { PaginationComponent } from '../../../shared/pagination/pagination.component'; import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; import { NotificationsStateService } from '../../notifications-state.service'; @@ -38,7 +38,7 @@ import { NotificationsStateService } from '../../notifications-state.service'; templateUrl: './quality-assurance-source.component.html', styleUrls: ['./quality-assurance-source.component.scss'], standalone: true, - imports: [AlertComponent, NgIf, LoadingComponent, PaginationComponent, NgFor, RouterLink, AsyncPipe, TranslateModule, DatePipe], + imports: [AlertComponent, NgIf, ThemedLoadingComponent, PaginationComponent, NgFor, RouterLink, AsyncPipe, TranslateModule, DatePipe], }) export class QualityAssuranceSourceComponent implements OnInit { diff --git a/src/app/notifications/qa/topics/quality-assurance-topics.component.spec.ts b/src/app/notifications/qa/topics/quality-assurance-topics.component.spec.ts index a08e970a29..c764128e46 100644 --- a/src/app/notifications/qa/topics/quality-assurance-topics.component.spec.ts +++ b/src/app/notifications/qa/topics/quality-assurance-topics.component.spec.ts @@ -18,7 +18,7 @@ import { ItemDataService } from 'src/app/core/data/item-data.service'; import { PaginationService } from '../../../core/pagination/pagination.service'; import { AlertComponent } from '../../../shared/alert/alert.component'; -import { LoadingComponent } from '../../../shared/loading/loading.component'; +import { ThemedLoadingComponent } from '../../../shared/loading/themed-loading.component'; import { getMockNotificationsStateService, qualityAssuranceTopicObjectMoreAbstract, @@ -69,7 +69,7 @@ describe('QualityAssuranceTopicsComponent test suite', () => { remove: { imports: [ AlertComponent, - LoadingComponent, + ThemedLoadingComponent, PaginationComponent, ], }, diff --git a/src/app/notifications/qa/topics/quality-assurance-topics.component.ts b/src/app/notifications/qa/topics/quality-assurance-topics.component.ts index dbf2e47201..0fcaa93805 100644 --- a/src/app/notifications/qa/topics/quality-assurance-topics.component.ts +++ b/src/app/notifications/qa/topics/quality-assurance-topics.component.ts @@ -41,7 +41,7 @@ import { getItemPageRoute } from '../../../item-page/item-page-routing-paths'; import { QualityAssuranceTopicsPageParams } from '../../../quality-assurance-notifications-pages/quality-assurance-topics-page/quality-assurance-topics-page-resolver.service'; import { AlertComponent } from '../../../shared/alert/alert.component'; import { hasValue } from '../../../shared/empty.util'; -import { LoadingComponent } from '../../../shared/loading/loading.component'; +import { ThemedLoadingComponent } from '../../../shared/loading/themed-loading.component'; import { PaginationComponent } from '../../../shared/pagination/pagination.component'; import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; import { NotificationsStateService } from '../../notifications-state.service'; @@ -54,7 +54,7 @@ import { NotificationsStateService } from '../../notifications-state.service'; templateUrl: './quality-assurance-topics.component.html', styleUrls: ['./quality-assurance-topics.component.scss'], standalone: true, - imports: [AlertComponent, NgIf, LoadingComponent, PaginationComponent, NgFor, RouterLink, AsyncPipe, TranslateModule, DatePipe], + imports: [AlertComponent, NgIf, ThemedLoadingComponent, PaginationComponent, NgFor, RouterLink, AsyncPipe, TranslateModule, DatePipe], }) export class QualityAssuranceTopicsComponent implements OnInit, OnDestroy, AfterViewInit { /** diff --git a/src/app/notifications/suggestion-actions/suggestion-actions.component.ts b/src/app/notifications/suggestion-actions/suggestion-actions.component.ts index 666d22d57f..02eaf10321 100644 --- a/src/app/notifications/suggestion-actions/suggestion-actions.component.ts +++ b/src/app/notifications/suggestion-actions/suggestion-actions.component.ts @@ -15,7 +15,7 @@ import { take } from 'rxjs/operators'; import { Suggestion } from '../../core/notifications/models/suggestion.model'; import { Collection } from '../../core/shared/collection.model'; import { ItemType } from '../../core/shared/item-relationships/item-type.model'; -import { CreateItemParentSelectorComponent } from '../../shared/dso-selector/modal-wrappers/create-item-parent-selector/create-item-parent-selector.component'; +import { ThemedCreateItemParentSelectorComponent } from '../../shared/dso-selector/modal-wrappers/create-item-parent-selector/themed-create-item-parent-selector.component'; import { EntityDropdownComponent } from '../../shared/entity-dropdown/entity-dropdown.component'; import { SuggestionApproveAndImport } from '../suggestion-list-element/suggestion-approve-and-import'; @@ -69,7 +69,7 @@ export class SuggestionActionsComponent { */ openDialog(entity: ItemType) { - const modalRef = this.modalService.open(CreateItemParentSelectorComponent); + const modalRef = this.modalService.open(ThemedCreateItemParentSelectorComponent); modalRef.componentInstance.emitOnly = true; modalRef.componentInstance.entityType = entity.label; diff --git a/src/app/notifications/suggestion-targets/publication-claim/publication-claim.component.ts b/src/app/notifications/suggestion-targets/publication-claim/publication-claim.component.ts index 95788bf4ba..136466b4ac 100644 --- a/src/app/notifications/suggestion-targets/publication-claim/publication-claim.component.ts +++ b/src/app/notifications/suggestion-targets/publication-claim/publication-claim.component.ts @@ -25,7 +25,7 @@ import { import { SuggestionTarget } from '../../../core/notifications/models/suggestion-target.model'; import { PaginationService } from '../../../core/pagination/pagination.service'; import { hasValue } from '../../../shared/empty.util'; -import { LoadingComponent } from '../../../shared/loading/loading.component'; +import { ThemedLoadingComponent } from '../../../shared/loading/themed-loading.component'; import { PaginationComponent } from '../../../shared/pagination/pagination.component'; import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; import { getSuggestionPageRoute } from '../../../suggestions-page/suggestions-page-routing-paths'; @@ -40,7 +40,7 @@ import { SuggestionTargetsStateService } from '../suggestion-targets.state.servi templateUrl: './publication-claim.component.html', styleUrls: ['./publication-claim.component.scss'], imports: [ - LoadingComponent, + ThemedLoadingComponent, AsyncPipe, TranslateModule, PaginationComponent, diff --git a/src/app/page-error/page-error.component.ts b/src/app/page-error/page-error.component.ts index f53e4ed2b4..5a8f87ed59 100644 --- a/src/app/page-error/page-error.component.ts +++ b/src/app/page-error/page-error.component.ts @@ -9,7 +9,7 @@ import { TranslateModule } from '@ngx-translate/core'; * This component representing the `PageError` DSpace page. */ @Component({ - selector: 'ds-page-error', + selector: 'ds-base-page-error', styleUrls: ['./page-error.component.scss'], templateUrl: './page-error.component.html', changeDetection: ChangeDetectionStrategy.Default, diff --git a/src/app/page-error/themed-page-error.component.ts b/src/app/page-error/themed-page-error.component.ts index 2e0e94d898..a3ee38fd60 100644 --- a/src/app/page-error/themed-page-error.component.ts +++ b/src/app/page-error/themed-page-error.component.ts @@ -7,10 +7,11 @@ import { PageErrorComponent } from './page-error.component'; * Themed wrapper for PageErrorComponent */ @Component({ - selector: 'ds-themed-page-error', + selector: 'ds-page-error', styleUrls: [], templateUrl: '../shared/theme-support/themed.component.html', standalone: true, + imports: [PageErrorComponent], }) export class ThemedPageErrorComponent extends ThemedComponent { diff --git a/src/app/page-internal-server-error/page-internal-server-error.component.ts b/src/app/page-internal-server-error/page-internal-server-error.component.ts index 72d424fd20..c51757c4e1 100644 --- a/src/app/page-internal-server-error/page-internal-server-error.component.ts +++ b/src/app/page-internal-server-error/page-internal-server-error.component.ts @@ -10,7 +10,7 @@ import { ServerResponseService } from '../core/services/server-response.service' * This component representing the `PageInternalServer` DSpace page. */ @Component({ - selector: 'ds-page-internal-server-error', + selector: 'ds-base-page-internal-server-error', styleUrls: ['./page-internal-server-error.component.scss'], templateUrl: './page-internal-server-error.component.html', changeDetection: ChangeDetectionStrategy.Default, diff --git a/src/app/page-internal-server-error/themed-page-internal-server-error.component.ts b/src/app/page-internal-server-error/themed-page-internal-server-error.component.ts index 41be907e19..01146b2a88 100644 --- a/src/app/page-internal-server-error/themed-page-internal-server-error.component.ts +++ b/src/app/page-internal-server-error/themed-page-internal-server-error.component.ts @@ -7,10 +7,11 @@ import { PageInternalServerErrorComponent } from './page-internal-server-error.c * Themed wrapper for PageInternalServerErrorComponent */ @Component({ - selector: 'ds-themed-page-internal-server-error', + selector: 'ds-page-internal-server-error', styleUrls: [], templateUrl: '../shared/theme-support/themed.component.html', standalone: true, + imports: [PageInternalServerErrorComponent], }) export class ThemedPageInternalServerErrorComponent extends ThemedComponent { diff --git a/src/app/pagenotfound/pagenotfound.component.ts b/src/app/pagenotfound/pagenotfound.component.ts index e28f7992b2..8fd738c882 100644 --- a/src/app/pagenotfound/pagenotfound.component.ts +++ b/src/app/pagenotfound/pagenotfound.component.ts @@ -13,7 +13,7 @@ import { ServerResponseService } from '../core/services/server-response.service' * This component representing the `PageNotFound` DSpace page. */ @Component({ - selector: 'ds-pagenotfound', + selector: 'ds-base-pagenotfound', styleUrls: ['./pagenotfound.component.scss'], templateUrl: './pagenotfound.component.html', changeDetection: ChangeDetectionStrategy.Default, diff --git a/src/app/pagenotfound/themed-pagenotfound.component.ts b/src/app/pagenotfound/themed-pagenotfound.component.ts index 114ad27f81..d7308908ce 100644 --- a/src/app/pagenotfound/themed-pagenotfound.component.ts +++ b/src/app/pagenotfound/themed-pagenotfound.component.ts @@ -7,10 +7,11 @@ import { PageNotFoundComponent } from './pagenotfound.component'; * Themed wrapper for PageNotFoundComponent */ @Component({ - selector: 'ds-themed-pagenotfound', + selector: 'ds-pagenotfound', styleUrls: [], templateUrl: '../shared/theme-support/themed.component.html', standalone: true, + imports: [PageNotFoundComponent], }) export class ThemedPageNotFoundComponent extends ThemedComponent { diff --git a/src/app/process-page/detail/process-detail.component.html b/src/app/process-page/detail/process-detail.component.html index 521aec12c2..7558939ef3 100644 --- a/src/app/process-page/detail/process-detail.component.html +++ b/src/app/process-page/detail/process-detail.component.html @@ -23,10 +23,10 @@
- + {{getFileName(file)}} ({{(file?.sizeBytes) | dsFileSize }}) - +
@@ -51,8 +51,8 @@ class="btn btn-primary" (click)="showProcessOutputLogs()"> {{ 'process.detail.logs.button' | translate }} - +
{{ (outputLogs$ | async) }}

- + >; + script?: Observable>; /** * The output logs created by this Process * Will be undefined unless the output {@link HALLink} has been resolved. */ @link(PROCESS_OUTPUT_TYPE) - output?: Observable>; + output?: Observable>; /** * The files created by this Process * Will be undefined unless the output {@link HALLink} has been resolved. */ @link(BITSTREAM, true) - files?: Observable>>; + files?: Observable>>; /** * The filetypes present in this Process * Will be undefined unless the output {@link HALLink} has been resolved. */ @link(FILETYPES) - filetypes?: Observable>; + filetypes?: Observable>; } diff --git a/src/app/process-page/scripts/script.model.ts b/src/app/process-page/scripts/script.model.ts index 7907695b10..3cd934336a 100644 --- a/src/app/process-page/scripts/script.model.ts +++ b/src/app/process-page/scripts/script.model.ts @@ -23,37 +23,37 @@ export class Script implements CacheableObject { */ @excludeFromEquals @autoserialize - type: ResourceType; + type: ResourceType; /** * The identifier of this script */ @autoserialize - id: string; + id: string; /** * The name of this script */ @autoserialize - name: string; + name: string; /** * A short description of this script */ @autoserialize - description: string; + description: string; /** * The available parameters for this script */ @autoserialize - parameters: ScriptParameter[]; + parameters: ScriptParameter[]; /** * The {@link HALLink}s for this Script */ @deserialize - _links: { + _links: { self: HALLink, }; } diff --git a/src/app/profile-page/profile-page-security-form/profile-page-security-form.component.ts b/src/app/profile-page/profile-page-security-form/profile-page-security-form.component.ts index 929ff280c5..7c0bf9ea2d 100644 --- a/src/app/profile-page/profile-page-security-form/profile-page-security-form.component.ts +++ b/src/app/profile-page/profile-page-security-form/profile-page-security-form.component.ts @@ -86,13 +86,13 @@ export class ProfilePageSecurityFormComponent implements OnInit { * Indicates whether the "checkPasswordEmpty" needs to be added or not */ @Input() - passwordCanBeEmpty = true; + passwordCanBeEmpty = true; /** * Prefix for the form's label messages of this component */ @Input() - FORM_PREFIX: string; + FORM_PREFIX: string; private subs: Subscription[] = []; diff --git a/src/app/profile-page/profile-page.component.ts b/src/app/profile-page/profile-page.component.ts index e62faacc97..bee4c2d023 100644 --- a/src/app/profile-page/profile-page.component.ts +++ b/src/app/profile-page/profile-page.component.ts @@ -52,7 +52,7 @@ import { ProfilePageResearcherFormComponent } from './profile-page-researcher-fo import { ProfilePageSecurityFormComponent } from './profile-page-security-form/profile-page-security-form.component'; @Component({ - selector: 'ds-profile-page', + selector: 'ds-base-profile-page', styleUrls: ['./profile-page.component.scss'], templateUrl: './profile-page.component.html', imports: [ diff --git a/src/app/profile-page/themed-profile-page.component.ts b/src/app/profile-page/themed-profile-page.component.ts index 8288c7b8dc..83149ff9f1 100644 --- a/src/app/profile-page/themed-profile-page.component.ts +++ b/src/app/profile-page/themed-profile-page.component.ts @@ -7,10 +7,11 @@ import { ProfilePageComponent } from './profile-page.component'; * Themed wrapper for ProfilePageComponent */ @Component({ - selector: 'ds-themed-profile-page', + selector: 'ds-profile-page', styleUrls: [], templateUrl: './../shared/theme-support/themed.component.html', standalone: true, + imports: [ProfilePageComponent], }) export class ThemedProfilePageComponent extends ThemedComponent { protected getComponentName(): string { diff --git a/src/app/register-email-form/register-email-form.component.ts b/src/app/register-email-form/register-email-form.component.ts index 25916fd87e..ac13abb865 100644 --- a/src/app/register-email-form/register-email-form.component.ts +++ b/src/app/register-email-form/register-email-form.component.ts @@ -63,7 +63,7 @@ export const TYPE_REQUEST_FORGOT = 'forgot'; export const TYPE_REQUEST_REGISTER = 'register'; @Component({ - selector: 'ds-register-email-form', + selector: 'ds-base-register-email-form', templateUrl: './register-email-form.component.html', standalone: true, imports: [NgIf, FormsModule, ReactiveFormsModule, AlertComponent, GoogleRecaptchaComponent, AsyncPipe, TranslateModule], @@ -82,13 +82,13 @@ export class RegisterEmailFormComponent implements OnDestroy, OnInit { * The message prefix */ @Input() - MESSAGE_PREFIX: string; + MESSAGE_PREFIX: string; /** * Type of register request to be done, register new email or forgot password (same endpoint) */ @Input() - typeRequest: string = null; + typeRequest: string = null; public AlertTypeEnum = AlertType; diff --git a/src/app/register-email-form/themed-registry-email-form.component.ts b/src/app/register-email-form/themed-registry-email-form.component.ts index 679e0c9a0f..8f95d3d707 100644 --- a/src/app/register-email-form/themed-registry-email-form.component.ts +++ b/src/app/register-email-form/themed-registry-email-form.component.ts @@ -10,10 +10,11 @@ import { RegisterEmailFormComponent } from './register-email-form.component'; * Themed wrapper for {@link RegisterEmailFormComponent} */ @Component({ - selector: 'ds-themed-register-email-form', + selector: 'ds-register-email-form', styleUrls: [], templateUrl: '../shared/theme-support/themed.component.html', standalone: true, + imports: [RegisterEmailFormComponent], }) export class ThemedRegisterEmailFormComponent extends ThemedComponent { diff --git a/src/app/register-page/create-profile/create-profile.component.ts b/src/app/register-page/create-profile/create-profile.component.ts index 21e88edfcc..7e88c5a1c0 100644 --- a/src/app/register-page/create-profile/create-profile.component.ts +++ b/src/app/register-page/create-profile/create-profile.component.ts @@ -50,7 +50,7 @@ import { NotificationsService } from '../../shared/notifications/notifications.s * Component that renders the create profile page to be used by a user registering through a token */ @Component({ - selector: 'ds-create-profile', + selector: 'ds-base-create-profile', styleUrls: ['./create-profile.component.scss'], templateUrl: './create-profile.component.html', imports: [ diff --git a/src/app/register-page/create-profile/themed-create-profile.component.ts b/src/app/register-page/create-profile/themed-create-profile.component.ts index cf5d6d2def..54f12a9d16 100644 --- a/src/app/register-page/create-profile/themed-create-profile.component.ts +++ b/src/app/register-page/create-profile/themed-create-profile.component.ts @@ -7,10 +7,11 @@ import { CreateProfileComponent } from './create-profile.component'; * Themed wrapper for CreateProfileComponent */ @Component({ - selector: 'ds-themed-create-profile', + selector: 'ds-create-profile', styleUrls: [], templateUrl: './../../shared/theme-support/themed.component.html', standalone: true, + imports: [CreateProfileComponent], }) export class ThemedCreateProfileComponent extends ThemedComponent { protected getComponentName(): string { diff --git a/src/app/register-page/register-email/register-email.component.html b/src/app/register-page/register-email/register-email.component.html index 6a87a4e9e0..1829bb2914 100644 --- a/src/app/register-page/register-email/register-email.component.html +++ b/src/app/register-page/register-email/register-email.component.html @@ -1,3 +1,3 @@ - - + diff --git a/src/app/register-page/register-email/register-email.component.spec.ts b/src/app/register-page/register-email/register-email.component.spec.ts index 8696c2fed5..3694c6088a 100644 --- a/src/app/register-page/register-email/register-email.component.spec.ts +++ b/src/app/register-page/register-email/register-email.component.spec.ts @@ -8,7 +8,6 @@ import { import { ReactiveFormsModule } from '@angular/forms'; import { TranslateModule } from '@ngx-translate/core'; -import { RegisterEmailFormComponent } from '../../register-email-form/register-email-form.component'; import { ThemedRegisterEmailFormComponent } from '../../register-email-form/themed-registry-email-form.component'; import { RegisterEmailComponent } from './register-email.component'; @@ -24,7 +23,7 @@ describe('RegisterEmailComponent', () => { }) .overrideComponent(RegisterEmailComponent, { remove: { - imports: [RegisterEmailFormComponent, ThemedRegisterEmailFormComponent], + imports: [ThemedRegisterEmailFormComponent], }, }) .compileComponents(); diff --git a/src/app/register-page/register-email/register-email.component.ts b/src/app/register-page/register-email/register-email.component.ts index 6164cb30d2..130a2c8518 100644 --- a/src/app/register-page/register-email/register-email.component.ts +++ b/src/app/register-page/register-email/register-email.component.ts @@ -1,17 +1,14 @@ import { Component } from '@angular/core'; import { ThemedRegisterEmailFormComponent } from 'src/app/register-email-form/themed-registry-email-form.component'; -import { - RegisterEmailFormComponent, - TYPE_REQUEST_REGISTER, -} from '../../register-email-form/register-email-form.component'; +import { TYPE_REQUEST_REGISTER } from '../../register-email-form/register-email-form.component'; @Component({ - selector: 'ds-register-email', + selector: 'ds-base-register-email', styleUrls: ['./register-email.component.scss'], templateUrl: './register-email.component.html', imports: [ - RegisterEmailFormComponent, ThemedRegisterEmailFormComponent, + ThemedRegisterEmailFormComponent, ], standalone: true, }) diff --git a/src/app/register-page/register-email/themed-register-email.component.ts b/src/app/register-page/register-email/themed-register-email.component.ts index 987191d218..3f557f564c 100644 --- a/src/app/register-page/register-email/themed-register-email.component.ts +++ b/src/app/register-page/register-email/themed-register-email.component.ts @@ -7,10 +7,11 @@ import { RegisterEmailComponent } from './register-email.component'; * Themed wrapper for RegisterEmailComponent */ @Component({ - selector: 'ds-themed-register-email', + selector: 'ds-register-email', styleUrls: [], templateUrl: '../../shared/theme-support/themed.component.html', standalone: true, + imports: [RegisterEmailComponent], }) export class ThemedRegisterEmailComponent extends ThemedComponent { protected getComponentName(): string { diff --git a/src/app/request-copy/deny-request-copy/deny-request-copy.component.html b/src/app/request-copy/deny-request-copy/deny-request-copy.component.html index e513212f94..b00bc079dd 100644 --- a/src/app/request-copy/deny-request-copy/deny-request-copy.component.html +++ b/src/app/request-copy/deny-request-copy/deny-request-copy.component.html @@ -3,7 +3,7 @@

{{'deny-request-copy.intro' | translate}}

- +
- +
diff --git a/src/app/request-copy/deny-request-copy/deny-request-copy.component.ts b/src/app/request-copy/deny-request-copy/deny-request-copy.component.ts index e2cc5293cc..7d87755f9e 100644 --- a/src/app/request-copy/deny-request-copy/deny-request-copy.component.ts +++ b/src/app/request-copy/deny-request-copy/deny-request-copy.component.ts @@ -44,7 +44,7 @@ import { RequestCopyEmail } from '../email-request-copy/request-copy-email.model import { ThemedEmailRequestCopyComponent } from '../email-request-copy/themed-email-request-copy.component'; @Component({ - selector: 'ds-deny-request-copy', + selector: 'ds-base-deny-request-copy', styleUrls: ['./deny-request-copy.component.scss'], templateUrl: './deny-request-copy.component.html', standalone: true, diff --git a/src/app/request-copy/deny-request-copy/themed-deny-request-copy.component.ts b/src/app/request-copy/deny-request-copy/themed-deny-request-copy.component.ts index 45141d10dd..aebcdd2b06 100644 --- a/src/app/request-copy/deny-request-copy/themed-deny-request-copy.component.ts +++ b/src/app/request-copy/deny-request-copy/themed-deny-request-copy.component.ts @@ -7,10 +7,11 @@ import { DenyRequestCopyComponent } from './deny-request-copy.component'; * Themed wrapper for deny-request-copy.component */ @Component({ - selector: 'ds-themed-deny-request-copy', + selector: 'ds-deny-request-copy', styleUrls: [], templateUrl: './../../shared/theme-support/themed.component.html', standalone: true, + imports: [DenyRequestCopyComponent], }) export class ThemedDenyRequestCopyComponent extends ThemedComponent { protected getComponentName(): string { diff --git a/src/app/request-copy/email-request-copy/email-request-copy.component.ts b/src/app/request-copy/email-request-copy/email-request-copy.component.ts index 3f44a6ed1f..66a9e0b17f 100644 --- a/src/app/request-copy/email-request-copy/email-request-copy.component.ts +++ b/src/app/request-copy/email-request-copy/email-request-copy.component.ts @@ -16,7 +16,7 @@ import { RequestCopyEmail } from './request-copy-email.model'; @Component({ - selector: 'ds-email-request-copy', + selector: 'ds-base-email-request-copy', styleUrls: ['./email-request-copy.component.scss'], templateUrl: './email-request-copy.component.html', standalone: true, diff --git a/src/app/request-copy/email-request-copy/themed-email-request-copy.component.ts b/src/app/request-copy/email-request-copy/themed-email-request-copy.component.ts index 2512485f81..f641045ef2 100644 --- a/src/app/request-copy/email-request-copy/themed-email-request-copy.component.ts +++ b/src/app/request-copy/email-request-copy/themed-email-request-copy.component.ts @@ -13,10 +13,11 @@ import { RequestCopyEmail } from './request-copy-email.model'; * Themed wrapper for email-request-copy.component */ @Component({ - selector: 'ds-themed-email-request-copy', + selector: 'ds-email-request-copy', styleUrls: [], templateUrl: './../../shared/theme-support/themed.component.html', standalone: true, + imports: [EmailRequestCopyComponent], }) export class ThemedEmailRequestCopyComponent extends ThemedComponent { /** diff --git a/src/app/request-copy/grant-deny-request-copy/grant-deny-request-copy.component.html b/src/app/request-copy/grant-deny-request-copy/grant-deny-request-copy.component.html index 4837e0dcda..37b275d8f8 100644 --- a/src/app/request-copy/grant-deny-request-copy/grant-deny-request-copy.component.html +++ b/src/app/request-copy/grant-deny-request-copy/grant-deny-request-copy.component.html @@ -26,5 +26,5 @@

- +
diff --git a/src/app/request-copy/grant-request-copy/grant-request-copy.component.html b/src/app/request-copy/grant-request-copy/grant-request-copy.component.html index 179206566e..d2c2cfc3c8 100644 --- a/src/app/request-copy/grant-request-copy/grant-request-copy.component.html +++ b/src/app/request-copy/grant-request-copy/grant-request-copy.component.html @@ -3,7 +3,7 @@

{{'grant-request-copy.intro' | translate}}

- +

{{ 'grant-deny-request-copy.email.permissions.info' | translate }}

@@ -11,7 +11,7 @@
-
+
- +
diff --git a/src/app/request-copy/grant-request-copy/grant-request-copy.component.ts b/src/app/request-copy/grant-request-copy/grant-request-copy.component.ts index e2e2fe8843..b14f15f34b 100644 --- a/src/app/request-copy/grant-request-copy/grant-request-copy.component.ts +++ b/src/app/request-copy/grant-request-copy/grant-request-copy.component.ts @@ -37,7 +37,7 @@ import { RequestCopyEmail } from '../email-request-copy/request-copy-email.model import { ThemedEmailRequestCopyComponent } from '../email-request-copy/themed-email-request-copy.component'; @Component({ - selector: 'ds-grant-request-copy', + selector: 'ds-base-grant-request-copy', styleUrls: ['./grant-request-copy.component.scss'], templateUrl: './grant-request-copy.component.html', standalone: true, diff --git a/src/app/request-copy/grant-request-copy/themed-grant-request-copy.component.ts b/src/app/request-copy/grant-request-copy/themed-grant-request-copy.component.ts index 7c6f9f61a6..654f7588bb 100644 --- a/src/app/request-copy/grant-request-copy/themed-grant-request-copy.component.ts +++ b/src/app/request-copy/grant-request-copy/themed-grant-request-copy.component.ts @@ -7,10 +7,11 @@ import { GrantRequestCopyComponent } from './grant-request-copy.component'; * Themed wrapper for grant-request-copy.component */ @Component({ - selector: 'ds-themed-grant-request-copy', + selector: 'ds-grant-request-copy', styleUrls: [], templateUrl: './../../shared/theme-support/themed.component.html', standalone: true, + imports: [GrantRequestCopyComponent], }) export class ThemedGrantRequestCopyComponent extends ThemedComponent { diff --git a/src/app/root/root.component.html b/src/app/root/root.component.html index 533efea9d1..b5b753cdb9 100644 --- a/src/app/root/root.component.html +++ b/src/app/root/root.component.html @@ -2,26 +2,26 @@ {{ 'root.skip-to-content' | translate }} -
- +
- - + +
- +
- +
@@ -29,5 +29,5 @@
- +
diff --git a/src/app/root/root.component.ts b/src/app/root/root.component.ts index d98fd85b0a..e93a7b5c52 100644 --- a/src/app/root/root.component.ts +++ b/src/app/root/root.component.ts @@ -1,9 +1,11 @@ import { AsyncPipe, + NgClass, NgIf, } from '@angular/common'; import { Component, + Inject, Input, OnInit, } from '@angular/core'; @@ -13,6 +15,7 @@ import { } from '@angular/router'; import { TranslateModule } from '@ngx-translate/core'; import { + BehaviorSubject, combineLatest as combineLatestObservable, Observable, of, @@ -30,6 +33,10 @@ import { environment } from '../../environments/environment'; import { ThemedAdminSidebarComponent } from '../admin/admin-sidebar/themed-admin-sidebar.component'; import { getPageInternalServerErrorRoute } from '../app-routing-paths'; import { ThemedBreadcrumbsComponent } from '../breadcrumbs/themed-breadcrumbs.component'; +import { + NativeWindowRef, + NativeWindowService, +} from '../core/services/window.service'; import { ThemedFooterComponent } from '../footer/themed-footer.component'; import { ThemedHeaderNavbarWrapperComponent } from '../header-nav-wrapper/themed-header-navbar-wrapper.component'; import { slideSidebarPadding } from '../shared/animations/slide'; @@ -42,12 +49,25 @@ import { CSSVariableService } from '../shared/sass-helper/css-variable.service'; import { SystemWideAlertBannerComponent } from '../system-wide-alert/alert-banner/system-wide-alert-banner.component'; @Component({ - selector: 'ds-root', + selector: 'ds-base-root', templateUrl: './root.component.html', styleUrls: ['./root.component.scss'], animations: [slideSidebarPadding], standalone: true, - imports: [TranslateModule, ThemedAdminSidebarComponent, SystemWideAlertBannerComponent, ThemedHeaderNavbarWrapperComponent, ThemedBreadcrumbsComponent, NgIf, ThemedLoadingComponent, RouterOutlet, ThemedFooterComponent, NotificationsBoardComponent, AsyncPipe], + imports: [ + TranslateModule, + ThemedAdminSidebarComponent, + SystemWideAlertBannerComponent, + ThemedHeaderNavbarWrapperComponent, + ThemedBreadcrumbsComponent, + NgIf, + NgClass, + ThemedLoadingComponent, + RouterOutlet, + ThemedFooterComponent, + NotificationsBoardComponent, + AsyncPipe, + ], }) export class RootComponent implements OnInit { theme: Observable = of({} as any); @@ -58,6 +78,8 @@ export class RootComponent implements OnInit { notificationOptions: INotificationBoardOptions; models: any; + browserOsClasses = new BehaviorSubject([]); + /** * Whether or not to show a full screen loader */ @@ -73,11 +95,23 @@ export class RootComponent implements OnInit { private cssService: CSSVariableService, private menuService: MenuService, private windowService: HostWindowService, + @Inject(NativeWindowService) private _window: NativeWindowRef, ) { this.notificationOptions = environment.notifications; } ngOnInit() { + const browserName = this.getBrowserName(); + if (browserName) { + const browserOsClasses = new Array(); + browserOsClasses.push(`browser-${browserName}`); + const osName = this.getOSName(); + if (osName) { + browserOsClasses.push(`browser-${browserName}-${osName}`); + } + this.browserOsClasses.next(browserOsClasses); + } + this.isSidebarVisible$ = this.menuService.isMenuVisibleWithVisibleSections(MenuID.ADMIN); this.expandedSidebarWidth$ = this.cssService.getVariable('--ds-admin-sidebar-total-width').pipe( @@ -108,4 +142,23 @@ export class RootComponent implements OnInit { mainContent.focus(); } } + + getBrowserName(): string { + const userAgent = this._window.nativeWindow.navigator.userAgent; + if (/Firefox/.test(userAgent)) { + return 'firefox'; + } + if (/Safari/.test(userAgent)) { + return 'safari'; + } + return undefined; + } + + getOSName(): string { + const userAgent = this._window.nativeWindow.navigator.userAgent; + if (/Windows/.test(userAgent)) { + return 'windows'; + } + return undefined; + } } diff --git a/src/app/root/themed-root.component.ts b/src/app/root/themed-root.component.ts index b2ea91c75d..fe88efa546 100644 --- a/src/app/root/themed-root.component.ts +++ b/src/app/root/themed-root.component.ts @@ -7,10 +7,11 @@ import { ThemedComponent } from '../shared/theme-support/themed.component'; import { RootComponent } from './root.component'; @Component({ - selector: 'ds-themed-root', + selector: 'ds-root', styleUrls: [], templateUrl: '../shared/theme-support/themed.component.html', standalone: true, + imports: [RootComponent], }) export class ThemedRootComponent extends ThemedComponent { /** diff --git a/src/app/search-navbar/search-navbar.component.ts b/src/app/search-navbar/search-navbar.component.ts index 006a217d57..28971a2644 100644 --- a/src/app/search-navbar/search-navbar.component.ts +++ b/src/app/search-navbar/search-navbar.component.ts @@ -20,7 +20,7 @@ import { ClickOutsideDirective } from '../shared/utils/click-outside.directive'; * The search box in the header that expands on focus and collapses on focus out */ @Component({ - selector: 'ds-search-navbar', + selector: 'ds-base-search-navbar', templateUrl: './search-navbar.component.html', styleUrls: ['./search-navbar.component.scss'], animations: [expandSearchInput], diff --git a/src/app/search-navbar/themed-search-navbar.component.ts b/src/app/search-navbar/themed-search-navbar.component.ts index 240b39314b..82348f7943 100644 --- a/src/app/search-navbar/themed-search-navbar.component.ts +++ b/src/app/search-navbar/themed-search-navbar.component.ts @@ -4,10 +4,11 @@ import { ThemedComponent } from '../shared/theme-support/themed.component'; import { SearchNavbarComponent } from './search-navbar.component'; @Component({ - selector: 'ds-themed-search-navbar', + selector: 'ds-search-navbar', styleUrls: [], templateUrl: '../shared/theme-support/themed.component.html', standalone: true, + imports: [SearchNavbarComponent], }) export class ThemedSearchNavbarComponent extends ThemedComponent { diff --git a/src/app/search-page/configuration-search-page.component.spec.ts b/src/app/search-page/configuration-search-page.component.spec.ts index 417e659899..f3931a2016 100644 --- a/src/app/search-page/configuration-search-page.component.spec.ts +++ b/src/app/search-page/configuration-search-page.component.spec.ts @@ -8,23 +8,23 @@ import { waitForAsync, } from '@angular/core/testing'; import { Router } from '@angular/router'; +import { of } from 'rxjs'; import { RouteService } from '../core/services/route.service'; import { SearchConfigurationService } from '../core/shared/search/search-configuration.service'; import { configureSearchComponentTestingModule } from '../shared/search/search.component.spec'; import { ConfigurationSearchPageComponent } from './configuration-search-page.component'; import createSpy = jasmine.createSpy; -import { of } from 'rxjs'; const CONFIGURATION = 'test-configuration'; const QUERY = 'test query'; @Component({ template: ` - - + `, imports: [ ConfigurationSearchPageComponent, diff --git a/src/app/search-page/configuration-search-page.component.ts b/src/app/search-page/configuration-search-page.component.ts index 3b28ed447a..afbe34d550 100644 --- a/src/app/search-page/configuration-search-page.component.ts +++ b/src/app/search-page/configuration-search-page.component.ts @@ -34,7 +34,7 @@ import { ViewModeSwitchComponent } from '../shared/view-mode-switch/view-mode-sw * This component renders a search page using a configuration as input. */ @Component({ - selector: 'ds-configuration-search-page', + selector: 'ds-base-configuration-search-page', styleUrls: ['../shared/search/search.component.scss'], templateUrl: '../shared/search/search.component.html', changeDetection: ChangeDetectionStrategy.OnPush, diff --git a/src/app/search-page/search-page.component.html b/src/app/search-page/search-page.component.html index 36ba53a885..8d31060783 100644 --- a/src/app/search-page/search-page.component.html +++ b/src/app/search-page/search-page.component.html @@ -1 +1 @@ - + diff --git a/src/app/search-page/search-page.component.ts b/src/app/search-page/search-page.component.ts index 272987a795..817cc6818e 100644 --- a/src/app/search-page/search-page.component.ts +++ b/src/app/search-page/search-page.component.ts @@ -5,7 +5,7 @@ import { SEARCH_CONFIG_SERVICE } from '../my-dspace-page/my-dspace-configuration import { ThemedSearchComponent } from '../shared/search/themed-search.component'; @Component({ - selector: 'ds-search-page', + selector: 'ds-base-search-page', templateUrl: './search-page.component.html', providers: [ { diff --git a/src/app/search-page/themed-configuration-search-page.component.ts b/src/app/search-page/themed-configuration-search-page.component.ts index 0d39dca430..cf84c9c9d4 100644 --- a/src/app/search-page/themed-configuration-search-page.component.ts +++ b/src/app/search-page/themed-configuration-search-page.component.ts @@ -15,58 +15,134 @@ import { ConfigurationSearchPageComponent } from './configuration-search-page.co * Themed wrapper for ConfigurationSearchPageComponent */ @Component({ - selector: 'ds-themed-configuration-search-page', + selector: 'ds-configuration-search-page', templateUrl: '../shared/theme-support/themed.component.html', standalone: true, + imports: [ConfigurationSearchPageComponent], }) export class ThemedConfigurationSearchPageComponent extends ThemedComponent { + /** + * The list of available configuration options + */ + @Input() configurationList: SearchConfigurationOption[]; - @Input() configurationList: SearchConfigurationOption[] = []; - + /** + * The current context + * If empty, 'search' is used + */ @Input() context: Context; + /** + * The configuration to use for the search options + * If empty, 'default' is used + */ @Input() configuration: string; + /** + * The actual query for the fixed filter. + * If empty, the query will be determined by the route parameter called 'filter' + */ @Input() fixedFilterQuery: string; + /** + * If this is true, the request will only be sent if there's + * no valid cached version. Defaults to true + */ @Input() useCachedVersionIfAvailable: boolean; + /** + * True when the search component should show results on the current page + */ @Input() inPlaceSearch: boolean; + /** + * The link type of the listed search results + */ @Input() linkType: CollectionElementLinkType; + /** + * The pagination id used in the search + */ @Input() paginationId: string; + /** + * Whether or not the search bar should be visible + */ @Input() searchEnabled: boolean; + /** + * The width of the sidebar (bootstrap columns) + */ @Input() sideBarWidth: number; + /** + * The placeholder of the search form input + */ @Input() searchFormPlaceholder: string; + /** + * A boolean representing if result entries are selectable + */ @Input() selectable: boolean; + /** + * The config option used for selection functionality + */ @Input() selectionConfig: SelectionConfig; + /** + * A boolean representing if show csv export button + */ @Input() showCsvExport: boolean; + /** + * A boolean representing if show search sidebar button + */ @Input() showSidebar: boolean; + /** + * Whether to show the thumbnail preview + */ @Input() showThumbnails: boolean; + /** + * Whether to show the view mode switch + */ @Input() showViewModes: boolean; + /** + * List of available view mode + */ @Input() useUniquePageId: boolean; + /** + * List of available view mode + */ @Input() viewModeList: ViewMode[]; + /** + * Defines whether or not to show the scope selector + */ @Input() showScopeSelector: boolean; + /** + * Whether or not to track search statistics by sending updates to the rest api + */ @Input() trackStatistics: boolean; + /** + * The default value for the search query when none is already defined in the {@link SearchConfigurationService} + */ @Input() query: string; + /** + * The fallback scope when no scope is defined in the url, if this is also undefined no scope will be set + */ @Input() scope: string; + /** + * Hides the scope in the url, this can be useful when you hardcode the scope in another way + */ @Input() hideScopeInUrl: boolean; protected inAndOutputNames: (keyof ConfigurationSearchPageComponent & keyof this)[] = [ diff --git a/src/app/search-page/themed-search-page.component.ts b/src/app/search-page/themed-search-page.component.ts index 30e6e6db11..649ad8a246 100644 --- a/src/app/search-page/themed-search-page.component.ts +++ b/src/app/search-page/themed-search-page.component.ts @@ -7,10 +7,11 @@ import { SearchPageComponent } from './search-page.component'; * Themed wrapper for SearchPageComponent */ @Component({ - selector: 'ds-themed-search-page', + selector: 'ds-search-page', styleUrls: [], templateUrl: '../shared/theme-support/themed.component.html', standalone: true, + imports: [SearchPageComponent], }) export class ThemedSearchPageComponent extends ThemedComponent { diff --git a/src/app/shared/auth-nav-menu/auth-nav-menu.component.html b/src/app/shared/auth-nav-menu/auth-nav-menu.component.html index 98749da7b5..74ae821634 100644 --- a/src/app/shared/auth-nav-menu/auth-nav-menu.component.html +++ b/src/app/shared/auth-nav-menu/auth-nav-menu.component.html @@ -13,8 +13,8 @@
@@ -30,7 +30,7 @@ ngbDropdownToggle>
- +
diff --git a/src/app/shared/auth-nav-menu/auth-nav-menu.component.spec.ts b/src/app/shared/auth-nav-menu/auth-nav-menu.component.spec.ts index a969b6f49b..539de03b09 100644 --- a/src/app/shared/auth-nav-menu/auth-nav-menu.component.spec.ts +++ b/src/app/shared/auth-nav-menu/auth-nav-menu.component.spec.ts @@ -267,7 +267,7 @@ describe('AuthNavMenuComponent', () => { component = null; }); it('should render UserMenuComponent component', () => { - const logoutDropdownMenu = deNavMenuItem.query(By.css('ds-themed-user-menu')); + const logoutDropdownMenu = deNavMenuItem.query(By.css('ds-user-menu')); expect(logoutDropdownMenu.nativeElement).toBeDefined(); }); }); diff --git a/src/app/shared/auth-nav-menu/auth-nav-menu.component.ts b/src/app/shared/auth-nav-menu/auth-nav-menu.component.ts index d24b1db853..16899cf73e 100644 --- a/src/app/shared/auth-nav-menu/auth-nav-menu.component.ts +++ b/src/app/shared/auth-nav-menu/auth-nav-menu.component.ts @@ -48,19 +48,17 @@ import { } from '../animations/fade'; import { isNotUndefined } from '../empty.util'; import { HostWindowService } from '../host-window.service'; -import { LogInComponent } from '../log-in/log-in.component'; import { ThemedLogInComponent } from '../log-in/themed-log-in.component'; import { BrowserOnlyPipe } from '../utils/browser-only.pipe'; import { ThemedUserMenuComponent } from './user-menu/themed-user-menu.component'; -import { UserMenuComponent } from './user-menu/user-menu.component'; @Component({ - selector: 'ds-auth-nav-menu', + selector: 'ds-base-auth-nav-menu', templateUrl: './auth-nav-menu.component.html', styleUrls: ['./auth-nav-menu.component.scss'], animations: [fadeInOut, fadeOut], standalone: true, - imports: [NgClass, NgIf, NgbDropdownModule, LogInComponent, ThemedLogInComponent, RouterLink, RouterLinkActive, UserMenuComponent, ThemedUserMenuComponent, AsyncPipe, TranslateModule, BrowserOnlyPipe], + imports: [NgClass, NgIf, NgbDropdownModule, ThemedLogInComponent, RouterLink, RouterLinkActive, ThemedUserMenuComponent, AsyncPipe, TranslateModule, BrowserOnlyPipe], }) export class AuthNavMenuComponent implements OnInit { /** diff --git a/src/app/shared/auth-nav-menu/themed-auth-nav-menu.component.ts b/src/app/shared/auth-nav-menu/themed-auth-nav-menu.component.ts index d15a10d9ce..9ed244a929 100644 --- a/src/app/shared/auth-nav-menu/themed-auth-nav-menu.component.ts +++ b/src/app/shared/auth-nav-menu/themed-auth-nav-menu.component.ts @@ -7,10 +7,11 @@ import { AuthNavMenuComponent } from './auth-nav-menu.component'; * Themed wrapper for {@link AuthNavMenuComponent} */ @Component({ - selector: 'ds-themed-auth-nav-menu', + selector: 'ds-auth-nav-menu', styleUrls: [], templateUrl: '../theme-support/themed.component.html', standalone: true, + imports: [AuthNavMenuComponent], }) export class ThemedAuthNavMenuComponent extends ThemedComponent { protected getComponentName(): string { diff --git a/src/app/shared/auth-nav-menu/user-menu/themed-user-menu.component.ts b/src/app/shared/auth-nav-menu/user-menu/themed-user-menu.component.ts index feb3f90b35..38112cfa7c 100644 --- a/src/app/shared/auth-nav-menu/user-menu/themed-user-menu.component.ts +++ b/src/app/shared/auth-nav-menu/user-menu/themed-user-menu.component.ts @@ -10,10 +10,11 @@ import { UserMenuComponent } from './user-menu.component'; * This component represents the user nav menu. */ @Component({ - selector: 'ds-themed-user-menu', + selector: 'ds-user-menu', templateUrl: './../../theme-support/themed.component.html', styleUrls: [], standalone: true, + imports: [UserMenuComponent], }) export class ThemedUserMenuComponent extends ThemedComponent{ diff --git a/src/app/shared/auth-nav-menu/user-menu/user-menu.component.html b/src/app/shared/auth-nav-menu/user-menu/user-menu.component.html index a6d4188f92..4c36cc2f24 100644 --- a/src/app/shared/auth-nav-menu/user-menu/user-menu.component.html +++ b/src/app/shared/auth-nav-menu/user-menu/user-menu.component.html @@ -1,4 +1,4 @@ - + diff --git a/src/app/shared/collection-dropdown/collection-dropdown.component.ts b/src/app/shared/collection-dropdown/collection-dropdown.component.ts index d330c0b6ff..0fef74b389 100644 --- a/src/app/shared/collection-dropdown/collection-dropdown.component.ts +++ b/src/app/shared/collection-dropdown/collection-dropdown.component.ts @@ -72,7 +72,7 @@ export interface CollectionListEntry { } @Component({ - selector: 'ds-collection-dropdown', + selector: 'ds-base-collection-dropdown', templateUrl: './collection-dropdown.component.html', styleUrls: ['./collection-dropdown.component.scss'], standalone: true, diff --git a/src/app/shared/collection-dropdown/themed-collection-dropdown.component.ts b/src/app/shared/collection-dropdown/themed-collection-dropdown.component.ts index 2f7055d4e7..643cd1f711 100644 --- a/src/app/shared/collection-dropdown/themed-collection-dropdown.component.ts +++ b/src/app/shared/collection-dropdown/themed-collection-dropdown.component.ts @@ -12,10 +12,11 @@ import { } from './collection-dropdown.component'; @Component({ - selector: 'ds-themed-collection-dropdown', + selector: 'ds-collection-dropdown', styleUrls: [], templateUrl: '../../shared/theme-support/themed.component.html', standalone: true, + imports: [CollectionDropdownComponent], }) export class ThemedCollectionDropdownComponent extends ThemedComponent { diff --git a/src/app/shared/comcol/comcol-forms/create-comcol-page/create-comcol-page.component.ts b/src/app/shared/comcol/comcol-forms/create-comcol-page/create-comcol-page.component.ts index 398ffeb6ba..d4c311bd1b 100644 --- a/src/app/shared/comcol/comcol-forms/create-comcol-page/create-comcol-page.component.ts +++ b/src/app/shared/comcol/comcol-forms/create-comcol-page/create-comcol-page.component.ts @@ -4,7 +4,10 @@ import { } from '@angular/core'; import { Router } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; -import { Observable } from 'rxjs'; +import { + BehaviorSubject, + Observable, +} from 'rxjs'; import { mergeMap, take, @@ -62,6 +65,11 @@ export class CreateComColPageComponent i */ protected type: ResourceType; + /** + * The + */ + isLoading$: BehaviorSubject = new BehaviorSubject(false); + public constructor( protected dsoDataService: ComColDataService, public dsoNameService: DSONameService, @@ -89,6 +97,7 @@ export class CreateComColPageComponent i * @param event The event returned by the community/collection form. Contains the new dso and logo uploader */ onSubmit(event) { + this.isLoading$.next(true); const dso = event.dso; const uploader = event.uploader; @@ -101,6 +110,7 @@ export class CreateComColPageComponent i ); })) .subscribe((dsoRD: TDomain) => { + this.isLoading$.next(false); if (isNotUndefined(dsoRD)) { this.newUUID = dsoRD.uuid; if (uploader.queue.length > 0) { diff --git a/src/app/shared/comcol/comcol-forms/edit-comcol-page/comcol-role/comcol-role.component.html b/src/app/shared/comcol/comcol-forms/edit-comcol-page/comcol-role/comcol-role.component.html index fa9dd5da09..2639a4c099 100644 --- a/src/app/shared/comcol/comcol-forms/edit-comcol-page/comcol-role/comcol-role.component.html +++ b/src/app/shared/comcol/comcol-forms/edit-comcol-page/comcol-role/comcol-role.component.html @@ -13,7 +13,7 @@
- +
{{'comcol-role.edit.no-group' | translate}}
diff --git a/src/app/shared/comcol/comcol-forms/edit-comcol-page/comcol-role/comcol-role.component.ts b/src/app/shared/comcol/comcol-forms/edit-comcol-page/comcol-role/comcol-role.component.ts index 8fa1133178..22c4ed49f3 100644 --- a/src/app/shared/comcol/comcol-forms/edit-comcol-page/comcol-role/comcol-role.component.ts +++ b/src/app/shared/comcol/comcol-forms/edit-comcol-page/comcol-role/comcol-role.component.ts @@ -71,7 +71,7 @@ export class ComcolRoleComponent implements OnInit { * The community or collection to manage. */ @Input() - dso: Community | Collection; + dso: Community | Collection; /** * The role to manage diff --git a/src/app/shared/comcol/comcol-page-browse-by/comcol-page-browse-by.component.ts b/src/app/shared/comcol/comcol-page-browse-by/comcol-page-browse-by.component.ts index bcfb04a443..a7730ee7dd 100644 --- a/src/app/shared/comcol/comcol-page-browse-by/comcol-page-browse-by.component.ts +++ b/src/app/shared/comcol/comcol-page-browse-by/comcol-page-browse-by.component.ts @@ -54,7 +54,7 @@ export interface ComColPageNavOption { * It expects the ID of the Community or Collection as input to be passed on as a scope */ @Component({ - selector: 'ds-comcol-page-browse-by', + selector: 'ds-base-comcol-page-browse-by', styleUrls: ['./comcol-page-browse-by.component.scss'], templateUrl: './comcol-page-browse-by.component.html', imports: [ diff --git a/src/app/shared/comcol/comcol-page-browse-by/themed-comcol-page-browse-by.component.ts b/src/app/shared/comcol/comcol-page-browse-by/themed-comcol-page-browse-by.component.ts index 2133bbe312..148e0466b4 100644 --- a/src/app/shared/comcol/comcol-page-browse-by/themed-comcol-page-browse-by.component.ts +++ b/src/app/shared/comcol/comcol-page-browse-by/themed-comcol-page-browse-by.component.ts @@ -10,10 +10,11 @@ import { ComcolPageBrowseByComponent } from './comcol-page-browse-by.component'; * Themed wrapper for ComcolPageBrowseByComponent */ @Component({ - selector: 'ds-themed-comcol-page-browse-by', + selector: 'ds-comcol-page-browse-by', styleUrls: [], templateUrl: '../../theme-support/themed.component.html', standalone: true, + imports: [ComcolPageBrowseByComponent], }) export class ThemedComcolPageBrowseByComponent extends ThemedComponent { /** diff --git a/src/app/shared/comcol/comcol-page-handle/comcol-page-handle.component.ts b/src/app/shared/comcol/comcol-page-handle/comcol-page-handle.component.ts index 4487c20982..e7023fd28b 100644 --- a/src/app/shared/comcol/comcol-page-handle/comcol-page-handle.component.ts +++ b/src/app/shared/comcol/comcol-page-handle/comcol-page-handle.component.ts @@ -11,7 +11,7 @@ import { TranslateModule } from '@ngx-translate/core'; */ @Component({ - selector: 'ds-comcol-page-handle', + selector: 'ds-base-comcol-page-handle', styleUrls: ['./comcol-page-handle.component.scss'], templateUrl: './comcol-page-handle.component.html', imports: [NgIf, TranslateModule], diff --git a/src/app/shared/comcol/comcol-page-handle/themed-comcol-page-handle.component.ts b/src/app/shared/comcol/comcol-page-handle/themed-comcol-page-handle.component.ts index 590a7136bd..98f137f934 100644 --- a/src/app/shared/comcol/comcol-page-handle/themed-comcol-page-handle.component.ts +++ b/src/app/shared/comcol/comcol-page-handle/themed-comcol-page-handle.component.ts @@ -10,10 +10,11 @@ import { ComcolPageHandleComponent } from './comcol-page-handle.component'; * Themed wrapper for BreadcrumbsComponent */ @Component({ - selector: 'ds-themed-comcol-page-handle', + selector: 'ds-comcol-page-handle', styleUrls: [], templateUrl: '../../theme-support/themed.component.html', standalone: true, + imports: [ComcolPageHandleComponent], }) diff --git a/src/app/shared/comcol/sections/comcol-search-section/comcol-search-section.component.html b/src/app/shared/comcol/sections/comcol-search-section/comcol-search-section.component.html index 7c97dabf43..efaef0a5b7 100644 --- a/src/app/shared/comcol/sections/comcol-search-section/comcol-search-section.component.html +++ b/src/app/shared/comcol/sections/comcol-search-section/comcol-search-section.component.html @@ -1,7 +1,7 @@ - - + diff --git a/src/app/shared/confirmation-modal/confirmation-modal.component.ts b/src/app/shared/confirmation-modal/confirmation-modal.component.ts index aedbf55a3a..7618706ab4 100644 --- a/src/app/shared/confirmation-modal/confirmation-modal.component.ts +++ b/src/app/shared/confirmation-modal/confirmation-modal.component.ts @@ -31,7 +31,7 @@ export class ConfirmationModalComponent { * An event fired when the cancel or confirm button is clicked, with respectively false or true */ @Output() - response = new EventEmitter(); + response = new EventEmitter(); constructor( protected activeModal: NgbActiveModal, diff --git a/src/app/shared/correction-suggestion/item-withdrawn-reinstate-modal.component.ts b/src/app/shared/correction-suggestion/item-withdrawn-reinstate-modal.component.ts index ab8cb689e8..decd7c8d38 100644 --- a/src/app/shared/correction-suggestion/item-withdrawn-reinstate-modal.component.ts +++ b/src/app/shared/correction-suggestion/item-withdrawn-reinstate-modal.component.ts @@ -14,7 +14,7 @@ import { BehaviorSubject } from 'rxjs'; import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; import { ModalBeforeDismiss } from '../interfaces/modal-before-dismiss.interface'; -import { LoadingComponent } from '../loading/loading.component'; +import { ThemedLoadingComponent } from '../loading/themed-loading.component'; @Component({ selector: 'ds-item-withdrawn-reinstate-modal', @@ -23,7 +23,7 @@ import { LoadingComponent } from '../loading/loading.component'; imports: [ NgIf, TranslateModule, - LoadingComponent, + ThemedLoadingComponent, FormsModule, AsyncPipe, ], diff --git a/src/app/shared/ds-select/ds-select.component.ts b/src/app/shared/ds-select/ds-select.component.ts index d018ffa29e..d82378d4e5 100644 --- a/src/app/shared/ds-select/ds-select.component.ts +++ b/src/app/shared/ds-select/ds-select.component.ts @@ -24,23 +24,23 @@ export class DsSelectComponent { * An optional label for the dropdown selector. */ @Input() - label: string; + label: string; /** * Whether the dropdown selector is disabled. */ @Input() - disabled: boolean; + disabled: boolean; /** * Emits an event when the dropdown selector is opened or closed. */ @Output() - toggled = new EventEmitter(); + toggled = new EventEmitter(); /** * Emits an event when the dropdown selector or closed. */ @Output() - close = new EventEmitter(); + close = new EventEmitter(); } diff --git a/src/app/shared/dso-selector/dso-selector/dso-selector.component.html b/src/app/shared/dso-selector/dso-selector/dso-selector.component.html index 71625066fa..c30bc389cc 100644 --- a/src/app/shared/dso-selector/dso-selector/dso-selector.component.html +++ b/src/app/shared/dso-selector/dso-selector/dso-selector.component.html @@ -32,7 +32,7 @@
diff --git a/src/app/shared/dso-selector/modal-wrappers/create-collection-parent-selector/create-collection-parent-selector.component.ts b/src/app/shared/dso-selector/modal-wrappers/create-collection-parent-selector/create-collection-parent-selector.component.ts index 5c55232318..05efb987a3 100644 --- a/src/app/shared/dso-selector/modal-wrappers/create-collection-parent-selector/create-collection-parent-selector.component.ts +++ b/src/app/shared/dso-selector/modal-wrappers/create-collection-parent-selector/create-collection-parent-selector.component.ts @@ -33,7 +33,7 @@ import { */ @Component({ - selector: 'ds-create-collection-parent-selector', + selector: 'ds-base-create-collection-parent-selector', templateUrl: '../dso-selector-modal-wrapper.component.html', standalone: true, imports: [NgIf, DSOSelectorComponent, TranslateModule], diff --git a/src/app/shared/dso-selector/modal-wrappers/create-collection-parent-selector/themed-create-collection-parent-selector.component.ts b/src/app/shared/dso-selector/modal-wrappers/create-collection-parent-selector/themed-create-collection-parent-selector.component.ts index 5ecd017f5c..d521d981ca 100644 --- a/src/app/shared/dso-selector/modal-wrappers/create-collection-parent-selector/themed-create-collection-parent-selector.component.ts +++ b/src/app/shared/dso-selector/modal-wrappers/create-collection-parent-selector/themed-create-collection-parent-selector.component.ts @@ -7,10 +7,11 @@ import { CreateCollectionParentSelectorComponent } from './create-collection-par * Themed wrapper for CreateCollectionParentSelectorComponent */ @Component({ - selector: 'ds-themed-create-collection-parent-selector', + selector: 'ds-create-collection-parent-selector', styleUrls: [], templateUrl: '../../../theme-support/themed.component.html', standalone: true, + imports: [CreateCollectionParentSelectorComponent], }) export class ThemedCreateCollectionParentSelectorComponent extends ThemedComponent { diff --git a/src/app/shared/dso-selector/modal-wrappers/create-community-parent-selector/create-community-parent-selector.component.ts b/src/app/shared/dso-selector/modal-wrappers/create-community-parent-selector/create-community-parent-selector.component.ts index 27b09b749c..6b1e51dbf5 100644 --- a/src/app/shared/dso-selector/modal-wrappers/create-community-parent-selector/create-community-parent-selector.component.ts +++ b/src/app/shared/dso-selector/modal-wrappers/create-community-parent-selector/create-community-parent-selector.component.ts @@ -36,7 +36,7 @@ import { */ @Component({ - selector: 'ds-create-community-parent-selector', + selector: 'ds-base-create-community-parent-selector', styleUrls: ['./create-community-parent-selector.component.scss'], templateUrl: './create-community-parent-selector.component.html', standalone: true, diff --git a/src/app/shared/dso-selector/modal-wrappers/create-community-parent-selector/themed-create-community-parent-selector.component.ts b/src/app/shared/dso-selector/modal-wrappers/create-community-parent-selector/themed-create-community-parent-selector.component.ts index 9568cc261d..e9f6609966 100644 --- a/src/app/shared/dso-selector/modal-wrappers/create-community-parent-selector/themed-create-community-parent-selector.component.ts +++ b/src/app/shared/dso-selector/modal-wrappers/create-community-parent-selector/themed-create-community-parent-selector.component.ts @@ -7,10 +7,11 @@ import { CreateCommunityParentSelectorComponent } from './create-community-paren * Themed wrapper for CreateCommunityParentSelectorComponent */ @Component({ - selector: 'ds-themed-create-community-parent-selector', + selector: 'ds-create-community-parent-selector', styleUrls: [], templateUrl: '../../../theme-support/themed.component.html', standalone: true, + imports: [CreateCommunityParentSelectorComponent], }) export class ThemedCreateCommunityParentSelectorComponent extends ThemedComponent { diff --git a/src/app/shared/dso-selector/modal-wrappers/create-item-parent-selector/create-item-parent-selector.component.ts b/src/app/shared/dso-selector/modal-wrappers/create-item-parent-selector/create-item-parent-selector.component.ts index 4c649ba85b..ede3cc3609 100644 --- a/src/app/shared/dso-selector/modal-wrappers/create-item-parent-selector/create-item-parent-selector.component.ts +++ b/src/app/shared/dso-selector/modal-wrappers/create-item-parent-selector/create-item-parent-selector.component.ts @@ -31,7 +31,7 @@ import { */ @Component({ - selector: 'ds-create-item-parent-selector', + selector: 'ds-base-create-item-parent-selector', // styleUrls: ['./create-item-parent-selector.component.scss'], // templateUrl: '../dso-selector-modal-wrapper.component.html', templateUrl: './create-item-parent-selector.component.html', diff --git a/src/app/shared/dso-selector/modal-wrappers/create-item-parent-selector/themed-create-item-parent-selector.component.ts b/src/app/shared/dso-selector/modal-wrappers/create-item-parent-selector/themed-create-item-parent-selector.component.ts index 68bf918c45..f6cd552629 100644 --- a/src/app/shared/dso-selector/modal-wrappers/create-item-parent-selector/themed-create-item-parent-selector.component.ts +++ b/src/app/shared/dso-selector/modal-wrappers/create-item-parent-selector/themed-create-item-parent-selector.component.ts @@ -10,10 +10,11 @@ import { CreateItemParentSelectorComponent } from './create-item-parent-selector * Themed wrapper for CreateItemParentSelectorComponent */ @Component({ - selector: 'ds-themed-create-item-parent-selector', + selector: 'ds-create-item-parent-selector', styleUrls: [], templateUrl: '../../../theme-support/themed.component.html', standalone: true, + imports: [CreateItemParentSelectorComponent], }) export class ThemedCreateItemParentSelectorComponent extends ThemedComponent { diff --git a/src/app/shared/dso-selector/modal-wrappers/edit-collection-selector/edit-collection-selector.component.ts b/src/app/shared/dso-selector/modal-wrappers/edit-collection-selector/edit-collection-selector.component.ts index 65424e643d..611a4f13de 100644 --- a/src/app/shared/dso-selector/modal-wrappers/edit-collection-selector/edit-collection-selector.component.ts +++ b/src/app/shared/dso-selector/modal-wrappers/edit-collection-selector/edit-collection-selector.component.ts @@ -30,7 +30,7 @@ import { */ @Component({ - selector: 'ds-edit-collection-selector', + selector: 'ds-base-edit-collection-selector', templateUrl: '../dso-selector-modal-wrapper.component.html', standalone: true, imports: [NgIf, DSOSelectorComponent, TranslateModule], diff --git a/src/app/shared/dso-selector/modal-wrappers/edit-collection-selector/themed-edit-collection-selector.component.ts b/src/app/shared/dso-selector/modal-wrappers/edit-collection-selector/themed-edit-collection-selector.component.ts index 834b4a5f0b..28604c18b6 100644 --- a/src/app/shared/dso-selector/modal-wrappers/edit-collection-selector/themed-edit-collection-selector.component.ts +++ b/src/app/shared/dso-selector/modal-wrappers/edit-collection-selector/themed-edit-collection-selector.component.ts @@ -7,10 +7,11 @@ import { EditCollectionSelectorComponent } from './edit-collection-selector.comp * Themed wrapper for EditCollectionSelectorComponent */ @Component({ - selector: 'ds-themed-edit-collection-selector', + selector: 'ds-edit-collection-selector', styleUrls: [], templateUrl: '../../../theme-support/themed.component.html', standalone: true, + imports: [EditCollectionSelectorComponent], }) export class ThemedEditCollectionSelectorComponent extends ThemedComponent { diff --git a/src/app/shared/dso-selector/modal-wrappers/edit-community-selector/edit-community-selector.component.ts b/src/app/shared/dso-selector/modal-wrappers/edit-community-selector/edit-community-selector.component.ts index 6f781cb9eb..3f7ede0de0 100644 --- a/src/app/shared/dso-selector/modal-wrappers/edit-community-selector/edit-community-selector.component.ts +++ b/src/app/shared/dso-selector/modal-wrappers/edit-community-selector/edit-community-selector.component.ts @@ -30,7 +30,7 @@ import { */ @Component({ - selector: 'ds-edit-community-selector', + selector: 'ds-base-edit-community-selector', templateUrl: '../dso-selector-modal-wrapper.component.html', standalone: true, imports: [NgIf, DSOSelectorComponent, TranslateModule], diff --git a/src/app/shared/dso-selector/modal-wrappers/edit-community-selector/themed-edit-community-selector.component.ts b/src/app/shared/dso-selector/modal-wrappers/edit-community-selector/themed-edit-community-selector.component.ts index 27798879ff..0e33746edc 100644 --- a/src/app/shared/dso-selector/modal-wrappers/edit-community-selector/themed-edit-community-selector.component.ts +++ b/src/app/shared/dso-selector/modal-wrappers/edit-community-selector/themed-edit-community-selector.component.ts @@ -7,10 +7,11 @@ import { EditCommunitySelectorComponent } from './edit-community-selector.compon * Themed wrapper for EditCommunitySelectorComponent */ @Component({ - selector: 'ds-themed-edit-community-selector', + selector: 'ds-edit-community-selector', styleUrls: [], templateUrl: '../../../theme-support/themed.component.html', standalone: true, + imports: [EditCommunitySelectorComponent], }) export class ThemedEditCommunitySelectorComponent extends ThemedComponent { diff --git a/src/app/shared/dso-selector/modal-wrappers/edit-item-selector/edit-item-selector.component.ts b/src/app/shared/dso-selector/modal-wrappers/edit-item-selector/edit-item-selector.component.ts index ebcc936a3e..903f432734 100644 --- a/src/app/shared/dso-selector/modal-wrappers/edit-item-selector/edit-item-selector.component.ts +++ b/src/app/shared/dso-selector/modal-wrappers/edit-item-selector/edit-item-selector.component.ts @@ -26,7 +26,7 @@ import { */ @Component({ - selector: 'ds-edit-item-selector', + selector: 'ds-base-edit-item-selector', templateUrl: 'edit-item-selector.component.html', standalone: true, imports: [NgIf, DSOSelectorComponent, TranslateModule], diff --git a/src/app/shared/dso-selector/modal-wrappers/edit-item-selector/themed-edit-item-selector.component.ts b/src/app/shared/dso-selector/modal-wrappers/edit-item-selector/themed-edit-item-selector.component.ts index e7b0a456d2..fbb6850a02 100644 --- a/src/app/shared/dso-selector/modal-wrappers/edit-item-selector/themed-edit-item-selector.component.ts +++ b/src/app/shared/dso-selector/modal-wrappers/edit-item-selector/themed-edit-item-selector.component.ts @@ -7,10 +7,11 @@ import { EditItemSelectorComponent } from './edit-item-selector.component'; * Themed wrapper for EditItemSelectorComponent */ @Component({ - selector: 'ds-themed-edit-item-selector', + selector: 'ds-edit-item-selector', styleUrls: [], templateUrl: '../../../theme-support/themed.component.html', standalone: true, + imports: [EditItemSelectorComponent], }) export class ThemedEditItemSelectorComponent extends ThemedComponent { diff --git a/src/app/shared/dso-selector/modal-wrappers/import-batch-selector/import-batch-selector.component.ts b/src/app/shared/dso-selector/modal-wrappers/import-batch-selector/import-batch-selector.component.ts index 599b9977ef..2b3785c0f9 100644 --- a/src/app/shared/dso-selector/modal-wrappers/import-batch-selector/import-batch-selector.component.ts +++ b/src/app/shared/dso-selector/modal-wrappers/import-batch-selector/import-batch-selector.component.ts @@ -40,7 +40,7 @@ export class ImportBatchSelectorComponent extends DSOSelectorModalWrapperCompone * An event fired when the modal is closed */ @Output() - response = new EventEmitter(); + response = new EventEmitter(); constructor(protected activeModal: NgbActiveModal, protected route: ActivatedRoute) { diff --git a/src/app/shared/entity-dropdown/entity-dropdown.component.html b/src/app/shared/entity-dropdown/entity-dropdown.component.html index 374c589e3e..049f5c81be 100644 --- a/src/app/shared/entity-dropdown/entity-dropdown.component.html +++ b/src/app/shared/entity-dropdown/entity-dropdown.component.html @@ -23,8 +23,8 @@
  • diff --git a/src/app/shared/file-download-link/file-download-link.component.ts b/src/app/shared/file-download-link/file-download-link.component.ts index 991f5ba864..9b01cd799d 100644 --- a/src/app/shared/file-download-link/file-download-link.component.ts +++ b/src/app/shared/file-download-link/file-download-link.component.ts @@ -32,7 +32,7 @@ import { } from '../empty.util'; @Component({ - selector: 'ds-file-download-link', + selector: 'ds-base-file-download-link', templateUrl: './file-download-link.component.html', styleUrls: ['./file-download-link.component.scss'], standalone: true, diff --git a/src/app/shared/file-download-link/themed-file-download-link.component.ts b/src/app/shared/file-download-link/themed-file-download-link.component.ts index e4085b06f9..c0fc4597f5 100644 --- a/src/app/shared/file-download-link/themed-file-download-link.component.ts +++ b/src/app/shared/file-download-link/themed-file-download-link.component.ts @@ -9,10 +9,11 @@ import { ThemedComponent } from '../theme-support/themed.component'; import { FileDownloadLinkComponent } from './file-download-link.component'; @Component({ - selector: 'ds-themed-file-download-link', + selector: 'ds-file-download-link', styleUrls: [], templateUrl: '../theme-support/themed.component.html', standalone: true, + imports: [FileDownloadLinkComponent], }) export class ThemedFileDownloadLinkComponent extends ThemedComponent { diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts index 1ef9a9e350..c26df24fea 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts @@ -155,7 +155,7 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo @Input() formModel: DynamicFormControlModel[]; @Input() asBootstrapFormGroup = false; @Input() bindId = true; - @Input() context: any | null = null; + @Input() context: any = null; @Input() group: UntypedFormGroup; @Input() hostClass: string[]; @Input() hasErrorMessaging = false; diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component.html b/src/app/shared/form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component.html index 001e0bc339..944e4650ab 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component.html +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component.html @@ -1,7 +1,7 @@
    - + diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/existing-relation-list-element/existing-relation-list-element.component.html b/src/app/shared/form/builder/ds-dynamic-form-ui/existing-relation-list-element/existing-relation-list-element.component.html index 5a5bd2ff2a..08efff4219 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/existing-relation-list-element/existing-relation-list-element.component.html +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/existing-relation-list-element/existing-relation-list-element.component.html @@ -1,7 +1,7 @@
    - + diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.ts index 4589171e36..62a6b8044f 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.ts @@ -96,6 +96,8 @@ export class DsDatePickerComponent extends DynamicFormControlComponent implement this.initialDay = now.getUTCDate(); if (this.model && this.model.value !== null) { + // todo: model value could object or Date according to its type annotation + // eslint-disable-next-line @typescript-eslint/no-base-to-string const values = this.model.value.toString().split(DS_DATE_PICKER_SEPARATOR); if (values.length > 0) { this.initialYear = parseInt(values[0], 10); diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list-radio-group.model.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list-radio-group.model.ts index d6ab989cd5..af5f65802d 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list-radio-group.model.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list-radio-group.model.ts @@ -1,5 +1,6 @@ import { DynamicFormControlLayout, + DynamicFormControlRelation, DynamicRadioGroupModel, DynamicRadioGroupModelConfig, serializable, @@ -16,12 +17,14 @@ export interface DynamicListModelConfig extends DynamicRadioGroupModelConfig { @serializable() vocabularyOptions: VocabularyOptions; @serializable() repeatable: boolean; + @serializable() typeBindRelations: DynamicFormControlRelation[]; @serializable() groupLength: number; @serializable() required: boolean; @serializable() hint: string; @@ -36,6 +39,7 @@ export class DynamicListRadioGroupModel extends DynamicRadioGroupModel { this.required = config.required; this.hint = config.hint; this.value = config.value; + this.typeBindRelations = config.typeBindRelations ? config.typeBindRelations : []; } get hasAuthority(): boolean { diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.component.html b/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.component.html index e661c458b6..38f15a5ed1 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.component.html +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.component.html @@ -57,7 +57,7 @@
    - +
    +
    - - +

    {{ 'submission.sections.describe.relationship-lookup.selection-tab.title.' + externalSource.id | translate}}

    @@ -21,8 +21,8 @@ [importConfig]="importConfig" (importObject)="import($event)"> - +
    diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.spec.ts index d7090b55c8..79831fe8ca 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.spec.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.spec.ts @@ -174,7 +174,7 @@ describe('DsDynamicLookupRelationExternalSourceTabComponent', () => { }); it('should display a ds-themed-loading component', () => { - const loading = fixture.debugElement.query(By.css('ds-themed-loading')); + const loading = fixture.debugElement.query(By.css('ds-loading')); expect(loading).not.toBeNull(); }); }); diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.ts index 484abce7d6..5a1f13544f 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.ts @@ -63,7 +63,7 @@ import { ExternalSourceEntryImportModalComponent } from './external-source-entry import { ThemedExternalSourceEntryImportModalComponent } from './external-source-entry-import-modal/themed-external-source-entry-import-modal.component'; @Component({ - selector: 'ds-dynamic-lookup-relation-external-source-tab', + selector: 'ds-base-dynamic-lookup-relation-external-source-tab', styleUrls: ['./dynamic-lookup-relation-external-source-tab.component.scss'], templateUrl: './dynamic-lookup-relation-external-source-tab.component.html', providers: [ diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/external-source-entry-import-modal.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/external-source-entry-import-modal.component.spec.ts index c0090f36a3..e2a4bbf4bb 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/external-source-entry-import-modal.component.spec.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/external-source-entry-import-modal.component.spec.ts @@ -21,7 +21,7 @@ import { NotificationsService } from '../../../../../../notifications/notificati import { ItemSearchResult } from '../../../../../../object-collection/shared/item-search-result.model'; import { SelectableListService } from '../../../../../../object-list/selectable-list/selectable-list.service'; import { createSuccessfulRemoteDataObject$ } from '../../../../../../remote-data.utils'; -import { SearchResultsComponent } from '../../../../../../search/search-results/search-results.component'; +import { ThemedSearchResultsComponent } from '../../../../../../search/search-results/themed-search-results.component'; import { createPaginatedList } from '../../../../../../testing/utils.test'; import { RelationshipOptions } from '../../../../models/relationship-options.model'; import { @@ -97,7 +97,7 @@ describe('DsDynamicLookupRelationExternalSourceTabComponent', () => { schemas: [NO_ERRORS_SCHEMA], }) .overrideComponent(ExternalSourceEntryImportModalComponent, { - remove: { imports: [SearchResultsComponent] }, + remove: { imports: [ThemedSearchResultsComponent] }, }) .compileComponents(); })); diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/external-source-entry-import-modal.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/external-source-entry-import-modal.component.ts index d9581dcd85..c2516ca9a8 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/external-source-entry-import-modal.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/external-source-entry-import-modal.component.ts @@ -46,7 +46,7 @@ import { SelectableListService } from '../../../../../../object-list/selectable- import { PaginationComponentOptions } from '../../../../../../pagination/pagination-component-options.model'; import { PaginatedSearchOptions } from '../../../../../../search/models/paginated-search-options.model'; import { SearchResult } from '../../../../../../search/models/search-result.model'; -import { SearchResultsComponent } from '../../../../../../search/search-results/search-results.component'; +import { ThemedSearchResultsComponent } from '../../../../../../search/search-results/themed-search-results.component'; import { RelationshipOptions } from '../../../../models/relationship-options.model'; /** @@ -61,12 +61,12 @@ export enum ImportType { } @Component({ - selector: 'ds-external-source-entry-import-modal', + selector: 'ds-base-external-source-entry-import-modal', styleUrls: ['./external-source-entry-import-modal.component.scss'], templateUrl: './external-source-entry-import-modal.component.html', imports: [ TranslateModule, - SearchResultsComponent, + ThemedSearchResultsComponent, NgIf, AsyncPipe, ], diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/themed-external-source-entry-import-modal.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/themed-external-source-entry-import-modal.component.ts index f9d2137f9b..35dba9ca54 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/themed-external-source-entry-import-modal.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/themed-external-source-entry-import-modal.component.ts @@ -4,10 +4,11 @@ import { ThemedComponent } from '../../../../../../theme-support/themed.componen import { ExternalSourceEntryImportModalComponent } from './external-source-entry-import-modal.component'; @Component({ - selector: 'ds-themed-external-source-entry-import-modal', + selector: 'ds-external-source-entry-import-modal', styleUrls: [], templateUrl: '../../../../../../../shared/theme-support/themed.component.html', standalone: true, + imports: [ExternalSourceEntryImportModalComponent], }) export class ThemedExternalSourceEntryImportModalComponent extends ThemedComponent { protected getComponentName(): string { diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/themed-dynamic-lookup-relation-external-source-tab.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/themed-dynamic-lookup-relation-external-source-tab.component.ts index 0673bd41af..9e4df4f5f8 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/themed-dynamic-lookup-relation-external-source-tab.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/themed-dynamic-lookup-relation-external-source-tab.component.ts @@ -15,10 +15,11 @@ import { RelationshipOptions } from '../../../models/relationship-options.model' import { DsDynamicLookupRelationExternalSourceTabComponent } from './dynamic-lookup-relation-external-source-tab.component'; @Component({ - selector: 'ds-themed-dynamic-lookup-relation-external-source-tab', + selector: 'ds-dynamic-lookup-relation-external-source-tab', styleUrls: [], templateUrl: '../../../../../theme-support/themed.component.html', standalone: true, + imports: [DsDynamicLookupRelationExternalSourceTabComponent], }) export class ThemedDynamicLookupRelationExternalSourceTabComponent extends ThemedComponent { protected inAndOutputNames: (keyof DsDynamicLookupRelationExternalSourceTabComponent & keyof this)[] = ['label', 'listId', diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.html b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.html index 12bc93ab0e..cfd2763b92 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.html +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.html @@ -1,4 +1,4 @@ -
    - + diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.ts index 8bf1c9c8b4..06b4946e12 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.ts @@ -53,7 +53,7 @@ import { RelationshipOptions } from '../../../models/relationship-options.model' @Component({ - selector: 'ds-dynamic-lookup-relation-search-tab', + selector: 'ds-base-dynamic-lookup-relation-search-tab', styleUrls: ['./dynamic-lookup-relation-search-tab.component.scss'], templateUrl: './dynamic-lookup-relation-search-tab.component.html', providers: [ diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/themed-dynamic-lookup-relation-search-tab.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/themed-dynamic-lookup-relation-search-tab.component.ts index cc82967080..71d55e6494 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/themed-dynamic-lookup-relation-search-tab.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/themed-dynamic-lookup-relation-search-tab.component.ts @@ -18,10 +18,11 @@ import { RelationshipOptions } from '../../../models/relationship-options.model' import { DsDynamicLookupRelationSearchTabComponent } from './dynamic-lookup-relation-search-tab.component'; @Component({ - selector: 'ds-themed-dynamic-lookup-relation-search-tab', + selector: 'ds-dynamic-lookup-relation-search-tab', styleUrls: [], templateUrl: '../../../../../theme-support/themed.component.html', standalone: true, + imports: [DsDynamicLookupRelationSearchTabComponent], }) export class ThemedDynamicLookupRelationSearchTabComponent extends ThemedComponent { protected inAndOutputNames: (keyof DsDynamicLookupRelationSearchTabComponent & keyof this)[] = ['relationship', 'listId', diff --git a/src/app/shared/form/builder/form-builder.service.spec.ts b/src/app/shared/form/builder/form-builder.service.spec.ts index 6cf3f709ab..1e48139191 100644 --- a/src/app/shared/form/builder/form-builder.service.spec.ts +++ b/src/app/shared/form/builder/form-builder.service.spec.ts @@ -31,12 +31,14 @@ import { DynamicTextAreaModel, DynamicTimePickerModel, } from '@ng-dynamic-forms/core'; +import { TranslateService } from '@ngx-translate/core'; import { FormRowModel } from '../../../core/config/models/config-submission-form.model'; import { SubmissionFormsModel } from '../../../core/config/models/config-submission-forms.model'; import { ConfigurationDataService } from '../../../core/data/configuration-data.service'; import { ConfigurationProperty } from '../../../core/shared/configuration-property.model'; import { VocabularyOptions } from '../../../core/submission/vocabularies/models/vocabulary-options.model'; +import { getMockTranslateService } from '../../mocks/translate.service.mock'; import { createSuccessfulRemoteDataObject$ } from '../../remote-data.utils'; import { DynamicDsDatePickerModel } from './ds-dynamic-form-ui/models/date-picker/date-picker.model'; import { DynamicConcatModel } from './ds-dynamic-form-ui/models/ds-dynamic-concat.model'; @@ -85,6 +87,7 @@ describe('FormBuilderService test suite', () => { beforeEach(() => { configSpy = createConfigSuccessSpy(typeFieldTestValue); + let translateService = getMockTranslateService(); TestBed.configureTestingModule({ imports: [ReactiveFormsModule], providers: [ @@ -93,6 +96,7 @@ describe('FormBuilderService test suite', () => { { provide: NG_VALIDATORS, useValue: testValidator, multi: true }, { provide: NG_ASYNC_VALIDATORS, useValue: testAsyncValidator, multi: true }, { provide: ConfigurationDataService, useValue: configSpy }, + { provide: TranslateService, useValue: translateService }, ], }); diff --git a/src/app/shared/form/builder/models/form-field.model.ts b/src/app/shared/form/builder/models/form-field.model.ts index f81c955fab..a89801007f 100644 --- a/src/app/shared/form/builder/models/form-field.model.ts +++ b/src/app/shared/form/builder/models/form-field.model.ts @@ -39,43 +39,43 @@ export class FormFieldModel { * The hints for this metadata field to display on form */ @autoserialize - hints: string; + hints: string; /** * The label for this metadata field to display on form */ @autoserialize - label: string; + label: string; /** * The languages available for this metadata field to display on form */ @autoserialize - languageCodes: LanguageCode[]; + languageCodes: LanguageCode[]; /** * The error message for this metadata field to display on form in case of field is required */ @autoserialize - mandatoryMessage: string; + mandatoryMessage: string; /** * Representing if this metadata field is mandatory or not */ @autoserialize - mandatory: string; + mandatory: string; /** * Representing if this metadata field is repeatable or not */ @autoserialize - repeatable: boolean; + repeatable: boolean; /** * Containing additional properties for this metadata field */ @autoserialize - input: { + input: { /** * Representing the type for this metadata field */ @@ -91,41 +91,41 @@ export class FormFieldModel { * Representing additional vocabulary configuration for this metadata field */ @autoserialize - selectableMetadata: SelectableMetadata[]; + selectableMetadata: SelectableMetadata[]; /** * Representing additional relationship configuration for this metadata field */ @autoserialize - selectableRelationship: RelationshipOptions; + selectableRelationship: RelationshipOptions; @autoserialize - rows: FormRowModel[]; + rows: FormRowModel[]; /** * Representing the scope for this metadata field */ @autoserialize - scope: string; + scope: string; /** * Containing additional css classes for this metadata field to use on form */ @autoserialize - style: string; + style: string; /** * Containing types to bind for this field */ @autoserialize - typeBind: string[]; + typeBind: string[]; /** * Containing the value for this metadata field */ @autoserialize - value: any; + value: any; @autoserialize - visibility: SectionVisibility; + visibility: SectionVisibility; } diff --git a/src/app/shared/form/builder/parsers/concat-field-parser.ts b/src/app/shared/form/builder/parsers/concat-field-parser.ts index 3b29d0eb48..40711c188d 100644 --- a/src/app/shared/form/builder/parsers/concat-field-parser.ts +++ b/src/app/shared/form/builder/parsers/concat-field-parser.ts @@ -1,4 +1,5 @@ import { Inject } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; import { hasNoValue, @@ -34,17 +35,18 @@ export class ConcatFieldParser extends FieldParser { @Inject(CONFIG_DATA) configData: FormFieldModel, @Inject(INIT_FORM_VALUES) initFormValues, @Inject(PARSER_OPTIONS) parserOptions: ParserOptions, + translate: TranslateService, protected separator: string, protected firstPlaceholder: string = null, protected secondPlaceholder: string = null) { - super(submissionId, configData, initFormValues, parserOptions); + super(submissionId, configData, initFormValues, parserOptions, translate); this.separator = separator; this.firstPlaceholder = firstPlaceholder; this.secondPlaceholder = secondPlaceholder; } - public modelFactory(fieldValue?: FormFieldMetadataValueObject | any, label?: boolean): any { + public modelFactory(fieldValue?: FormFieldMetadataValueObject, label?: boolean): any { const id: string = this.configData.selectableMetadata[0].metadata; const clsInput = { diff --git a/src/app/shared/form/builder/parsers/date-field-parser.spec.ts b/src/app/shared/form/builder/parsers/date-field-parser.spec.ts index d1912a7cad..ec2172523f 100644 --- a/src/app/shared/form/builder/parsers/date-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/date-field-parser.spec.ts @@ -1,12 +1,17 @@ +import { getMockTranslateService } from 'src/app/shared/mocks/translate.service.mock'; + import { DynamicDsDatePickerModel } from '../ds-dynamic-form-ui/models/date-picker/date-picker.model'; import { FormFieldModel } from '../models/form-field.model'; import { FormFieldMetadataValueObject } from '../models/form-field-metadata-value.model'; import { DateFieldParser } from './date-field-parser'; import { ParserOptions } from './parser-options'; + + describe('DateFieldParser test suite', () => { let field: FormFieldModel; let initFormValues: any = {}; + let translateService = getMockTranslateService(); const submissionId = '1234'; const parserOptions: ParserOptions = { @@ -37,13 +42,13 @@ describe('DateFieldParser test suite', () => { }); it('should init parser properly', () => { - const parser = new DateFieldParser(submissionId, field, initFormValues, parserOptions); + const parser = new DateFieldParser(submissionId, field, initFormValues, parserOptions, translateService); expect(parser instanceof DateFieldParser).toBe(true); }); it('should return a DynamicDsDatePickerModel object when repeatable option is false', () => { - const parser = new DateFieldParser(submissionId, field, initFormValues, parserOptions); + const parser = new DateFieldParser(submissionId, field, initFormValues, parserOptions, translateService); const fieldModel = parser.parse(); @@ -56,7 +61,7 @@ describe('DateFieldParser test suite', () => { }; const expectedValue = '1983-11-18'; - const parser = new DateFieldParser(submissionId, field, initFormValues, parserOptions); + const parser = new DateFieldParser(submissionId, field, initFormValues, parserOptions, translateService); const fieldModel = parser.parse(); diff --git a/src/app/shared/form/builder/parsers/date-field-parser.ts b/src/app/shared/form/builder/parsers/date-field-parser.ts index ea9f193e33..8104568b1a 100644 --- a/src/app/shared/form/builder/parsers/date-field-parser.ts +++ b/src/app/shared/form/builder/parsers/date-field-parser.ts @@ -9,7 +9,7 @@ import { FieldParser } from './field-parser'; export class DateFieldParser extends FieldParser { - public modelFactory(fieldValue?: FormFieldMetadataValueObject | any, label?: boolean): any { + public modelFactory(fieldValue?: FormFieldMetadataValueObject, label?: boolean): any { let malformedDate = false; const inputDateModelConfig: DynamicDsDateControlModelConfig = this.initModel(null, false, true); inputDateModelConfig.legend = this.configData.label; @@ -18,6 +18,8 @@ export class DateFieldParser extends FieldParser { this.setValues(inputDateModelConfig as any, fieldValue); // Init Data and validity check if (isNotEmpty(inputDateModelConfig.value)) { + // todo: model value could be object or Date according to its type annotation + // eslint-disable-next-line @typescript-eslint/no-base-to-string const value = inputDateModelConfig.value.toString(); if (value.length >= 4) { const valuesArray = value.split(DS_DATE_PICKER_SEPARATOR); diff --git a/src/app/shared/form/builder/parsers/disabled-field-parser.spec.ts b/src/app/shared/form/builder/parsers/disabled-field-parser.spec.ts index fd6908ed3b..759f357f28 100644 --- a/src/app/shared/form/builder/parsers/disabled-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/disabled-field-parser.spec.ts @@ -1,3 +1,5 @@ +import { getMockTranslateService } from 'src/app/shared/mocks/translate.service.mock'; + import { DynamicDisabledModel } from '../ds-dynamic-form-ui/models/disabled/dynamic-disabled.model'; import { FormFieldModel } from '../models/form-field.model'; import { DisabledFieldParser } from './disabled-field-parser'; @@ -6,6 +8,7 @@ import { ParserOptions } from './parser-options'; describe('DisabledFieldParser test suite', () => { let field: FormFieldModel; let initFormValues: any = {}; + let translateService = getMockTranslateService(); const submissionId = '1234'; const parserOptions: ParserOptions = { @@ -35,13 +38,13 @@ describe('DisabledFieldParser test suite', () => { }); it('should init parser properly', () => { - const parser = new DisabledFieldParser(submissionId, field, initFormValues, parserOptions); + const parser = new DisabledFieldParser(submissionId, field, initFormValues, parserOptions, translateService); expect(parser instanceof DisabledFieldParser).toBe(true); }); it('should return a DynamicDisabledModel object when repeatable option is false', () => { - const parser = new DisabledFieldParser(submissionId, field, initFormValues, parserOptions); + const parser = new DisabledFieldParser(submissionId, field, initFormValues, parserOptions, translateService); const fieldModel = parser.parse(); @@ -56,7 +59,7 @@ describe('DisabledFieldParser test suite', () => { }; const expectedValue = 'test description'; - const parser = new DisabledFieldParser(submissionId, field, initFormValues, parserOptions); + const parser = new DisabledFieldParser(submissionId, field, initFormValues, parserOptions, translateService); const fieldModel = parser.parse(); expect(fieldModel.value.value).toEqual(expectedValue); diff --git a/src/app/shared/form/builder/parsers/disabled-field-parser.ts b/src/app/shared/form/builder/parsers/disabled-field-parser.ts index e9b97ba7d9..ac977de40a 100644 --- a/src/app/shared/form/builder/parsers/disabled-field-parser.ts +++ b/src/app/shared/form/builder/parsers/disabled-field-parser.ts @@ -10,7 +10,7 @@ import { FieldParser } from './field-parser'; */ export class DisabledFieldParser extends FieldParser { - public modelFactory(fieldValue?: FormFieldMetadataValueObject | any, label?: boolean): any { + public modelFactory(fieldValue?: FormFieldMetadataValueObject, label?: boolean): any { const emptyModelConfig: DsDynamicDisabledModelConfig = this.initModel(null, label); this.setValues(emptyModelConfig, fieldValue, true); return new DynamicDisabledModel(emptyModelConfig); diff --git a/src/app/shared/form/builder/parsers/dropdown-field-parser.spec.ts b/src/app/shared/form/builder/parsers/dropdown-field-parser.spec.ts index 0ab376d1aa..c0c3daa304 100644 --- a/src/app/shared/form/builder/parsers/dropdown-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/dropdown-field-parser.spec.ts @@ -1,3 +1,5 @@ +import { getMockTranslateService } from 'src/app/shared/mocks/translate.service.mock'; + import { DynamicScrollableDropdownModel } from '../ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.model'; import { FormFieldModel } from '../models/form-field.model'; import { DropdownFieldParser } from './dropdown-field-parser'; @@ -5,6 +7,7 @@ import { ParserOptions } from './parser-options'; describe('DropdownFieldParser test suite', () => { let field: FormFieldModel; + let translateService = getMockTranslateService(); const submissionId = '1234'; const initFormValues = {}; @@ -37,13 +40,13 @@ describe('DropdownFieldParser test suite', () => { }); it('should init parser properly', () => { - const parser = new DropdownFieldParser(submissionId, field, initFormValues, parserOptions); + const parser = new DropdownFieldParser(submissionId, field, initFormValues, parserOptions, translateService); expect(parser instanceof DropdownFieldParser).toBe(true); }); it('should return a DynamicScrollableDropdownModel object when repeatable option is false', () => { - const parser = new DropdownFieldParser(submissionId, field, initFormValues, parserOptions); + const parser = new DropdownFieldParser(submissionId, field, initFormValues, parserOptions, translateService); const fieldModel = parser.parse(); @@ -52,7 +55,7 @@ describe('DropdownFieldParser test suite', () => { it('should throw when authority is not passed', () => { field.selectableMetadata[0].controlledVocabulary = null; - const parser = new DropdownFieldParser(submissionId, field, initFormValues, parserOptions); + const parser = new DropdownFieldParser(submissionId, field, initFormValues, parserOptions, translateService); expect(() => parser.parse()) .toThrow(); diff --git a/src/app/shared/form/builder/parsers/dropdown-field-parser.ts b/src/app/shared/form/builder/parsers/dropdown-field-parser.ts index 1fc28d1274..5a21f794f8 100644 --- a/src/app/shared/form/builder/parsers/dropdown-field-parser.ts +++ b/src/app/shared/form/builder/parsers/dropdown-field-parser.ts @@ -1,5 +1,6 @@ import { Inject } from '@angular/core'; import { DynamicFormControlLayout } from '@ng-dynamic-forms/core'; +import { TranslateService } from '@ngx-translate/core'; import { isNotEmpty } from '../../../empty.util'; import { @@ -24,11 +25,12 @@ export class DropdownFieldParser extends FieldParser { @Inject(CONFIG_DATA) configData: FormFieldModel, @Inject(INIT_FORM_VALUES) initFormValues, @Inject(PARSER_OPTIONS) parserOptions: ParserOptions, + translate: TranslateService, ) { - super(submissionId, configData, initFormValues, parserOptions); + super(submissionId, configData, initFormValues, parserOptions, translate); } - public modelFactory(fieldValue?: FormFieldMetadataValueObject | any, label?: boolean): any { + public modelFactory(fieldValue?: FormFieldMetadataValueObject, label?: boolean): any { const dropdownModelConfig: DynamicScrollableDropdownModelConfig = this.initModel(null, label); let layout: DynamicFormControlLayout; diff --git a/src/app/shared/form/builder/parsers/field-parser.ts b/src/app/shared/form/builder/parsers/field-parser.ts index c77b244d6a..590d5f564e 100644 --- a/src/app/shared/form/builder/parsers/field-parser.ts +++ b/src/app/shared/form/builder/parsers/field-parser.ts @@ -8,6 +8,7 @@ import { MATCH_VISIBLE, OR_OPERATOR, } from '@ng-dynamic-forms/core'; +import { TranslateService } from '@ngx-translate/core'; import uniqueId from 'lodash/uniqueId'; import { SubmissionScopeType } from '../../../../core/submission/submission-scope-type'; @@ -61,6 +62,7 @@ export abstract class FieldParser { @Inject(CONFIG_DATA) protected configData: FormFieldModel, @Inject(INIT_FORM_VALUES) protected initFormValues: any, @Inject(PARSER_OPTIONS) protected parserOptions: ParserOptions, + protected translate: TranslateService, ) { } @@ -68,8 +70,8 @@ export abstract class FieldParser { public parse() { if (((this.getInitValueCount() > 1 && !this.configData.repeatable) || (this.configData.repeatable)) - && (this.configData.input.type !== ParserType.List) - && (this.configData.input.type !== ParserType.Tag) + && (this.configData.input.type !== ParserType.List.valueOf()) + && (this.configData.input.type !== ParserType.Tag.valueOf()) ) { let arrayCounter = 0; let fieldArrayCounter = 0; @@ -81,7 +83,7 @@ export abstract class FieldParser { } let isDraggable = true; - if (this.configData.input.type === ParserType.Onebox && this.configData?.selectableMetadata?.length > 1) { + if (this.configData.input.type === ParserType.Onebox.valueOf() && this.configData?.selectableMetadata?.length > 1) { isDraggable = false; } const config = { @@ -344,12 +346,12 @@ export abstract class FieldParser { && isNotEmpty(fieldScope) && isNotEmpty(visibility) && (( - submissionScope === SubmissionScopeType.WorkspaceItem + submissionScope === SubmissionScopeType.WorkspaceItem.valueOf() && visibility.main === VisibilityType.READONLY ) || (visibility.other === VisibilityType.READONLY - && submissionScope === SubmissionScopeType.WorkflowItem + && submissionScope === SubmissionScopeType.WorkflowItem.valueOf() ) ); } @@ -405,11 +407,14 @@ export abstract class FieldParser { } else { regex = new RegExp(this.configData.input.regex); } + const baseTranslationKey = 'error.validation.pattern'; + const fieldranslationKey = `${baseTranslationKey}.${controlModel.id}`; + const fieldTranslationExists = this.translate.instant(fieldranslationKey) !== fieldranslationKey; controlModel.validators = Object.assign({}, controlModel.validators, { pattern: regex }); controlModel.errorMessages = Object.assign( {}, controlModel.errorMessages, - { pattern: 'error.validation.pattern' }); + { pattern: fieldTranslationExists ? fieldranslationKey : baseTranslationKey }); } protected markAsRequired(controlModel) { diff --git a/src/app/shared/form/builder/parsers/list-field-parser.spec.ts b/src/app/shared/form/builder/parsers/list-field-parser.spec.ts index f0a446c0ce..646fee9dec 100644 --- a/src/app/shared/form/builder/parsers/list-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/list-field-parser.spec.ts @@ -1,3 +1,5 @@ +import { getMockTranslateService } from 'src/app/shared/mocks/translate.service.mock'; + import { DynamicListCheckboxGroupModel } from '../ds-dynamic-form-ui/models/list/dynamic-list-checkbox-group.model'; import { DynamicListRadioGroupModel } from '../ds-dynamic-form-ui/models/list/dynamic-list-radio-group.model'; import { FormFieldModel } from '../models/form-field.model'; @@ -8,6 +10,7 @@ import { ParserOptions } from './parser-options'; describe('ListFieldParser test suite', () => { let field: FormFieldModel; let initFormValues = {}; + let translateService = getMockTranslateService(); const submissionId = '1234'; const parserOptions: ParserOptions = { @@ -39,13 +42,13 @@ describe('ListFieldParser test suite', () => { }); it('should init parser properly', () => { - const parser = new ListFieldParser(submissionId, field, initFormValues, parserOptions); + const parser = new ListFieldParser(submissionId, field, initFormValues, parserOptions, translateService); expect(parser instanceof ListFieldParser).toBe(true); }); it('should return a DynamicListCheckboxGroupModel object when repeatable option is true', () => { - const parser = new ListFieldParser(submissionId, field, initFormValues, parserOptions); + const parser = new ListFieldParser(submissionId, field, initFormValues, parserOptions, translateService); const fieldModel = parser.parse(); @@ -54,7 +57,7 @@ describe('ListFieldParser test suite', () => { it('should return a DynamicListRadioGroupModel object when repeatable option is false', () => { field.repeatable = false; - const parser = new ListFieldParser(submissionId, field, initFormValues, parserOptions); + const parser = new ListFieldParser(submissionId, field, initFormValues, parserOptions, translateService); const fieldModel = parser.parse(); @@ -67,7 +70,7 @@ describe('ListFieldParser test suite', () => { }; const expectedValue = [new FormFieldMetadataValueObject('test type')]; - const parser = new ListFieldParser(submissionId, field, initFormValues, parserOptions); + const parser = new ListFieldParser(submissionId, field, initFormValues, parserOptions, translateService); const fieldModel = parser.parse(); diff --git a/src/app/shared/form/builder/parsers/list-field-parser.ts b/src/app/shared/form/builder/parsers/list-field-parser.ts index f786e888ce..dd61987247 100644 --- a/src/app/shared/form/builder/parsers/list-field-parser.ts +++ b/src/app/shared/form/builder/parsers/list-field-parser.ts @@ -6,7 +6,7 @@ import { FieldParser } from './field-parser'; export class ListFieldParser extends FieldParser { - public modelFactory(fieldValue?: FormFieldMetadataValueObject | any, label?: boolean): any { + public modelFactory(fieldValue?: FormFieldMetadataValueObject, label?: boolean): any { const listModelConfig = this.initModel(null, label); listModelConfig.repeatable = this.configData.repeatable; diff --git a/src/app/shared/form/builder/parsers/lookup-field-parser.spec.ts b/src/app/shared/form/builder/parsers/lookup-field-parser.spec.ts index 6d57e68643..2ed45803ed 100644 --- a/src/app/shared/form/builder/parsers/lookup-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/lookup-field-parser.spec.ts @@ -1,3 +1,5 @@ +import { getMockTranslateService } from 'src/app/shared/mocks/translate.service.mock'; + import { DynamicLookupModel } from '../ds-dynamic-form-ui/models/lookup/dynamic-lookup.model'; import { FormFieldModel } from '../models/form-field.model'; import { FormFieldMetadataValueObject } from '../models/form-field-metadata-value.model'; @@ -7,6 +9,7 @@ import { ParserOptions } from './parser-options'; describe('LookupFieldParser test suite', () => { let field: FormFieldModel; let initFormValues = {}; + let translateService = getMockTranslateService(); const submissionId = '1234'; const parserOptions: ParserOptions = { @@ -38,13 +41,13 @@ describe('LookupFieldParser test suite', () => { }); it('should init parser properly', () => { - const parser = new LookupFieldParser(submissionId, field, initFormValues, parserOptions); + const parser = new LookupFieldParser(submissionId, field, initFormValues, parserOptions, translateService); expect(parser instanceof LookupFieldParser).toBe(true); }); it('should return a DynamicLookupModel object when repeatable option is false', () => { - const parser = new LookupFieldParser(submissionId, field, initFormValues, parserOptions); + const parser = new LookupFieldParser(submissionId, field, initFormValues, parserOptions, translateService); const fieldModel = parser.parse(); @@ -57,7 +60,7 @@ describe('LookupFieldParser test suite', () => { }; const expectedValue = new FormFieldMetadataValueObject('test journal'); - const parser = new LookupFieldParser(submissionId, field, initFormValues, parserOptions); + const parser = new LookupFieldParser(submissionId, field, initFormValues, parserOptions, translateService); const fieldModel = parser.parse(); diff --git a/src/app/shared/form/builder/parsers/lookup-field-parser.ts b/src/app/shared/form/builder/parsers/lookup-field-parser.ts index de7ba04e76..cb75022cff 100644 --- a/src/app/shared/form/builder/parsers/lookup-field-parser.ts +++ b/src/app/shared/form/builder/parsers/lookup-field-parser.ts @@ -7,7 +7,7 @@ import { FieldParser } from './field-parser'; export class LookupFieldParser extends FieldParser { - public modelFactory(fieldValue?: FormFieldMetadataValueObject | any, label?: boolean): any { + public modelFactory(fieldValue?: FormFieldMetadataValueObject, label?: boolean): any { if (this.configData.selectableMetadata[0].controlledVocabulary) { const lookupModelConfig: DynamicLookupModelConfig = this.initModel(null, label); diff --git a/src/app/shared/form/builder/parsers/lookup-name-field-parser.spec.ts b/src/app/shared/form/builder/parsers/lookup-name-field-parser.spec.ts index c6e8df8985..3384071f6e 100644 --- a/src/app/shared/form/builder/parsers/lookup-name-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/lookup-name-field-parser.spec.ts @@ -1,3 +1,5 @@ +import { getMockTranslateService } from 'src/app/shared/mocks/translate.service.mock'; + import { DynamicLookupNameModel } from '../ds-dynamic-form-ui/models/lookup/dynamic-lookup-name.model'; import { FormFieldModel } from '../models/form-field.model'; import { FormFieldMetadataValueObject } from '../models/form-field-metadata-value.model'; @@ -7,6 +9,7 @@ import { ParserOptions } from './parser-options'; describe('LookupNameFieldParser test suite', () => { let field: FormFieldModel; let initFormValues = {}; + let translateService = getMockTranslateService(); const submissionId = '1234'; const parserOptions: ParserOptions = { @@ -38,13 +41,13 @@ describe('LookupNameFieldParser test suite', () => { }); it('should init parser properly', () => { - const parser = new LookupNameFieldParser(submissionId, field, initFormValues, parserOptions); + const parser = new LookupNameFieldParser(submissionId, field, initFormValues, parserOptions, translateService); expect(parser instanceof LookupNameFieldParser).toBe(true); }); it('should return a DynamicLookupNameModel object when repeatable option is false', () => { - const parser = new LookupNameFieldParser(submissionId, field, initFormValues, parserOptions); + const parser = new LookupNameFieldParser(submissionId, field, initFormValues, parserOptions, translateService); const fieldModel = parser.parse(); @@ -57,7 +60,7 @@ describe('LookupNameFieldParser test suite', () => { }; const expectedValue = new FormFieldMetadataValueObject('test author'); - const parser = new LookupNameFieldParser(submissionId, field, initFormValues, parserOptions); + const parser = new LookupNameFieldParser(submissionId, field, initFormValues, parserOptions, translateService); const fieldModel = parser.parse(); diff --git a/src/app/shared/form/builder/parsers/lookup-name-field-parser.ts b/src/app/shared/form/builder/parsers/lookup-name-field-parser.ts index 0701d7cb7d..ba7f2f0235 100644 --- a/src/app/shared/form/builder/parsers/lookup-name-field-parser.ts +++ b/src/app/shared/form/builder/parsers/lookup-name-field-parser.ts @@ -7,7 +7,7 @@ import { FieldParser } from './field-parser'; export class LookupNameFieldParser extends FieldParser { - public modelFactory(fieldValue?: FormFieldMetadataValueObject | any, label?: boolean): any { + public modelFactory(fieldValue?: FormFieldMetadataValueObject, label?: boolean): any { if (this.configData.selectableMetadata[0].controlledVocabulary) { const lookupModelConfig: DynamicLookupNameModelConfig = this.initModel(null, label); diff --git a/src/app/shared/form/builder/parsers/name-field-parser.spec.ts b/src/app/shared/form/builder/parsers/name-field-parser.spec.ts index 7184e67e96..80b3f9ee83 100644 --- a/src/app/shared/form/builder/parsers/name-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/name-field-parser.spec.ts @@ -1,3 +1,5 @@ +import { getMockTranslateService } from 'src/app/shared/mocks/translate.service.mock'; + import { DynamicConcatModel } from '../ds-dynamic-form-ui/models/ds-dynamic-concat.model'; import { FormFieldModel } from '../models/form-field.model'; import { FormFieldMetadataValueObject } from '../models/form-field-metadata-value.model'; @@ -9,6 +11,7 @@ describe('NameFieldParser test suite', () => { let field2: FormFieldModel; let field3: FormFieldModel; let initFormValues: any = {}; + let translateService = getMockTranslateService(); const submissionId = '1234'; const parserOptions: ParserOptions = { @@ -71,13 +74,13 @@ describe('NameFieldParser test suite', () => { }); it('should init parser properly', () => { - const parser = new NameFieldParser(submissionId, field1, initFormValues, parserOptions); + const parser = new NameFieldParser(submissionId, field1, initFormValues, parserOptions, translateService); expect(parser instanceof NameFieldParser).toBe(true); }); it('should return a DynamicConcatModel object when repeatable option is false', () => { - const parser = new NameFieldParser(submissionId, field2, initFormValues, parserOptions); + const parser = new NameFieldParser(submissionId, field2, initFormValues, parserOptions, translateService); const fieldModel = parser.parse(); @@ -85,7 +88,7 @@ describe('NameFieldParser test suite', () => { }); it('should return a DynamicConcatModel object with the correct separator', () => { - const parser = new NameFieldParser(submissionId, field2, initFormValues, parserOptions); + const parser = new NameFieldParser(submissionId, field2, initFormValues, parserOptions, translateService); const fieldModel = parser.parse(); @@ -98,7 +101,7 @@ describe('NameFieldParser test suite', () => { }; const expectedValue = new FormFieldMetadataValueObject('test, name', undefined, undefined, 'test'); - const parser = new NameFieldParser(submissionId, field1, initFormValues, parserOptions); + const parser = new NameFieldParser(submissionId, field1, initFormValues, parserOptions, translateService); const fieldModel = parser.parse(); diff --git a/src/app/shared/form/builder/parsers/name-field-parser.ts b/src/app/shared/form/builder/parsers/name-field-parser.ts index 1c0fbd6d01..4bb36ad4d0 100644 --- a/src/app/shared/form/builder/parsers/name-field-parser.ts +++ b/src/app/shared/form/builder/parsers/name-field-parser.ts @@ -1,4 +1,5 @@ import { Inject } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; import { FormFieldModel } from '../models/form-field.model'; import { ConcatFieldParser } from './concat-field-parser'; @@ -17,7 +18,8 @@ export class NameFieldParser extends ConcatFieldParser { @Inject(CONFIG_DATA) configData: FormFieldModel, @Inject(INIT_FORM_VALUES) initFormValues, @Inject(PARSER_OPTIONS) parserOptions: ParserOptions, + translate: TranslateService, ) { - super(submissionId, configData, initFormValues, parserOptions, ',', 'form.last-name', 'form.first-name'); + super(submissionId, configData, initFormValues, parserOptions, translate, ',', 'form.last-name', 'form.first-name'); } } diff --git a/src/app/shared/form/builder/parsers/onebox-field-parser.spec.ts b/src/app/shared/form/builder/parsers/onebox-field-parser.spec.ts index 467431d963..b2a50395e1 100644 --- a/src/app/shared/form/builder/parsers/onebox-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/onebox-field-parser.spec.ts @@ -1,3 +1,5 @@ +import { getMockTranslateService } from 'src/app/shared/mocks/translate.service.mock'; + import { DsDynamicInputModel } from '../ds-dynamic-form-ui/models/ds-dynamic-input.model'; import { DynamicQualdropModel } from '../ds-dynamic-form-ui/models/ds-dynamic-qualdrop.model'; import { DynamicOneboxModel } from '../ds-dynamic-form-ui/models/onebox/dynamic-onebox.model'; @@ -10,6 +12,7 @@ describe('OneboxFieldParser test suite', () => { let field1: FormFieldModel; let field2: FormFieldModel; let field3: FormFieldModel; + let translateService = getMockTranslateService(); const submissionId = '1234'; const initFormValues = {}; @@ -73,13 +76,13 @@ describe('OneboxFieldParser test suite', () => { }); it('should init parser properly', () => { - const parser = new OneboxFieldParser(submissionId, field1, initFormValues, parserOptions); + const parser = new OneboxFieldParser(submissionId, field1, initFormValues, parserOptions, translateService); expect(parser instanceof OneboxFieldParser).toBe(true); }); it('should return a DynamicQualdropModel object when selectableMetadata is multiple', () => { - const parser = new OneboxFieldParser(submissionId, field2, initFormValues, parserOptions); + const parser = new OneboxFieldParser(submissionId, field2, initFormValues, parserOptions, translateService); const fieldModel = parser.parse(); @@ -87,7 +90,7 @@ describe('OneboxFieldParser test suite', () => { }); it('should return a DsDynamicInputModel object when selectableMetadata is not multiple', () => { - const parser = new OneboxFieldParser(submissionId, field3, initFormValues, parserOptions); + const parser = new OneboxFieldParser(submissionId, field3, initFormValues, parserOptions, translateService); const fieldModel = parser.parse(); @@ -95,7 +98,7 @@ describe('OneboxFieldParser test suite', () => { }); it('should return a DynamicOneboxModel object when selectableMetadata has authority', () => { - const parser = new OneboxFieldParser(submissionId, field1, initFormValues, parserOptions); + const parser = new OneboxFieldParser(submissionId, field1, initFormValues, parserOptions, translateService); const fieldModel = parser.parse(); @@ -124,7 +127,7 @@ describe('OneboxFieldParser test suite', () => { languageCodes: [], } as FormFieldModel; - parser = new OneboxFieldParser(submissionId, regexField, initFormValues, parserOptions); + parser = new OneboxFieldParser(submissionId, regexField, initFormValues, parserOptions, translateService); fieldModel = parser.parse(); }); diff --git a/src/app/shared/form/builder/parsers/onebox-field-parser.ts b/src/app/shared/form/builder/parsers/onebox-field-parser.ts index 2b994146c7..9c5ef7423e 100644 --- a/src/app/shared/form/builder/parsers/onebox-field-parser.ts +++ b/src/app/shared/form/builder/parsers/onebox-field-parser.ts @@ -24,7 +24,7 @@ import { FieldParser } from './field-parser'; export class OneboxFieldParser extends FieldParser { - public modelFactory(fieldValue?: FormFieldMetadataValueObject | any, label?: boolean): any { + public modelFactory(fieldValue?: FormFieldMetadataValueObject, label?: boolean): any { if (this.configData.selectableMetadata.length > 1) { // Case Qualdrop Model const clsGroup = { diff --git a/src/app/shared/form/builder/parsers/parser-factory.ts b/src/app/shared/form/builder/parsers/parser-factory.ts index 439baf4d4b..a56a27d77f 100644 --- a/src/app/shared/form/builder/parsers/parser-factory.ts +++ b/src/app/shared/form/builder/parsers/parser-factory.ts @@ -1,4 +1,5 @@ import { StaticProvider } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; import { DateFieldParser } from './date-field-parser'; import { DisabledFieldParser } from './disabled-field-parser'; @@ -26,6 +27,7 @@ const fieldParserDeps = [ CONFIG_DATA, INIT_FORM_VALUES, PARSER_OPTIONS, + TranslateService, ]; /** diff --git a/src/app/shared/form/builder/parsers/relation-group-field-parser.spec.ts b/src/app/shared/form/builder/parsers/relation-group-field-parser.spec.ts index ef4f0f709a..9eeeeea35c 100644 --- a/src/app/shared/form/builder/parsers/relation-group-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/relation-group-field-parser.spec.ts @@ -1,3 +1,5 @@ +import { getMockTranslateService } from 'src/app/shared/mocks/translate.service.mock'; + import { DynamicRelationGroupModel } from '../ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.model'; import { FormFieldModel } from '../models/form-field.model'; import { FormFieldMetadataValueObject } from '../models/form-field-metadata-value.model'; @@ -7,6 +9,7 @@ import { RelationGroupFieldParser } from './relation-group-field-parser'; describe('RelationGroupFieldParser test suite', () => { let field: FormFieldModel; let initFormValues = {}; + let translateService = getMockTranslateService(); const submissionId = '1234'; const parserOptions: ParserOptions = { @@ -73,13 +76,13 @@ describe('RelationGroupFieldParser test suite', () => { }); it('should init parser properly', () => { - const parser = new RelationGroupFieldParser(submissionId, field, initFormValues, parserOptions); + const parser = new RelationGroupFieldParser(submissionId, field, initFormValues, parserOptions, translateService); expect(parser instanceof RelationGroupFieldParser).toBe(true); }); it('should return a DynamicRelationGroupModel object', () => { - const parser = new RelationGroupFieldParser(submissionId, field, initFormValues, parserOptions); + const parser = new RelationGroupFieldParser(submissionId, field, initFormValues, parserOptions, translateService); const fieldModel = parser.parse(); @@ -88,7 +91,7 @@ describe('RelationGroupFieldParser test suite', () => { it('should throw when rows configuration is empty', () => { field.rows = null; - const parser = new RelationGroupFieldParser(submissionId, field, initFormValues, parserOptions); + const parser = new RelationGroupFieldParser(submissionId, field, initFormValues, parserOptions, translateService); expect(() => parser.parse()) .toThrow(); @@ -99,7 +102,7 @@ describe('RelationGroupFieldParser test suite', () => { author: [new FormFieldMetadataValueObject('test author')], affiliation: [new FormFieldMetadataValueObject('test affiliation')], }; - const parser = new RelationGroupFieldParser(submissionId, field, initFormValues, parserOptions); + const parser = new RelationGroupFieldParser(submissionId, field, initFormValues, parserOptions, translateService); const fieldModel = parser.parse(); const expectedValue = [{ diff --git a/src/app/shared/form/builder/parsers/relation-group-field-parser.ts b/src/app/shared/form/builder/parsers/relation-group-field-parser.ts index 112bfdaf24..30401b20ef 100644 --- a/src/app/shared/form/builder/parsers/relation-group-field-parser.ts +++ b/src/app/shared/form/builder/parsers/relation-group-field-parser.ts @@ -11,7 +11,7 @@ import { FieldParser } from './field-parser'; export class RelationGroupFieldParser extends FieldParser { - public modelFactory(fieldValue?: FormFieldMetadataValueObject | any, label?: boolean) { + public modelFactory(fieldValue?: FormFieldMetadataValueObject, label?: boolean) { const modelConfiguration: DynamicRelationGroupModelConfig = this.initModel(null, label); modelConfiguration.submissionId = this.submissionId; diff --git a/src/app/shared/form/builder/parsers/row-parser.spec.ts b/src/app/shared/form/builder/parsers/row-parser.spec.ts index 9fde7cc839..d87931a488 100644 --- a/src/app/shared/form/builder/parsers/row-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/row-parser.spec.ts @@ -1,3 +1,7 @@ +import { Injector } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; +import { getMockTranslateService } from 'src/app/shared/mocks/translate.service.mock'; + import { FormRowModel } from '../../../../core/config/models/config-submission-form.model'; import { DynamicRowArrayModel } from '../ds-dynamic-form-ui/models/ds-dynamic-row-array-model'; import { DynamicRowGroupModel } from '../ds-dynamic-form-ui/models/ds-dynamic-row-group-model'; @@ -16,6 +20,7 @@ describe('RowParser test suite', () => { let row8: FormRowModel; let row9: FormRowModel; let row10: FormRowModel; + let injector: Injector; const submissionId = '1234'; const scopeUUID = 'testScopeUUID'; @@ -25,6 +30,12 @@ describe('RowParser test suite', () => { const typeField = 'dc_type'; beforeEach(() => { + let translateService = getMockTranslateService(); + injector = Injector.create({ + providers: [ + { provide: TranslateService, useValue: translateService }, + ], + }); row1 = { fields: [ { @@ -330,14 +341,14 @@ describe('RowParser test suite', () => { }); it('should init parser properly', () => { - const parser = new RowParser(undefined); + const parser = new RowParser(injector); expect(parser instanceof RowParser).toBe(true); }); describe('parse', () => { it('should return a DynamicRowGroupModel object', () => { - const parser = new RowParser(undefined); + const parser = new RowParser(injector); const rowModel = parser.parse(submissionId, row1, scopeUUID, initFormValues, submissionScope, readOnly, typeField); @@ -345,7 +356,7 @@ describe('RowParser test suite', () => { }); it('should return a row with three fields', () => { - const parser = new RowParser(undefined); + const parser = new RowParser(injector); const rowModel = parser.parse(submissionId, row1, scopeUUID, initFormValues, submissionScope, readOnly, typeField); @@ -353,7 +364,7 @@ describe('RowParser test suite', () => { }); it('should return a DynamicRowArrayModel object', () => { - const parser = new RowParser(undefined); + const parser = new RowParser(injector); const rowModel = parser.parse(submissionId, row2, scopeUUID, initFormValues, submissionScope, readOnly, typeField); @@ -361,7 +372,7 @@ describe('RowParser test suite', () => { }); it('should return a row that contains only scoped fields', () => { - const parser = new RowParser(undefined); + const parser = new RowParser(injector); const rowModel = parser.parse(submissionId, row3, scopeUUID, initFormValues, submissionScope, readOnly, typeField); @@ -369,7 +380,7 @@ describe('RowParser test suite', () => { }); it('should be able to parse a dropdown combo field', () => { - const parser = new RowParser(undefined); + const parser = new RowParser(injector); const rowModel = parser.parse(submissionId, row4, scopeUUID, initFormValues, submissionScope, readOnly, typeField); @@ -377,7 +388,7 @@ describe('RowParser test suite', () => { }); it('should be able to parse a lookup-name field', () => { - const parser = new RowParser(undefined); + const parser = new RowParser(injector); const rowModel = parser.parse(submissionId, row5, scopeUUID, initFormValues, submissionScope, readOnly, typeField); @@ -385,7 +396,7 @@ describe('RowParser test suite', () => { }); it('should be able to parse a list field', () => { - const parser = new RowParser(undefined); + const parser = new RowParser(injector); const rowModel = parser.parse(submissionId, row6, scopeUUID, initFormValues, submissionScope, readOnly, typeField); @@ -393,7 +404,7 @@ describe('RowParser test suite', () => { }); it('should be able to parse a date field', () => { - const parser = new RowParser(undefined); + const parser = new RowParser(injector); const rowModel = parser.parse(submissionId, row7, scopeUUID, initFormValues, submissionScope, readOnly, typeField); @@ -401,7 +412,7 @@ describe('RowParser test suite', () => { }); it('should be able to parse a tag field', () => { - const parser = new RowParser(undefined); + const parser = new RowParser(injector); const rowModel = parser.parse(submissionId, row8, scopeUUID, initFormValues, submissionScope, readOnly, typeField); @@ -409,7 +420,7 @@ describe('RowParser test suite', () => { }); it('should be able to parse a textarea field', () => { - const parser = new RowParser(undefined); + const parser = new RowParser(injector); const rowModel = parser.parse(submissionId, row9, scopeUUID, initFormValues, submissionScope, readOnly, typeField); @@ -417,7 +428,7 @@ describe('RowParser test suite', () => { }); it('should be able to parse a group field', () => { - const parser = new RowParser(undefined); + const parser = new RowParser(injector); const rowModel = parser.parse(submissionId, row10, scopeUUID, initFormValues, submissionScope, readOnly, typeField); diff --git a/src/app/shared/form/builder/parsers/row-parser.ts b/src/app/shared/form/builder/parsers/row-parser.ts index 97c9cb8257..b4febfc47f 100644 --- a/src/app/shared/form/builder/parsers/row-parser.ts +++ b/src/app/shared/form/builder/parsers/row-parser.ts @@ -154,9 +154,9 @@ export class RowParser { && ( isEmpty(visibility) && ( - submissionScope === SubmissionScopeType.WorkspaceItem && scope !== SubmissionFieldScopeType.WorkspaceItem + submissionScope === SubmissionScopeType.WorkspaceItem.valueOf() && scope !== SubmissionFieldScopeType.WorkspaceItem.valueOf() || - submissionScope === SubmissionScopeType.WorkflowItem && scope !== SubmissionFieldScopeType.WorkflowItem + submissionScope === SubmissionScopeType.WorkflowItem.valueOf() && scope !== SubmissionFieldScopeType.WorkflowItem.valueOf() ) ); } diff --git a/src/app/shared/form/builder/parsers/series-field-parser.spec.ts b/src/app/shared/form/builder/parsers/series-field-parser.spec.ts index d4b37e5d7f..8141bf23e0 100644 --- a/src/app/shared/form/builder/parsers/series-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/series-field-parser.spec.ts @@ -1,3 +1,5 @@ +import { getMockTranslateService } from 'src/app/shared/mocks/translate.service.mock'; + import { DynamicConcatModel } from '../ds-dynamic-form-ui/models/ds-dynamic-concat.model'; import { FormFieldModel } from '../models/form-field.model'; import { FormFieldMetadataValueObject } from '../models/form-field-metadata-value.model'; @@ -7,6 +9,7 @@ import { SeriesFieldParser } from './series-field-parser'; describe('SeriesFieldParser test suite', () => { let field: FormFieldModel; let initFormValues: any = {}; + let translateService = getMockTranslateService(); const submissionId = '1234'; const parserOptions: ParserOptions = { @@ -34,13 +37,13 @@ describe('SeriesFieldParser test suite', () => { }); it('should init parser properly', () => { - const parser = new SeriesFieldParser(submissionId, field, initFormValues, parserOptions); + const parser = new SeriesFieldParser(submissionId, field, initFormValues, parserOptions, translateService); expect(parser instanceof SeriesFieldParser).toBe(true); }); it('should return a DynamicConcatModel object when repeatable option is false', () => { - const parser = new SeriesFieldParser(submissionId, field, initFormValues, parserOptions); + const parser = new SeriesFieldParser(submissionId, field, initFormValues, parserOptions, translateService); const fieldModel = parser.parse(); @@ -48,7 +51,7 @@ describe('SeriesFieldParser test suite', () => { }); it('should return a DynamicConcatModel object with the correct separator', () => { - const parser = new SeriesFieldParser(submissionId, field, initFormValues, parserOptions); + const parser = new SeriesFieldParser(submissionId, field, initFormValues, parserOptions, translateService); const fieldModel = parser.parse(); @@ -61,7 +64,7 @@ describe('SeriesFieldParser test suite', () => { }; const expectedValue = new FormFieldMetadataValueObject('test; series', undefined, undefined, 'test'); - const parser = new SeriesFieldParser(submissionId, field, initFormValues, parserOptions); + const parser = new SeriesFieldParser(submissionId, field, initFormValues, parserOptions, translateService); const fieldModel = parser.parse(); diff --git a/src/app/shared/form/builder/parsers/series-field-parser.ts b/src/app/shared/form/builder/parsers/series-field-parser.ts index ce85d07d29..196718ab87 100644 --- a/src/app/shared/form/builder/parsers/series-field-parser.ts +++ b/src/app/shared/form/builder/parsers/series-field-parser.ts @@ -1,4 +1,5 @@ import { Inject } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; import { FormFieldModel } from '../models/form-field.model'; import { ConcatFieldParser } from './concat-field-parser'; @@ -17,7 +18,8 @@ export class SeriesFieldParser extends ConcatFieldParser { @Inject(CONFIG_DATA) configData: FormFieldModel, @Inject(INIT_FORM_VALUES) initFormValues, @Inject(PARSER_OPTIONS) parserOptions: ParserOptions, + translate: TranslateService, ) { - super(submissionId, configData, initFormValues, parserOptions, ';'); + super(submissionId, configData, initFormValues, parserOptions, translate, ';'); } } diff --git a/src/app/shared/form/builder/parsers/tag-field-parser.spec.ts b/src/app/shared/form/builder/parsers/tag-field-parser.spec.ts index 571d06d983..303d63cad5 100644 --- a/src/app/shared/form/builder/parsers/tag-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/tag-field-parser.spec.ts @@ -1,3 +1,5 @@ +import { getMockTranslateService } from 'src/app/shared/mocks/translate.service.mock'; + import { DynamicTagModel } from '../ds-dynamic-form-ui/models/tag/dynamic-tag.model'; import { FormFieldModel } from '../models/form-field.model'; import { FormFieldMetadataValueObject } from '../models/form-field-metadata-value.model'; @@ -7,6 +9,7 @@ import { TagFieldParser } from './tag-field-parser'; describe('TagFieldParser test suite', () => { let field: FormFieldModel; let initFormValues: any = {}; + let translateService = getMockTranslateService(); const submissionId = '1234'; const parserOptions: ParserOptions = { @@ -38,13 +41,13 @@ describe('TagFieldParser test suite', () => { }); it('should init parser properly', () => { - const parser = new TagFieldParser(submissionId, field, initFormValues, parserOptions); + const parser = new TagFieldParser(submissionId, field, initFormValues, parserOptions, translateService); expect(parser instanceof TagFieldParser).toBe(true); }); it('should return a DynamicTagModel object when repeatable option is false', () => { - const parser = new TagFieldParser(submissionId, field, initFormValues, parserOptions); + const parser = new TagFieldParser(submissionId, field, initFormValues, parserOptions, translateService); const fieldModel = parser.parse(); @@ -59,7 +62,7 @@ describe('TagFieldParser test suite', () => { ], }; - const parser = new TagFieldParser(submissionId, field, initFormValues, parserOptions); + const parser = new TagFieldParser(submissionId, field, initFormValues, parserOptions, translateService); const fieldModel = parser.parse(); diff --git a/src/app/shared/form/builder/parsers/tag-field-parser.ts b/src/app/shared/form/builder/parsers/tag-field-parser.ts index 5c3f2e60bc..6534b9dc5f 100644 --- a/src/app/shared/form/builder/parsers/tag-field-parser.ts +++ b/src/app/shared/form/builder/parsers/tag-field-parser.ts @@ -7,7 +7,7 @@ import { FieldParser } from './field-parser'; export class TagFieldParser extends FieldParser { - public modelFactory(fieldValue?: FormFieldMetadataValueObject | any, label?: boolean): any { + public modelFactory(fieldValue?: FormFieldMetadataValueObject, label?: boolean): any { const tagModelConfig: DynamicTagModelConfig = this.initModel(null, label); if (this.configData.selectableMetadata[0].controlledVocabulary && this.configData.selectableMetadata[0].controlledVocabulary.length > 0) { diff --git a/src/app/shared/form/builder/parsers/textarea-field-parser.spec.ts b/src/app/shared/form/builder/parsers/textarea-field-parser.spec.ts index 95fef2103c..cfab7c36e3 100644 --- a/src/app/shared/form/builder/parsers/textarea-field-parser.spec.ts +++ b/src/app/shared/form/builder/parsers/textarea-field-parser.spec.ts @@ -1,3 +1,5 @@ +import { getMockTranslateService } from 'src/app/shared/mocks/translate.service.mock'; + import { DsDynamicTextAreaModel } from '../ds-dynamic-form-ui/models/ds-dynamic-textarea.model'; import { FormFieldModel } from '../models/form-field.model'; import { FormFieldMetadataValueObject } from '../models/form-field-metadata-value.model'; @@ -7,6 +9,7 @@ import { TextareaFieldParser } from './textarea-field-parser'; describe('TextareaFieldParser test suite', () => { let field: FormFieldModel; let initFormValues: any = {}; + let translateService = getMockTranslateService(); const submissionId = '1234'; const parserOptions: ParserOptions = { @@ -36,13 +39,13 @@ describe('TextareaFieldParser test suite', () => { }); it('should init parser properly', () => { - const parser = new TextareaFieldParser(submissionId, field, initFormValues, parserOptions); + const parser = new TextareaFieldParser(submissionId, field, initFormValues, parserOptions, translateService); expect(parser instanceof TextareaFieldParser).toBe(true); }); it('should return a DsDynamicTextAreaModel object when repeatable option is false', () => { - const parser = new TextareaFieldParser(submissionId, field, initFormValues, parserOptions); + const parser = new TextareaFieldParser(submissionId, field, initFormValues, parserOptions, translateService); const fieldModel = parser.parse(); @@ -57,7 +60,7 @@ describe('TextareaFieldParser test suite', () => { }; const expectedValue = 'test description'; - const parser = new TextareaFieldParser(submissionId, field, initFormValues, parserOptions); + const parser = new TextareaFieldParser(submissionId, field, initFormValues, parserOptions, translateService); const fieldModel = parser.parse(); diff --git a/src/app/shared/form/builder/parsers/textarea-field-parser.ts b/src/app/shared/form/builder/parsers/textarea-field-parser.ts index c253899b3d..a19fa0ccd2 100644 --- a/src/app/shared/form/builder/parsers/textarea-field-parser.ts +++ b/src/app/shared/form/builder/parsers/textarea-field-parser.ts @@ -8,7 +8,7 @@ import { FieldParser } from './field-parser'; export class TextareaFieldParser extends FieldParser { - public modelFactory(fieldValue?: FormFieldMetadataValueObject | any, label?: boolean): any { + public modelFactory(fieldValue?: FormFieldMetadataValueObject, label?: boolean): any { const textAreaModelConfig: DsDynamicTextAreaModelConfig = this.initModel(null, label); const layout = { diff --git a/src/app/shared/form/vocabulary-treeview/vocabulary-treeview.component.html b/src/app/shared/form/vocabulary-treeview/vocabulary-treeview.component.html index 54a1992b97..412c732b01 100644 --- a/src/app/shared/form/vocabulary-treeview/vocabulary-treeview.component.html +++ b/src/app/shared/form/vocabulary-treeview/vocabulary-treeview.component.html @@ -22,7 +22,7 @@
    - +

    {{'vocabulary-treeview.search.no-result' | translate}}

    diff --git a/src/app/shared/idle-modal/idle-modal.component.ts b/src/app/shared/idle-modal/idle-modal.component.ts index b8633147da..112ed7719c 100644 --- a/src/app/shared/idle-modal/idle-modal.component.ts +++ b/src/app/shared/idle-modal/idle-modal.component.ts @@ -37,7 +37,7 @@ export class IdleModalComponent implements OnInit { * An event fired when the modal is closed */ @Output() - response = new EventEmitter(); + response = new EventEmitter(); constructor(private activeModal: NgbActiveModal, private authService: AuthService, diff --git a/src/app/shared/lang-switch/lang-switch.component.ts b/src/app/shared/lang-switch/lang-switch.component.ts index c29b3f375a..3acb169419 100644 --- a/src/app/shared/lang-switch/lang-switch.component.ts +++ b/src/app/shared/lang-switch/lang-switch.component.ts @@ -18,7 +18,7 @@ import { environment } from '../../../environments/environment'; import { LocaleService } from '../../core/locale/locale.service'; @Component({ - selector: 'ds-lang-switch', + selector: 'ds-base-lang-switch', styleUrls: ['lang-switch.component.scss'], templateUrl: 'lang-switch.component.html', standalone: true, diff --git a/src/app/shared/lang-switch/themed-lang-switch.component.ts b/src/app/shared/lang-switch/themed-lang-switch.component.ts index 2e4d7f50e5..685d0f1799 100644 --- a/src/app/shared/lang-switch/themed-lang-switch.component.ts +++ b/src/app/shared/lang-switch/themed-lang-switch.component.ts @@ -7,10 +7,11 @@ import { LangSwitchComponent } from './lang-switch.component'; * Themed wrapper for {@link LangSwitchComponent} */ @Component({ - selector: 'ds-themed-lang-switch', + selector: 'ds-lang-switch', styleUrls: [], templateUrl: '../theme-support/themed.component.html', standalone: true, + imports: [LangSwitchComponent], }) export class ThemedLangSwitchComponent extends ThemedComponent { diff --git a/src/app/shared/loading/loading.component.ts b/src/app/shared/loading/loading.component.ts index 03846fe394..b982af4208 100644 --- a/src/app/shared/loading/loading.component.ts +++ b/src/app/shared/loading/loading.component.ts @@ -11,7 +11,7 @@ import { Subscription } from 'rxjs'; import { hasValue } from '../empty.util'; @Component({ - selector: 'ds-loading', + selector: 'ds-base-loading', styleUrls: ['./loading.component.scss'], templateUrl: './loading.component.html', standalone: true, diff --git a/src/app/shared/loading/themed-loading.component.ts b/src/app/shared/loading/themed-loading.component.ts index 983bdadff0..d16e0b988e 100644 --- a/src/app/shared/loading/themed-loading.component.ts +++ b/src/app/shared/loading/themed-loading.component.ts @@ -12,10 +12,11 @@ import { LoadingComponent } from './loading.component'; * Themed wrapper for LoadingComponent */ @Component({ - selector: 'ds-themed-loading', + selector: 'ds-loading', styleUrls: [], templateUrl: '../../shared/theme-support/themed.component.html', standalone: true, + imports: [LoadingComponent], }) export class ThemedLoadingComponent extends ThemedComponent { diff --git a/src/app/shared/log-in/log-in.component.html b/src/app/shared/log-in/log-in.component.html index d80513d5d0..309e226772 100644 --- a/src/app/shared/log-in/log-in.component.html +++ b/src/app/shared/log-in/log-in.component.html @@ -1,4 +1,4 @@ - +