diff --git a/packages/api/package-lock.json b/packages/api/package-lock.json index b6b758162..252f55b89 100644 --- a/packages/api/package-lock.json +++ b/packages/api/package-lock.json @@ -6,6 +6,7 @@ "": { "name": "@ontotext/workbench-api", "dependencies": { + "@reactivex/rxjs": "^6.6.7", "@types/jest": "^27.0.1", "@types/systemjs": "^6.1.1", "@types/webpack-env": "^1.16.2", @@ -2676,6 +2677,22 @@ "integrity": "sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==", "dev": true }, + "node_modules/@reactivex/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/@reactivex/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-xZIV2JgHhWoVPm3uVcFbZDRVJfx2hgqmuTX7J4MuKaZ+j5jN29agniCPBwrlCmpA15/zLKcPi7/bogt0ZwOFyA==", + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@reactivex/rxjs/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, "node_modules/@sinonjs/commons": { "version": "1.8.6", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", diff --git a/packages/api/package.json b/packages/api/package.json index 29404fcdf..f867ad662 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -19,29 +19,30 @@ "@babel/eslint-parser": "^7.23.3", "@babel/plugin-transform-runtime": "^7.23.3", "@babel/preset-env": "^7.23.3", + "@babel/preset-typescript": "^7.23.3", "@babel/runtime": "^7.23.3", "babel-jest": "^27.5.1", "concurrently": "^6.2.1", "cross-env": "^7.0.3", "eslint": "^7.32.0", "eslint-config-prettier": "^8.3.0", + "eslint-config-ts-important-stuff": "^1.1.0", "eslint-plugin-prettier": "^3.4.1", "identity-obj-proxy": "^3.0.0", "jest": "^27.5.1", "jest-cli": "^27.5.1", "prettier": "^2.3.2", "pretty-quick": "^3.1.1", + "ts-config-single-spa": "^3.0.0", + "typescript": "^4.3.5", "webpack": "^5.89.0", - "webpack-merge": "^5.8.0", "webpack-cli": "^4.10.0", - "webpack-dev-server": "^4.0.0", - "@babel/preset-typescript": "^7.23.3", - "eslint-config-ts-important-stuff": "^1.1.0", - "typescript": "^4.3.5", "webpack-config-single-spa-ts": "^4.0.0", - "ts-config-single-spa": "^3.0.0" + "webpack-dev-server": "^4.0.0", + "webpack-merge": "^5.8.0" }, "dependencies": { + "@reactivex/rxjs": "^6.6.7", "@types/jest": "^27.0.1", "@types/systemjs": "^6.1.1", "@types/webpack-env": "^1.16.2", diff --git a/packages/api/src/authentication-service.ts b/packages/api/src/authentication-service.ts deleted file mode 100644 index 00827ee56..000000000 --- a/packages/api/src/authentication-service.ts +++ /dev/null @@ -1,5 +0,0 @@ -export class AuthenticationService { - static login(): string { - return "Athentication.login from the API"; - } -} diff --git a/packages/api/src/models/events/event.ts b/packages/api/src/models/events/event.ts new file mode 100644 index 000000000..6dc5cfc61 --- /dev/null +++ b/packages/api/src/models/events/event.ts @@ -0,0 +1,24 @@ +/** + * A generic shape of our internal event data payloads. + */ +export class Event { + /** + * The name of the event. + */ + readonly NAME; + /** + * The payload of the event. + */ + readonly payload; + /** + * Creates a new instance of the event. + * + * @param name - the name of the event. + * @param payload - the payload of the event. This is optional and if omitted, the event will have no payload, + * just a name. + */ + constructor(name: string, payload?: any) { + this.NAME = name; + this.payload = payload; + } +} diff --git a/packages/api/src/ontotext-workbench-api.ts b/packages/api/src/ontotext-workbench-api.ts index b3e680b18..65b07e4d7 100644 --- a/packages/api/src/ontotext-workbench-api.ts +++ b/packages/api/src/ontotext-workbench-api.ts @@ -1,3 +1,6 @@ // Anything exported from this file is importable by other in-browser modules. -export {AuthenticationService} from './authentication-service'; -export {RepositoryService} from './repository-service'; +export {AuthenticationService} from './services/authentication.service'; +export {RepositoryService} from './services/repository.service'; +export {ServiceProvider} from './service.provider' +export {EventService} from './services/event.service'; +export {LanguageService} from './services/language.service'; diff --git a/packages/api/src/repository-service.ts b/packages/api/src/repository-service.ts deleted file mode 100644 index f5f9c66be..000000000 --- a/packages/api/src/repository-service.ts +++ /dev/null @@ -1,5 +0,0 @@ -export class RepositoryService { - static getRepositories(): Promise { - return fetch("http://localhost:9000/rest/repositories/all"); - } -} diff --git a/packages/api/src/service.provider.ts b/packages/api/src/service.provider.ts new file mode 100644 index 000000000..dddaa0d1f --- /dev/null +++ b/packages/api/src/service.provider.ts @@ -0,0 +1,23 @@ +import {Service} from './services/service'; + +/** + * Service provider for all {@link Service} instances. + * This provider caches all workbench services created on demand, ensuring that all micro frontends share a single instance of each service. + */ +export class ServiceProvider { + + /** + * The static modifier ensures the map is the same for all ServiceProviders. Each micro-frontend will have its + * own instance of {@see ServiceFactoryService}, but the map with instances will be shared. + * + * @private + */ + private static readonly SERVICE_INSTANCES = new Map + + public static get(type: { new(service: Service): T; }): T { + if (!ServiceProvider.SERVICE_INSTANCES.has(type.name)) { + ServiceProvider.SERVICE_INSTANCES.set(type.name, new type(this)); + } + return this.SERVICE_INSTANCES.get(type.name) as T; + } +} diff --git a/packages/api/src/services/authentication.service.ts b/packages/api/src/services/authentication.service.ts new file mode 100644 index 000000000..89b2fafc1 --- /dev/null +++ b/packages/api/src/services/authentication.service.ts @@ -0,0 +1,7 @@ +import {Service} from './service'; + +export class AuthenticationService implements Service { + login(): string { + return "Athentication.login from the API"; + } +} diff --git a/packages/api/src/services/event.service.ts b/packages/api/src/services/event.service.ts new file mode 100644 index 000000000..e5f6cbba9 --- /dev/null +++ b/packages/api/src/services/event.service.ts @@ -0,0 +1,44 @@ +import {Event} from '../models/events/event'; +import {Service} from './service'; + +/** + * Service used for global communication within all modules. It allows emitting and subscribing to CustomEvents across + * the application where the events are emitted via the document.body element. This allows for the events + * to be caught by any component in any module. + */ +export class EventService implements Service { + /** + * Emits a {@link CustomEvent} of type passed event.NAME and detail event.payload. + * + * @param event - the event to be emitted. + * @return the emitted event. + */ + emit(event: Event): CustomEvent { + const customEvent = new CustomEvent(event.NAME, {detail: event.payload}); + this.getHostElement().dispatchEvent(customEvent); + return customEvent; + } + + /** + * Subscribes for event of type eventName. + * + * @param eventName - type of subscription event. + * @param callback - callback function that will be called when the event occurred. + * + * @return unsubscribe function which can be used for manual unsubscription. + */ + subscribe(eventName: string, callback: (payload: any) => void): () => void { + const listener = (event) => { + if (event instanceof CustomEvent) { + callback(event.detail); + } + }; + this.getHostElement().addEventListener(eventName, listener); + + return () => this.getHostElement().removeEventListener(eventName, listener); + } + + private getHostElement(): HTMLElement { + return document.body; + } +} diff --git a/packages/api/src/services/language.service.ts b/packages/api/src/services/language.service.ts new file mode 100644 index 000000000..ec3d1cf2c --- /dev/null +++ b/packages/api/src/services/language.service.ts @@ -0,0 +1,44 @@ +import {Service} from './service'; +import {ReplaySubject, Subject} from '@reactivex/rxjs/dist/package'; + +/** + * The LanguageService class manages the application's language settings. + */ +export class LanguageService implements Service { + + static readonly DEFAULT_LANGUAGE = 'en'; + private readonly selectedLanguage = new ReplaySubject(1); + + /** + * Constructs a new LanguageService instance. + * Reads the initial language setting from local storage (not yet implemented) + * and sets it as the first value of the ReplaySubject. + */ + constructor() { + // TODO read it from local store and pass it as first value + } + + /** + * Changes the current language of the application. + * This method updates the language setting and notifies all subscribers + * about the language change. The new language is also intended to be saved + * to local storage (not yet implemented). + * + * @param {string} locale - The new language code to set (e.g., 'en', 'fr', 'de'). + */ + changeLanguage(locale: string): void { + // TODO save it to local store. + this.selectedLanguage.next(locale); + } + + /** + * Returns an observable that emits the current language whenever it changes. + * Subscribers to this observable will receive updates whenever the language + * is changed using the `changeLanguage` method. + * + * @returns {Subject} An observable stream of language changes. + */ + onLanguageChanged(): Subject { + return this.selectedLanguage; + } +} diff --git a/packages/api/src/services/repository.service.ts b/packages/api/src/services/repository.service.ts new file mode 100644 index 000000000..22632f246 --- /dev/null +++ b/packages/api/src/services/repository.service.ts @@ -0,0 +1,7 @@ +import {Service} from './service'; + +export class RepositoryService implements Service { + getRepositories(): Promise { + return fetch("http://localhost:9000/rest/repositories/all"); + } +} diff --git a/packages/api/src/services/service.ts b/packages/api/src/services/service.ts new file mode 100644 index 000000000..4f06d06d3 --- /dev/null +++ b/packages/api/src/services/service.ts @@ -0,0 +1,4 @@ +/** + * Interface for identifying a service as part of the workbench system. + */ +export interface Service {} diff --git a/packages/api/tsconfig.json b/packages/api/tsconfig.json index 09157ecc3..1ecff6536 100644 --- a/packages/api/tsconfig.json +++ b/packages/api/tsconfig.json @@ -1,7 +1,6 @@ { "extends": "ts-config-single-spa", - "files": [ - "src/authentication-service.ts", "src/ontotext-workbench-api.ts"], + "files": ["src/ontotext-workbench-api.ts"], "compilerOptions": { "declarationDir": "dist", "outDir": "dist", diff --git a/packages/legacy-workbench/src/app.js b/packages/legacy-workbench/src/app.js index 3aa927bd5..fa4c8b2ca 100644 --- a/packages/legacy-workbench/src/app.js +++ b/packages/legacy-workbench/src/app.js @@ -15,6 +15,7 @@ import 'angular/core/directives/operations-statuses-monitor/operations-statuses- import 'angular/core/directives/autocomplete/autocomplete.directive'; import 'angular/core/directives/prop-indeterminate/prop-indeterminate.directive'; import {defineCustomElements} from 'ontotext-yasgui-web-component/loader'; +import {ServiceProvider, LanguageService} from "@ontotext/workbench-api"; // $translate.instant converts from strings to <b> // and $sce.trustAsHtml could not recognise that this is valid html @@ -157,6 +158,7 @@ const moduleDefinition = function (productInfo) { // to construct version/edition-specific links. $menuItemsProvider.setProductInfo(productInfo); + // TODO this remove this when clean code. The main menu is processed in shared-components let mainMenu = PluginRegistry.get('main.menu'); mainMenu.forEach(function (menu) { menu.items.forEach(function (item) { @@ -218,6 +220,22 @@ const moduleDefinition = function (productInfo) { ThemeService.applyDarkThemeMode(); GuidesService.init(); + + // ========================= + // Functions and configurations for integration with the shared-components module. + // ========================= + const languageService = ServiceProvider.get(LanguageService); + + const languageChangeSubscriptions = languageService.onLanguageChanged() + .subscribe((language) => { + $translate.use(language); + }); + + $rootScope.$on('destroy', () => { + if (languageChangeSubscriptions) { + languageChangeSubscriptions.unsubscribe(); + } + }) }]); workbench.filter('titlecase', function() { diff --git a/packages/legacy-workbench/src/js/angular/controllers.js b/packages/legacy-workbench/src/js/angular/controllers.js index abb4ece93..956e53861 100644 --- a/packages/legacy-workbench/src/js/angular/controllers.js +++ b/packages/legacy-workbench/src/js/angular/controllers.js @@ -23,7 +23,6 @@ import './guides/directives'; import {GUIDE_PAUSE} from './guides/tour-lib-services/shepherd.service'; import 'angular-pageslide-directive/dist/angular-pageslide-directive'; import 'angularjs-slider/dist/rzslider.min'; -// import {AuthenticationService, RepositoryService} from "@ontotext/workbench-api"; angular .module('graphdb.workbench.se.controllers', [ @@ -61,14 +60,6 @@ homeCtrl.$inject = ['$scope', '$rootScope', '$http', '$repositories', '$jwtAuth' function homeCtrl($scope, $rootScope, $http, $repositories, $jwtAuth, $licenseService, AutocompleteRestService, LicenseRestService, RepositoriesRestService, RDF4JRepositoriesRestService, toastr) { - // console.log(`LOGIN TS API in new WB`, AuthenticationService.login()); - // RepositoryService.getRepositories().then((response) => { - // console.log(`response`, response); - // return response.json(); - // }).then((data) => { - // console.log(`REPOSITORIES TS API in new WB`, data); - // }); - $scope.doClear = false; $scope.getActiveRepositorySize = function () { diff --git a/packages/shared-components/.gitignore b/packages/shared-components/.gitignore index c3ea58a61..5b05faa92 100644 --- a/packages/shared-components/.gitignore +++ b/packages/shared-components/.gitignore @@ -24,3 +24,4 @@ $RECYCLE.BIN/ Thumbs.db UserInterfaceState.xcuserstate .env +api/ diff --git a/packages/shared-components/package-lock.json b/packages/shared-components/package-lock.json index e561737e2..65bd22acf 100644 --- a/packages/shared-components/package-lock.json +++ b/packages/shared-components/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.1", "license": "MIT", "dependencies": { + "@reactivex/rxjs": "^6.6.7", "font-awesome": "^4.7.0", "single-spa": "^6.0.1" }, @@ -1039,6 +1040,22 @@ } } }, + "node_modules/@reactivex/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/@reactivex/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-xZIV2JgHhWoVPm3uVcFbZDRVJfx2hgqmuTX7J4MuKaZ+j5jN29agniCPBwrlCmpA15/zLKcPi7/bogt0ZwOFyA==", + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/@reactivex/rxjs/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", diff --git a/packages/shared-components/package.json b/packages/shared-components/package.json index 95cae22d5..d74f96376 100644 --- a/packages/shared-components/package.json +++ b/packages/shared-components/package.json @@ -54,6 +54,7 @@ "puppeteer": "^21.9.0" }, "dependencies": { + "@reactivex/rxjs": "^6.6.7", "font-awesome": "^4.7.0", "single-spa": "^6.0.1" } diff --git a/packages/shared-components/src/components/assets/graphdb-logo-sq.svg b/packages/shared-components/src/assets/graphdb-logo-sq.svg similarity index 100% rename from packages/shared-components/src/components/assets/graphdb-logo-sq.svg rename to packages/shared-components/src/assets/graphdb-logo-sq.svg diff --git a/packages/shared-components/src/components/assets/graphdb-logo.svg b/packages/shared-components/src/assets/graphdb-logo.svg similarity index 100% rename from packages/shared-components/src/components/assets/graphdb-logo.svg rename to packages/shared-components/src/assets/graphdb-logo.svg diff --git a/packages/shared-components/src/assets/i18n/en.json b/packages/shared-components/src/assets/i18n/en.json new file mode 100644 index 000000000..7e3dee567 --- /dev/null +++ b/packages/shared-components/src/assets/i18n/en.json @@ -0,0 +1,42 @@ +{ + "common.import": "Import", + "menu.rdf.label": "RDF", + "menu.explore.label": "Explore", + "menu.graphs.overview.label": "Graphs overview", + "menu.class.relationships.label": "Class relationships", + "menu.class.hierarchy.label": "Class hierarchy", + "menu.similarity.label": "Similarity", + "menu.sparql.label": "SPARQL", + "menu.monitor.label": "Monitor", + "menu.queries.and.updates.label": "Queries and Updates", + "menu.backup_and_restore.label": "Backup and Restore", + "menu.enableFtsIndex.label": "Enable full-text search (FTS) index", + "menu.ftsIndexes.label": "FTS indexes to build (comma delimited)", + "menu.ftsStringLiteralsIndex.label": "FTS index for xsd:string literals", + "menu.ftsIrisIndex.label": "FTS index for full-text indexing of IRIs", + "menu.ftsDefaultAnalyzer.label": "Analyzer for the default index", + "menu.resources.label": "System", + "menu.setup.label": "Setup", + "menu.lab.label": "Lab", + "menu.ttyg.label": "Talk to Your Graph", + "menu.repositories.label": "Repositories", + "menu.aclmanagement.label": "ACL Management", + "menu.users.and.access.label": "Users and Access", + "menu.my.settings.label": "My Settings", + "menu.cluster.label": "Cluster", + "menu.connectors.label": "Connectors", + "menu.namespaces.label": "Namespaces", + "menu.autocomplete.label": "Autocomplete", + "menu.jdbc.label": "JDBC", + "menu.sparql.template.label": "SPARQL Templates", + "menu.license.label": "License", + "menu.help.label": "Help", + "menu.system.information.label": "System information", + "menu.rest.api.label": "REST API", + "menu.documentation.label": "Documentation", + "menu.tutorials.label": "Tutorials", + "menu.support.label": "Support", + "menu.guides.label": "Interactive guides", + "menu.plugins.label": "Plugins", + "visual.graph.label": "Visual graph" +} diff --git a/packages/shared-components/src/assets/i18n/fr.json b/packages/shared-components/src/assets/i18n/fr.json new file mode 100644 index 000000000..15b90adfb --- /dev/null +++ b/packages/shared-components/src/assets/i18n/fr.json @@ -0,0 +1,45 @@ +{ + "common.import": "Importer", + "menu.rdf.label": "RDF", + "menu.explore.label": "Explorez", + "menu.graphs.overview.label": "Aperçu des graphes", + "menu.class.relationships.label": "Relations entre les classes", + "menu.class.hierarchy.label": "Hiérarchie des classes", + "menu.visual.graph.label": "Graphique visuel", + "menu.similarity.label": "Similarité", + "menu.sparql.label": "SPARQL", + "menu.monitor.label": "Surveiller", + "menu.queries.and.updates.label": "Requêtes et mises à jour", + "menu.backup_and_restore.label": "Sauvegarde et Restauration", + "menu.enableFtsIndex.label": "Activer l'index de recherche plein texte", + "menu.ftsIndexes.label": "Index de recherche en texte intégral à construire (délimités par des virgules)", + "menu.ftsStringLiteralsIndex.label": "Index de recherche en texte intégral pour les littéraux xsd:string", + "menu.ftsIrisIndex.label": "Index de recherche en texte intégral pour l'indexation en texte intégral des IRI", + "menu.ftsDefaultAnalyzer.label": "Analyseur pour l'index par défaut", + "menu.ftsLanguages.label": "Entrez la langue pour la recherche plein texte", + "menu.resources.label": "Système", + "menu.setup.label": "Configurer", + "menu.lab.label": "Laboratoire", + "menu.ttyg.label": "Parlez à votre graphe", + "menu.repositories.label": "Dépôts", + "menu.aclmanagement.label": "Gestion ACL", + "menu.users.and.access.label": "Utilisateurs et accès", + "menu.my.settings.label": "Mes paramètres", + "menu.cluster.label": "Cluster", + "menu.connectors.label": "Connecteurs", + "menu.namespaces.label": "Espaces de noms", + "menu.autocomplete.label": "Autocomplétion", + "menu.rdf.rank.label": "Rang RDF", + "menu.jdbc.label": "JDBC", + "menu.sparql.template.label": "Modèles SPARQL", + "menu.license.label": "Licence", + "menu.help.label": "Aide", + "menu.system.information.label": "Informations sur le système", + "menu.rest.api.label": "API REST", + "menu.documentation.label": "Documentation", + "menu.tutorials.label": "Tutoriels", + "menu.support.label": "Support", + "menu.guides.label": "Guides interactifs", + "menu.plugins.label": "Plugins", + "visual.graph.label": "Graphique visuel" +} diff --git a/packages/shared-components/src/components.d.ts b/packages/shared-components/src/components.d.ts index 9185b2053..1cfda0b8e 100644 --- a/packages/shared-components/src/components.d.ts +++ b/packages/shared-components/src/components.d.ts @@ -6,7 +6,9 @@ */ import { HTMLStencilElement, JSXBase } from "@stencil/core/internal"; import { ExternalMenuModel } from "./components/onto-navbar/menu-model"; +import { TranslationParameter } from "./models/translation/translation-parameter"; export { ExternalMenuModel } from "./components/onto-navbar/menu-model"; +export { TranslationParameter } from "./models/translation/translation-parameter"; export namespace Components { interface OntoFooter { } @@ -15,6 +17,20 @@ export namespace Components { interface OntoNavbar { "menuItems": ExternalMenuModel; } + /** + * The purpose of this component is to display translated literals in the DOM. A Stencil component re-renders when a prop or state changes, + * but it may not re-render when the language changes. In such cases, this component should be used. It handles language change events + * and re-translates the passed language and translation parameters. + * Example of usage: + * + * + * + * + * + * + * ; "onto-header": LocalJSX.OntoHeader & JSXBase.HTMLAttributes; "onto-navbar": LocalJSX.OntoNavbar & JSXBase.HTMLAttributes; + /** + * The purpose of this component is to display translated literals in the DOM. A Stencil component re-renders when a prop or state changes, + * but it may not re-render when the language changes. In such cases, this component should be used. It handles language change events + * and re-translates the passed language and translation parameters. + * Example of usage: + * + * + * ; } } } diff --git a/packages/shared-components/src/components/onto-header/onto-header.tsx b/packages/shared-components/src/components/onto-header/onto-header.tsx index 94f72db3d..0816d6e90 100644 --- a/packages/shared-components/src/components/onto-header/onto-header.tsx +++ b/packages/shared-components/src/components/onto-header/onto-header.tsx @@ -1,4 +1,5 @@ import { Component, Host, h } from '@stencil/core'; +import {ServiceProvider, LanguageService} from "@ontotext/workbench-api"; @Component({ tag: 'onto-header', @@ -6,13 +7,33 @@ import { Component, Host, h } from '@stencil/core'; shadow: false, }) export class OntoHeader { + + private languageService: LanguageService; + + // TODO remove this when implement language selector + private locale = 'en' + + constructor() { + this.languageService = ServiceProvider.get(LanguageService); + } + + // TODO remove this when implement language selector + private onLanguageChanged(): void { + if (this.locale === 'en') { + this.locale = 'fr'; + } else { + this.locale = 'en'; + } + this.languageService.changeLanguage(this.locale); + } + render() { return (
🔍
TestRepo ⌄
-
EN ⌄
+
this.onLanguageChanged()}>EN ⌄
); diff --git a/packages/shared-components/src/components/onto-navbar/onto-navbar.tsx b/packages/shared-components/src/components/onto-navbar/onto-navbar.tsx index d25fc9479..db53885e8 100644 --- a/packages/shared-components/src/components/onto-navbar/onto-navbar.tsx +++ b/packages/shared-components/src/components/onto-navbar/onto-navbar.tsx @@ -101,14 +101,14 @@ export class OntoNavbar {