diff --git a/api-extractor/carbonio-shell-ui.api.md b/api-extractor/carbonio-shell-ui.api.md
index 9230bc3bf..d26641f4d 100644
--- a/api-extractor/carbonio-shell-ui.api.md
+++ b/api-extractor/carbonio-shell-ui.api.md
@@ -4,15 +4,12 @@
```ts
-///
-
import { Action as Action_2 } from '../../lib';
import { ActionFactory as ActionFactory_2 } from '../../types/integrations';
import { AnyFunction as AnyFunction_2 } from '../../utils/typeUtils';
import { BadgeInfo as BadgeInfo_2 } from '../../lib';
import type { CaptureOptions } from 'posthog-js';
import { CarbonioModule as CarbonioModule_2 } from '../../types/apps';
-import type { ChipItem } from '@zextras/carbonio-design-system';
import type { ComponentType } from 'react';
import type { DefaultTheme } from 'styled-components';
import { DropdownItem } from '@zextras/carbonio-design-system';
@@ -197,10 +194,10 @@ export const addBoard: AppDependantExports['addBoard'];
// Warning: (ae-forgotten-export) The symbol "BoardState" needs to be exported by the entry point lib.d.ts
//
// @public (undocumented)
-const addBoard_2: (app: string) => (board: Omit, "id" | "app" | "icon"> & {
- id?: string | undefined;
- icon?: string | undefined;
-}, expanded?: BoardState['expanded']) => Board;
+const addBoard_2: (app: string) => (board: Omit, "app" | "icon" | "id"> & {
+ id?: string;
+ icon?: string;
+}, expanded?: BoardState["expanded"]) => Board;
// @public (undocumented)
export const addBoardView: AppDependantExports['addBoardView'];
@@ -211,9 +208,6 @@ export const addPrimaryAccessoryView: AppDependantExports['addPrimaryAccessoryVi
// @public (undocumented)
export const addRoute: AppDependantExports['addRoute'];
-// @public (undocumented)
-export const addSearchView: AppDependantExports['addSearchView'];
-
// @public (undocumented)
export const addSecondaryAccessoryView: AppDependantExports['addSecondaryAccessoryView'];
@@ -238,8 +232,6 @@ type AppActions = {
removeBoardView: (id: string) => void;
addSettingsView: (data: SettingsView) => string;
removeSettingsView: (id: string) => void;
- addSearchView: (data: SearchView) => string;
- removeSearchView: (id: string) => void;
addUtilityView: (data: UtilityView) => string;
removeUtilityView: (id: string) => void;
addPrimaryAccessoryView: (data: PrimaryAccessoryView) => string;
@@ -268,7 +260,6 @@ type AppDependantExports = {
addRoute: (data: Partial) => ReturnType;
addBoardView: (data: Omit) => ReturnType;
addSettingsView: (data: Partial) => ReturnType;
- addSearchView: (data: Partial) => ReturnType;
addUtilityView: (data: Partial) => ReturnType;
addPrimaryAccessoryView: (data: Partial) => ReturnType;
addSecondaryAccessoryView: (data: Partial) => ReturnType;
@@ -586,10 +577,10 @@ type GeneralizedTimeObj = {
};
// @public (undocumented)
-export const getAction: (type: string, id: string, target?: T | undefined) => [Action | undefined, boolean];
+export const getAction: (type: string, id: string, target?: T) => [Action | undefined, boolean];
// @public (undocumented)
-export const getActions: (context: TContext, type: string) => TAction[];
+export const getActions: (context: TContext, type: string) => Array;
// @public (undocumented)
export const getApp: AppDependantExports['getApp'];
@@ -628,7 +619,7 @@ export const getCurrentRoute: () => AppRoute | undefined;
//
// @public (undocumented)
const getEditSettingsForApp: (app: string) => (mods: Mods) => Promise;
// @public (undocumented)
@@ -638,16 +629,16 @@ export const getI18n: AppDependantExports['getI18n'];
const getI18n_2: (app: string) => () => i18n;
// @public (undocumented)
-export const getIntegratedComponent: (id: string) => [React_2.FunctionComponent>, boolean];
+export const getIntegratedComponent: = React_2.ComponentType>>(id: string) => [TComponent, boolean];
// @public (undocumented)
-export const getIntegratedFunction: (id: string) => [AnyFunction, boolean];
+export const getIntegratedFunction: (id: string) => [TFunction, boolean];
// @public (undocumented)
export const getNotificationManager: () => INotificationManager;
// @public (undocumented)
-const getSoapFetch: (app: string) => >(api: string, body: Request_1, otherAccount?: string, signal?: AbortSignal) => Promise;
+const getSoapFetch: (app: string) => >(api: string, body: Request, otherAccount?: string, signal?: AbortSignal) => Promise;
// @public (undocumented)
const getTFunction: (app: string) => TFunction;
@@ -671,7 +662,7 @@ export const getUserSetting: (...path: Array) => string | T;
export const getUserSettings: () => AccountSettings;
// @public (undocumented)
-const getXmlSoapFetch: (app: string) => >(api: string, body: Request_1, otherAccount?: string) => Promise;
+const getXmlSoapFetch: (app: string) => >(api: string, body: Request, otherAccount?: string) => Promise;
// @public (undocumented)
export const goBackHistory: () => void;
@@ -810,9 +801,6 @@ export const LOCAL_STORAGE_BOARD_SIZE = "board_size";
// @public (undocumented)
export const LOCAL_STORAGE_LAST_PRIMARY_KEY = "config";
-// @public (undocumented)
-export const LOCAL_STORAGE_SEARCH_KEY = "search_suggestions";
-
// @public (undocumented)
export const LOCAL_STORAGE_SETTINGS_KEY = "settings";
@@ -944,17 +932,6 @@ type PropsMods = Record void;
-// @public (undocumented)
-export type QueryChip = ChipItem & QueryItem;
-
-// @public (undocumented)
-export interface QueryItem {
- // (undocumented)
- app?: string;
- // (undocumented)
- value?: string;
-}
-
// @public (undocumented)
interface RawSoapContext {
// (undocumented)
@@ -1002,32 +979,32 @@ type RawSoapNotify = {
};
// @public (undocumented)
-export const registerActions: (...items: {
+export const registerActions: (...items: Array<{
id: string;
action: ActionFactory_2;
type: string;
-}[]) => void;
+}>) => void;
// @public (undocumented)
export const registerComponents: AppDependantExports['registerComponents'];
// @public (undocumented)
-export const registerFunctions: (...items: {
+export const registerFunctions: (...items: Array<{
id: string;
fn: AnyFunction_2;
-}[]) => void;
+}>) => void;
// @public (undocumented)
-export const removeActions: (...ids: string[]) => void;
+export const removeActions: (...ids: Array) => void;
// @public (undocumented)
export const removeBoardView: (id: string) => void;
// @public (undocumented)
-export const removeComponents: (...ids: string[]) => void;
+export const removeComponents: (...ids: Array) => void;
// @public (undocumented)
-export const removeFunctions: (...ids: string[]) => void;
+export const removeFunctions: (...ids: Array) => void;
// @public (undocumented)
export const removePrimaryAccessoryView: (id: string) => void;
@@ -1035,9 +1012,6 @@ export const removePrimaryAccessoryView: (id: string) => void;
// @public (undocumented)
export const removeRoute: (id: string) => void;
-// @public (undocumented)
-export const removeSearchView: (id: string) => void;
-
// @public (undocumented)
export const removeSecondaryAccessoryView: (id: string) => void;
@@ -1088,9 +1062,6 @@ interface RouteLeavingGuardProps {
when?: boolean;
}
-// @public (undocumented)
-export const runSearch: (query: Array, module: string) => void;
-
// @public (undocumented)
export const SCALING_LIMIT: {
readonly width: 1400;
@@ -1116,9 +1087,6 @@ export const SCALING_OPTIONS: readonly [{
readonly label: "xl";
}];
-// @public (undocumented)
-export const SEARCH_APP_ID = "search";
-
// @public (undocumented)
type SearchFolderFields = {
query?: string;
@@ -1126,22 +1094,6 @@ type SearchFolderFields = {
types?: string;
};
-// @public (undocumented)
-type SearchView = CarbonioView & {
- icon: string;
- label: string;
- position: number;
-};
-
-// @public (undocumented)
-export type SearchViewProps = {
- useQuery: () => [QueryChip[], Function];
- ResultsHeader: React_2.ComponentType<{
- label: string;
- }>;
- useDisableSearch: () => [boolean, Function];
-};
-
// @public (undocumented)
type SecondaryAccessoryView = CarbonioAccessoryView;
@@ -1405,10 +1357,10 @@ type UpdateSettingsParams = {
export const upsertApp: (app: Pick) => void;
// @public (undocumented)
-export const useAction: (type: string, id: string, target?: T | undefined) => [Action | undefined, boolean];
+export const useAction: (type: string, id: string, target?: T) => [Action | undefined, boolean];
// @public (undocumented)
-export const useActions: (context: TContext, type: string) => TAction[];
+export const useActions: (context: TContext, type: string) => Array;
// @public (undocumented)
export const useApp: AppDependantExports['useApp'];
@@ -1438,10 +1390,10 @@ export const useCurrentRoute: () => AppRoute | undefined;
export function useGoBackHistoryCallback(): () => void;
// @public (undocumented)
-export const useIntegratedComponent: (id: string) => [React_2.FunctionComponent>, boolean];
+export const useIntegratedComponent: = React_2.ComponentType>>(id: string) => [TComponent, boolean];
// @public (undocumented)
-export const useIntegratedFunction: (id: string) => [AnyFunction, boolean];
+export const useIntegratedFunction: (id: string) => [TFunction, boolean];
// @public
export function useIsCarbonioCE(): boolean | undefined;
@@ -1455,13 +1407,13 @@ export function useLocalStorage(key: string, initialValue: T, options?: Local
export const useNotify: () => SoapNotify[];
// @public (undocumented)
-export const usePushHistoryCallback: () => (params: HistoryParams) => void;
+export const usePushHistoryCallback: () => ((params: HistoryParams) => void);
// @public (undocumented)
export const useRefresh: () => SoapRefresh;
// @public (undocumented)
-export const useReplaceHistoryCallback: () => (params: HistoryParams) => void;
+export const useReplaceHistoryCallback: () => ((params: HistoryParams) => void);
// Warning: (ae-forgotten-export) The symbol "Tracker" needs to be exported by the entry point lib.d.ts
//
@@ -1518,18 +1470,18 @@ interface ZimletProp {
// Warnings were encountered during analysis:
//
// lib/boot/app/app-dependant-exports.d.ts:11:5 - (ae-forgotten-export) The symbol "AppActions" needs to be exported by the entry point lib.d.ts
-// lib/boot/app/app-dependant-exports.d.ts:19:5 - (ae-forgotten-export) The symbol "IntegrationActions" needs to be exported by the entry point lib.d.ts
-// lib/boot/app/app-dependant-exports.d.ts:20:5 - (ae-forgotten-export) The symbol "getEditSettingsForApp" needs to be exported by the entry point lib.d.ts
-// lib/boot/app/app-dependant-exports.d.ts:21:5 - (ae-forgotten-export) The symbol "getI18n_2" needs to be exported by the entry point lib.d.ts
-// lib/boot/app/app-dependant-exports.d.ts:22:5 - (ae-forgotten-export) The symbol "getTFunction" needs to be exported by the entry point lib.d.ts
-// lib/boot/app/app-dependant-exports.d.ts:23:5 - (ae-forgotten-export) The symbol "getSoapFetch" needs to be exported by the entry point lib.d.ts
-// lib/boot/app/app-dependant-exports.d.ts:24:5 - (ae-forgotten-export) The symbol "getXmlSoapFetch" needs to be exported by the entry point lib.d.ts
-// lib/boot/app/app-dependant-exports.d.ts:25:5 - (ae-forgotten-export) The symbol "getAppContextHook" needs to be exported by the entry point lib.d.ts
-// lib/boot/app/app-dependant-exports.d.ts:26:5 - (ae-forgotten-export) The symbol "getAppContext_2" needs to be exported by the entry point lib.d.ts
-// lib/boot/app/app-dependant-exports.d.ts:27:5 - (ae-forgotten-export) The symbol "getAppHook" needs to be exported by the entry point lib.d.ts
-// lib/boot/app/app-dependant-exports.d.ts:28:5 - (ae-forgotten-export) The symbol "getApp_2" needs to be exported by the entry point lib.d.ts
-// lib/boot/app/app-dependant-exports.d.ts:29:5 - (ae-forgotten-export) The symbol "addBoard_2" needs to be exported by the entry point lib.d.ts
-// lib/boot/app/app-dependant-exports.d.ts:33:5 - (ae-forgotten-export) The symbol "ContextBridgeState" needs to be exported by the entry point lib.d.ts
+// lib/boot/app/app-dependant-exports.d.ts:18:5 - (ae-forgotten-export) The symbol "IntegrationActions" needs to be exported by the entry point lib.d.ts
+// lib/boot/app/app-dependant-exports.d.ts:19:5 - (ae-forgotten-export) The symbol "getEditSettingsForApp" needs to be exported by the entry point lib.d.ts
+// lib/boot/app/app-dependant-exports.d.ts:20:5 - (ae-forgotten-export) The symbol "getI18n_2" needs to be exported by the entry point lib.d.ts
+// lib/boot/app/app-dependant-exports.d.ts:21:5 - (ae-forgotten-export) The symbol "getTFunction" needs to be exported by the entry point lib.d.ts
+// lib/boot/app/app-dependant-exports.d.ts:22:5 - (ae-forgotten-export) The symbol "getSoapFetch" needs to be exported by the entry point lib.d.ts
+// lib/boot/app/app-dependant-exports.d.ts:23:5 - (ae-forgotten-export) The symbol "getXmlSoapFetch" needs to be exported by the entry point lib.d.ts
+// lib/boot/app/app-dependant-exports.d.ts:24:5 - (ae-forgotten-export) The symbol "getAppContextHook" needs to be exported by the entry point lib.d.ts
+// lib/boot/app/app-dependant-exports.d.ts:25:5 - (ae-forgotten-export) The symbol "getAppContext_2" needs to be exported by the entry point lib.d.ts
+// lib/boot/app/app-dependant-exports.d.ts:26:5 - (ae-forgotten-export) The symbol "getAppHook" needs to be exported by the entry point lib.d.ts
+// lib/boot/app/app-dependant-exports.d.ts:27:5 - (ae-forgotten-export) The symbol "getApp_2" needs to be exported by the entry point lib.d.ts
+// lib/boot/app/app-dependant-exports.d.ts:28:5 - (ae-forgotten-export) The symbol "addBoard_2" needs to be exported by the entry point lib.d.ts
+// lib/boot/app/app-dependant-exports.d.ts:32:5 - (ae-forgotten-export) The symbol "ContextBridgeState" needs to be exported by the entry point lib.d.ts
// lib/network/edit-settings.d.ts:3:5 - (ae-forgotten-export) The symbol "ModifyPropertiesResponse" needs to be exported by the entry point lib.d.ts
// lib/network/edit-settings.d.ts:4:5 - (ae-forgotten-export) The symbol "ModifyPrefsResponse" needs to be exported by the entry point lib.d.ts
// lib/network/edit-settings.d.ts:5:5 - (ae-forgotten-export) The symbol "ModifyIdentityResponse" needs to be exported by the entry point lib.d.ts
@@ -1538,14 +1490,13 @@ interface ZimletProp {
// lib/network/edit-settings.d.ts:8:5 - (ae-forgotten-export) The symbol "RevokeRightsResponse" needs to be exported by the entry point lib.d.ts
// lib/network/edit-settings.d.ts:9:5 - (ae-forgotten-export) The symbol "GrantRightsResponse" needs to be exported by the entry point lib.d.ts
// lib/settings/components/settings-header.d.ts:5:5 - (ae-forgotten-export) The symbol "RouteLeavingGuardProps" needs to be exported by the entry point lib.d.ts
-// lib/store/app/store.d.ts:23:5 - (ae-forgotten-export) The symbol "CarbonioModule" needs to be exported by the entry point lib.d.ts
-// lib/store/app/store.d.ts:25:5 - (ae-forgotten-export) The symbol "AppRouteDescriptor" needs to be exported by the entry point lib.d.ts
-// lib/store/app/store.d.ts:29:5 - (ae-forgotten-export) The symbol "BoardView" needs to be exported by the entry point lib.d.ts
-// lib/store/app/store.d.ts:31:5 - (ae-forgotten-export) The symbol "SettingsView" needs to be exported by the entry point lib.d.ts
-// lib/store/app/store.d.ts:33:5 - (ae-forgotten-export) The symbol "SearchView" needs to be exported by the entry point lib.d.ts
-// lib/store/app/store.d.ts:35:5 - (ae-forgotten-export) The symbol "UtilityView" needs to be exported by the entry point lib.d.ts
-// lib/store/app/store.d.ts:37:5 - (ae-forgotten-export) The symbol "PrimaryAccessoryView" needs to be exported by the entry point lib.d.ts
-// lib/store/app/store.d.ts:39:5 - (ae-forgotten-export) The symbol "SecondaryAccessoryView" needs to be exported by the entry point lib.d.ts
+// lib/store/app/store.d.ts:22:5 - (ae-forgotten-export) The symbol "CarbonioModule" needs to be exported by the entry point lib.d.ts
+// lib/store/app/store.d.ts:24:5 - (ae-forgotten-export) The symbol "AppRouteDescriptor" needs to be exported by the entry point lib.d.ts
+// lib/store/app/store.d.ts:28:5 - (ae-forgotten-export) The symbol "BoardView" needs to be exported by the entry point lib.d.ts
+// lib/store/app/store.d.ts:30:5 - (ae-forgotten-export) The symbol "SettingsView" needs to be exported by the entry point lib.d.ts
+// lib/store/app/store.d.ts:32:5 - (ae-forgotten-export) The symbol "UtilityView" needs to be exported by the entry point lib.d.ts
+// lib/store/app/store.d.ts:34:5 - (ae-forgotten-export) The symbol "PrimaryAccessoryView" needs to be exported by the entry point lib.d.ts
+// lib/store/app/store.d.ts:36:5 - (ae-forgotten-export) The symbol "SecondaryAccessoryView" needs to be exported by the entry point lib.d.ts
// lib/store/context-bridge.d.ts:4:5 - (ae-forgotten-export) The symbol "PackageDependentFunction" needs to be exported by the entry point lib.d.ts
// lib/store/context-bridge.d.ts:5:5 - (ae-forgotten-export) The symbol "AnyFunction" needs to be exported by the entry point lib.d.ts
// lib/store/integrations/store.d.ts:25:9 - (ae-forgotten-export) The symbol "ActionFactory" needs to be exported by the entry point lib.d.ts
@@ -1558,13 +1509,13 @@ interface ZimletProp {
// lib/types/account/index.d.ts:140:5 - (ae-forgotten-export) The symbol "AccountRightTargetEmail" needs to be exported by the entry point lib.d.ts
// lib/types/account/index.d.ts:145:9 - (ae-forgotten-export) The symbol "AccountRightName" needs to be exported by the entry point lib.d.ts
// lib/types/account/index.d.ts:146:9 - (ae-forgotten-export) The symbol "AccountRightTarget" needs to be exported by the entry point lib.d.ts
-// lib/types/apps/index.d.ts:68:5 - (ae-forgotten-export) The symbol "PanelMode" needs to be exported by the entry point lib.d.ts
+// lib/types/apps/index.d.ts:60:5 - (ae-forgotten-export) The symbol "PanelMode" needs to be exported by the entry point lib.d.ts
// lib/types/misc/index.d.ts:73:9 - (ae-forgotten-export) The symbol "SoapPolicy" needs to be exported by the entry point lib.d.ts
// lib/types/misc/index.d.ts:92:5 - (ae-forgotten-export) The symbol "FolderView" needs to be exported by the entry point lib.d.ts
// lib/types/misc/index.d.ts:108:5 - (ae-forgotten-export) The symbol "Meta" needs to be exported by the entry point lib.d.ts
// lib/types/misc/index.d.ts:112:5 - (ae-forgotten-export) The symbol "SoapRetentionPolicy" needs to be exported by the entry point lib.d.ts
// lib/types/misc/index.d.ts:126:5 - (ae-forgotten-export) The symbol "SortBy" needs to be exported by the entry point lib.d.ts
-// lib/types/network/index.d.ts:108:5 - (ae-forgotten-export) The symbol "AccountACEInfo" needs to be exported by the entry point lib.d.ts
+// lib/types/network/index.d.ts:107:5 - (ae-forgotten-export) The symbol "AccountACEInfo" needs to be exported by the entry point lib.d.ts
// lib/types/network/soap.d.ts:11:5 - (ae-forgotten-export) The symbol "NameSpace" needs to be exported by the entry point lib.d.ts
// lib/types/network/soap.d.ts:69:5 - (ae-forgotten-export) The symbol "SoapSearchFolder" needs to be exported by the entry point lib.d.ts
// lib/types/network/soap.d.ts:91:9 - (ae-forgotten-export) The symbol "Tag" needs to be exported by the entry point lib.d.ts
diff --git a/package-lock.json b/package-lock.json
index 9edb4191d..47a615222 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -13,11 +13,12 @@
"@sentry/browser": "^7.103.0",
"@tinymce/tinymce-react": "^4.3.2",
"@zextras/carbonio-design-system": "9.0.0-devel.7",
- "@zextras/carbonio-ui-preview": "^3.1.0-devel.2",
+ "@zextras/carbonio-ui-preview": "^3.1.0",
"darkreader": "^4.9.79",
"date-fns": "^4.1.0",
"history": "^5.3.0",
"i18next": "^22.5.1",
+ "i18next-chained-backend": "^4.6.2",
"i18next-http-backend": "^2.5.0",
"immer": "^10.0.3",
"lodash": "^4.17.21",
@@ -49,14 +50,15 @@
"@tsconfig/node18": "^18.2.4",
"@types/jest": "^29.5.12",
"@types/lodash": "^4.17.4",
- "@types/node": "^18.19.54",
+ "@types/node": "^18.19.67",
"@types/react": "^18.3.11",
"@types/react-dom": "^18.3.0",
"@types/react-router-dom": "^5.3.3",
"@types/ua-parser-js": "^0.7.39",
"@types/webpack": "^5.28.5",
"@types/webpack-env": "^1.18.4",
- "@zextras/carbonio-ui-configs": "^1.0.3",
+ "@zextras/carbonio-search-ui": "github:zextras/carbonio-search-ui#devel",
+ "@zextras/carbonio-ui-configs": "^2.0.1",
"@zextras/carbonio-ui-sdk": "^1.7.10",
"autoprefixer": "^10.4.17",
"babel-loader": "^9.2.1",
@@ -92,7 +94,7 @@
},
"peerDependencies": {
"@zextras/carbonio-design-system": "9.0.0-devel.7",
- "@zextras/carbonio-ui-preview": "3.1.0-devel.2",
+ "@zextras/carbonio-ui-preview": "^3.1.0",
"core-js": "^3.39.0",
"lodash": "^4.17.21",
"react": "^18.3.1",
@@ -2357,6 +2359,13 @@
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz",
"integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA=="
},
+ "node_modules/@emotion/stylis": {
+ "version": "0.8.5",
+ "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz",
+ "integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@emotion/unitless": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz",
@@ -2378,10 +2387,11 @@
}
},
"node_modules/@eslint-community/regexpp": {
- "version": "4.10.0",
- "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz",
- "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==",
+ "version": "4.12.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz",
+ "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": "^12.0.0 || ^14.0.0 || >=16.0.0"
}
@@ -3880,6 +3890,17 @@
"integrity": "sha512-2LuNTFBIO0m7kKIQvvPHN6UE63VjpmL9rnEEaOOaiSPbZK+zUOYIzBAWcED+3XYzhYsd/0mD57VdxAEqqV52CQ==",
"dev": true
},
+ "node_modules/@popperjs/core": {
+ "version": "2.11.8",
+ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
+ "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/popperjs"
+ }
+ },
"node_modules/@remix-run/router": {
"version": "1.21.0",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.21.0.tgz",
@@ -5006,10 +5027,11 @@
}
},
"node_modules/@types/node": {
- "version": "18.19.54",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.54.tgz",
- "integrity": "sha512-+BRgt0G5gYjTvdLac9sIeE0iZcJxi4Jc4PV5EUzqi+88jmQLr+fRZdv2tCTV7IHKSGxM6SaLoOXQWWUiLUItMw==",
+ "version": "18.19.67",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.67.tgz",
+ "integrity": "sha512-wI8uHusga+0ZugNp0Ol/3BqQfEcCCNfojtO6Oou9iVNGPTL6QNSdnUdqq85fRgIorLhLMuPIKpsN98QE9Nh+KQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"undici-types": "~5.26.4"
}
@@ -5733,11 +5755,247 @@
"styled-components": "^6.1.13"
}
},
+ "node_modules/@zextras/carbonio-search-ui": {
+ "version": "0.0.0",
+ "resolved": "git+ssh://git@github.com/zextras/carbonio-search-ui.git#44c387d8c6a7c0f985191e4b7c265a56d5b38076",
+ "dev": true,
+ "license": "AGPL-3.0-only",
+ "dependencies": {
+ "@zextras/carbonio-design-system": "9.0.0-devel.12",
+ "@zextras/carbonio-shell-ui": "devel",
+ "core-js": "^3.39.0",
+ "i18next": "^22.5.1",
+ "immer": "^10.1.1",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
+ "react-i18next": "^12.3.1",
+ "react-router-dom": "^5.3.4",
+ "styled-components": "^6.1.13",
+ "zustand": "^5.0.2"
+ }
+ },
+ "node_modules/@zextras/carbonio-search-ui/node_modules/@zextras/carbonio-design-system": {
+ "version": "9.0.0-devel.12",
+ "resolved": "https://registry.npmjs.org/@zextras/carbonio-design-system/-/carbonio-design-system-9.0.0-devel.12.tgz",
+ "integrity": "sha512-syGN2E5pdhqTzmuyC5SODbbNp/4y33/ZHE80RYfLUAHitWo0QEJEJ9Cu8dMnXnBzGpC92B2M5u5N6m0bHbvm6Q==",
+ "dev": true,
+ "license": "AGPL-3.0-only",
+ "dependencies": {
+ "@floating-ui/dom": "^1.6.11",
+ "core-js": "^3.38.1",
+ "darkreader": "^4.9.92",
+ "polished": "^4.3.1",
+ "react-datepicker": "^7.5.0"
+ },
+ "peerDependencies": {
+ "date-fns": "^4.1.0",
+ "lodash": "^4.17.21",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
+ "styled-components": "^6.1.13"
+ }
+ },
+ "node_modules/@zextras/carbonio-search-ui/node_modules/zustand": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.2.tgz",
+ "integrity": "sha512-8qNdnJVJlHlrKXi50LDqqUNmUbuBjoKLrYQBnoChIbVph7vni+sY+YpvdjXG9YLd/Bxr6scMcR+rm5H3aSqPaw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.20.0"
+ },
+ "peerDependencies": {
+ "@types/react": ">=18.0.0",
+ "immer": ">=9.0.6",
+ "react": ">=18.0.0",
+ "use-sync-external-store": ">=1.2.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "immer": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ },
+ "use-sync-external-store": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@zextras/carbonio-shell-ui": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/@zextras/carbonio-shell-ui/-/carbonio-shell-ui-9.0.0.tgz",
+ "integrity": "sha512-GL3t4n0H19zMmN5idDRyfwbBJPjSYf13TxR16PLx6gi+lli0ME98HoZ/xsZX1v8U1HZ0z2wDs6Q/ZDEkgpoK3g==",
+ "dev": true,
+ "license": "AGPL-3.0-only",
+ "dependencies": {
+ "@fontsource/roboto": "^5.0.8",
+ "@sentry/browser": "^7.103.0",
+ "@tinymce/tinymce-react": "^4.3.2",
+ "@zextras/carbonio-design-system": "^8.1.0",
+ "@zextras/carbonio-ui-preview": "^3.1.0",
+ "darkreader": "^4.9.79",
+ "history": "^5.3.0",
+ "i18next": "^22.5.1",
+ "i18next-http-backend": "^2.5.0",
+ "immer": "^10.0.3",
+ "lodash": "^4.17.21",
+ "posthog-js": "^1.163.1",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
+ "react-i18next": "^12.3.1",
+ "react-router-dom": "^5.3.4",
+ "react-router-dom-v5-compat": "^6.28.0",
+ "styled-components": "^5.3.11",
+ "tinymce": "^6.8.4",
+ "ua-parser-js": "^1.0.37",
+ "zustand": "^4.5.1"
+ },
+ "engines": {
+ "node": "v18",
+ "npm": "v10"
+ },
+ "peerDependencies": {
+ "@zextras/carbonio-design-system": "^8.1.0",
+ "@zextras/carbonio-ui-preview": "^3.1.0",
+ "core-js": "^3.31.1",
+ "lodash": "^4.17.21",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
+ "react-i18next": "^12.3.1",
+ "react-router-dom": "^5.3.4",
+ "styled-components": "^5.3.11"
+ },
+ "peerDependenciesMeta": {
+ "@zextras/carbonio-design-system": {
+ "optional": true
+ },
+ "@zextras/carbonio-ui-preview": {
+ "optional": true
+ },
+ "lodash": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ },
+ "react-dom": {
+ "optional": true
+ },
+ "react-i18next": {
+ "optional": true
+ },
+ "react-router-dom": {
+ "optional": true
+ },
+ "styled-components": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@zextras/carbonio-shell-ui/node_modules/@emotion/unitless": {
+ "version": "0.7.5",
+ "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz",
+ "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@zextras/carbonio-shell-ui/node_modules/@zextras/carbonio-design-system": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/@zextras/carbonio-design-system/-/carbonio-design-system-8.1.0.tgz",
+ "integrity": "sha512-PxiPPetc3YCKr7NsxiM83TWZhthdCsUkg6r3Njc5zOQVIE0SAMbv4fjDcPMTtDnqx8TV9e0EVZtGA0TZADmnaQ==",
+ "dev": true,
+ "license": "AGPL-3.0-only",
+ "dependencies": {
+ "@floating-ui/dom": "^1.5.3",
+ "core-js": "^3.38.1",
+ "darkreader": "^4.9.58",
+ "polished": "^4.2.2",
+ "react-datepicker": "^4.25.0"
+ },
+ "peerDependencies": {
+ "lodash": "^4.17.21",
+ "react": "^18.0.0",
+ "react-dom": "^18.0.0",
+ "styled-components": "^5.3.11"
+ }
+ },
+ "node_modules/@zextras/carbonio-shell-ui/node_modules/date-fns": {
+ "version": "2.30.0",
+ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz",
+ "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.21.0"
+ },
+ "engines": {
+ "node": ">=0.11"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/date-fns"
+ }
+ },
+ "node_modules/@zextras/carbonio-shell-ui/node_modules/react-datepicker": {
+ "version": "4.25.0",
+ "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-4.25.0.tgz",
+ "integrity": "sha512-zB7CSi44SJ0sqo8hUQ3BF1saE/knn7u25qEMTO1CQGofY1VAKahO8k9drZtp0cfW1DMfoYLR3uSY1/uMvbEzbg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@popperjs/core": "^2.11.8",
+ "classnames": "^2.2.6",
+ "date-fns": "^2.30.0",
+ "prop-types": "^15.7.2",
+ "react-onclickoutside": "^6.13.0",
+ "react-popper": "^2.3.0"
+ },
+ "peerDependencies": {
+ "react": "^16.9.0 || ^17 || ^18",
+ "react-dom": "^16.9.0 || ^17 || ^18"
+ }
+ },
+ "node_modules/@zextras/carbonio-shell-ui/node_modules/styled-components": {
+ "version": "5.3.11",
+ "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.11.tgz",
+ "integrity": "sha512-uuzIIfnVkagcVHv9nE0VPlHPSCmXIUGKfJ42LNjxCCTDTL5sgnJ8Z7GZBq0EnLYGln77tPpEpExt2+qa+cZqSw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.0.0",
+ "@babel/traverse": "^7.4.5",
+ "@emotion/is-prop-valid": "^1.1.0",
+ "@emotion/stylis": "^0.8.4",
+ "@emotion/unitless": "^0.7.4",
+ "babel-plugin-styled-components": ">= 1.12.0",
+ "css-to-react-native": "^3.0.0",
+ "hoist-non-react-statics": "^3.0.0",
+ "shallowequal": "^1.1.0",
+ "supports-color": "^5.5.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/styled-components"
+ },
+ "peerDependencies": {
+ "react": ">= 16.8.0",
+ "react-dom": ">= 16.8.0",
+ "react-is": ">= 16.8.0"
+ }
+ },
"node_modules/@zextras/carbonio-ui-configs": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/@zextras/carbonio-ui-configs/-/carbonio-ui-configs-1.0.3.tgz",
- "integrity": "sha512-TcIUjhwd/s2/YyWbpdNFKe1m7VNo3QOAnJuYDNWM7P1t9iQ6kI2hUO+UJfN/UVpM2OaCfJtlInRJ1U4LeT/msQ==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@zextras/carbonio-ui-configs/-/carbonio-ui-configs-2.0.1.tgz",
+ "integrity": "sha512-oSIp/EDtp4/tVWITJvKqaYwuh7hxNNyxcCJ3A90gBp29zJvxzO9jNegIeQLZQSxb8jO32ZWBxs6abTVMhVoQWA==",
"dev": true,
+ "license": "AGPL-3.0-only",
"dependencies": {
"@typescript-eslint/eslint-plugin": "^6.3.0",
"@typescript-eslint/parser": "^6.3.0",
@@ -5753,11 +6011,14 @@
"eslint-plugin-react": "^7.33.1",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-sonarjs": "^0.21.0",
- "eslint-plugin-testing-library": "^6.1.0",
+ "eslint-plugin-testing-library": "^7.0.0",
"eslint-plugin-unused-imports": "^3.0.0",
"prettier": "^3.0.1",
"prettier-eslint": "^16.1.2",
- "typescript": "^5.1.6"
+ "typescript": "^5.7.2"
+ },
+ "peerDependencies": {
+ "typescript": "^5.4"
}
},
"node_modules/@zextras/carbonio-ui-preview": {
@@ -7163,6 +7424,13 @@
"integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==",
"dev": true
},
+ "node_modules/classnames": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
+ "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/clean-css": {
"version": "5.3.3",
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz",
@@ -7644,10 +7912,11 @@
}
},
"node_modules/core-js": {
- "version": "3.38.1",
- "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.38.1.tgz",
- "integrity": "sha512-OP35aUorbU3Zvlx7pjsFdu1rGNnD4pgw/CWoYzRY3t2EzoVT7shKHY1dlAy3f41cGIO7ZDPQimhGFTlEYkG/Hw==",
+ "version": "3.39.0",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.39.0.tgz",
+ "integrity": "sha512-raM0ew0/jJUqkJ0E6e8UDtl+y/7ktFivgWvqw8dNSQeNWoSDLvQ1H/RN3aPXB9tBd4/FhyR4RDPGhsNIMsAn7g==",
"hasInstallScript": true,
+ "license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/core-js"
@@ -9538,6 +9807,7 @@
"resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-0.21.0.tgz",
"integrity": "sha512-oezUDfFT5S6j3rQheZ4DLPrbetPmMS7zHIKWGHr0CM3g5JgyZroz1FpIKa4jV83NsGpmgIeagpokWDKIJzRQmw==",
"dev": true,
+ "license": "LGPL-3.0",
"engines": {
"node": ">=14"
},
@@ -9546,32 +9816,35 @@
}
},
"node_modules/eslint-plugin-testing-library": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-6.2.0.tgz",
- "integrity": "sha512-+LCYJU81WF2yQ+Xu4A135CgK8IszcFcyMF4sWkbiu6Oj+Nel0TrkZq/HvDw0/1WuO3dhDQsZA/OpEMGd0NfcUw==",
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-7.1.1.tgz",
+ "integrity": "sha512-nszC833aZPwB6tik1nMkbFqmtgIXTT0sfJEYs0zMBKMlkQ4to2079yUV96SvmLh00ovSBJI4pgcBC1TiIP8mXg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "@typescript-eslint/utils": "^5.58.0"
+ "@typescript-eslint/scope-manager": "^8.15.0",
+ "@typescript-eslint/utils": "^8.15.0"
},
"engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0",
- "npm": ">=6"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0",
+ "pnpm": "^9.14.0"
},
"peerDependencies": {
- "eslint": "^7.5.0 || ^8.0.0"
+ "eslint": "^8.57.0 || ^9.0.0"
}
},
"node_modules/eslint-plugin-testing-library/node_modules/@typescript-eslint/scope-manager": {
- "version": "5.62.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz",
- "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==",
+ "version": "8.18.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.18.0.tgz",
+ "integrity": "sha512-PNGcHop0jkK2WVYGotk/hxj+UFLhXtGPiGtiaWgVBVP1jhMoMCHlTyJA+hEj4rszoSdLTK3fN4oOatrL0Cp+Xw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "5.62.0",
- "@typescript-eslint/visitor-keys": "5.62.0"
+ "@typescript-eslint/types": "8.18.0",
+ "@typescript-eslint/visitor-keys": "8.18.0"
},
"engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
@@ -9579,12 +9852,13 @@
}
},
"node_modules/eslint-plugin-testing-library/node_modules/@typescript-eslint/types": {
- "version": "5.62.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz",
- "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==",
+ "version": "8.18.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.18.0.tgz",
+ "integrity": "sha512-FNYxgyTCAnFwTrzpBGq+zrnoTO4x0c1CKYY5MuUTzpScqmY5fmsh2o3+57lqdI3NZucBDCzDgdEbIaNfAjAHQA==",
"dev": true,
+ "license": "MIT",
"engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
@@ -9592,117 +9866,109 @@
}
},
"node_modules/eslint-plugin-testing-library/node_modules/@typescript-eslint/typescript-estree": {
- "version": "5.62.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz",
- "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==",
+ "version": "8.18.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.18.0.tgz",
+ "integrity": "sha512-rqQgFRu6yPkauz+ms3nQpohwejS8bvgbPyIDq13cgEDbkXt4LH4OkDMT0/fN1RUtzG8e8AKJyDBoocuQh8qNeg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "5.62.0",
- "@typescript-eslint/visitor-keys": "5.62.0",
+ "@typescript-eslint/types": "8.18.0",
+ "@typescript-eslint/visitor-keys": "8.18.0",
"debug": "^4.3.4",
- "globby": "^11.1.0",
+ "fast-glob": "^3.3.2",
"is-glob": "^4.0.3",
- "semver": "^7.3.7",
- "tsutils": "^3.21.0"
+ "minimatch": "^9.0.4",
+ "semver": "^7.6.0",
+ "ts-api-utils": "^1.3.0"
},
"engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
- "peerDependenciesMeta": {
- "typescript": {
- "optional": true
- }
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <5.8.0"
}
},
"node_modules/eslint-plugin-testing-library/node_modules/@typescript-eslint/utils": {
- "version": "5.62.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz",
- "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==",
+ "version": "8.18.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.18.0.tgz",
+ "integrity": "sha512-p6GLdY383i7h5b0Qrfbix3Vc3+J2k6QWw6UMUeY5JGfm3C5LbZ4QIZzJNoNOfgyRe0uuYKjvVOsO/jD4SJO+xg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "@eslint-community/eslint-utils": "^4.2.0",
- "@types/json-schema": "^7.0.9",
- "@types/semver": "^7.3.12",
- "@typescript-eslint/scope-manager": "5.62.0",
- "@typescript-eslint/types": "5.62.0",
- "@typescript-eslint/typescript-estree": "5.62.0",
- "eslint-scope": "^5.1.1",
- "semver": "^7.3.7"
+ "@eslint-community/eslint-utils": "^4.4.0",
+ "@typescript-eslint/scope-manager": "8.18.0",
+ "@typescript-eslint/types": "8.18.0",
+ "@typescript-eslint/typescript-estree": "8.18.0"
},
"engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <5.8.0"
}
},
"node_modules/eslint-plugin-testing-library/node_modules/@typescript-eslint/visitor-keys": {
- "version": "5.62.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz",
- "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==",
+ "version": "8.18.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.18.0.tgz",
+ "integrity": "sha512-pCh/qEA8Lb1wVIqNvBke8UaRjJ6wrAWkJO5yyIbs8Yx6TNGYyfNjOo61tLv+WwLvoLPp4BQ8B7AHKijl8NGUfw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "5.62.0",
- "eslint-visitor-keys": "^3.3.0"
+ "@typescript-eslint/types": "8.18.0",
+ "eslint-visitor-keys": "^4.2.0"
},
"engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
- "node_modules/eslint-plugin-testing-library/node_modules/eslint-scope": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
- "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
- "dev": true,
- "dependencies": {
- "esrecurse": "^4.3.0",
- "estraverse": "^4.1.1"
- },
- "engines": {
- "node": ">=8.0.0"
- }
- },
- "node_modules/eslint-plugin-testing-library/node_modules/estraverse": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
- "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+ "node_modules/eslint-plugin-testing-library/node_modules/eslint-visitor-keys": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
+ "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
"dev": true,
+ "license": "Apache-2.0",
"engines": {
- "node": ">=4.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
}
},
- "node_modules/eslint-plugin-testing-library/node_modules/lru-cache": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
- "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "node_modules/eslint-plugin-testing-library/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
"dev": true,
+ "license": "ISC",
"dependencies": {
- "yallist": "^4.0.0"
+ "brace-expansion": "^2.0.1"
},
"engines": {
- "node": ">=10"
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/eslint-plugin-testing-library/node_modules/semver": {
- "version": "7.6.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
- "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
+ "version": "7.6.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
+ "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
"dev": true,
- "dependencies": {
- "lru-cache": "^6.0.0"
- },
+ "license": "ISC",
"bin": {
"semver": "bin/semver.js"
},
@@ -9710,12 +9976,6 @@
"node": ">=10"
}
},
- "node_modules/eslint-plugin-testing-library/node_modules/yallist": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
- "dev": true
- },
"node_modules/eslint-plugin-unused-imports": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-3.1.0.tgz",
@@ -11001,6 +11261,16 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/has-property-descriptors": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
@@ -11371,6 +11641,15 @@
"@babel/runtime": "^7.20.6"
}
},
+ "node_modules/i18next-chained-backend": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/i18next-chained-backend/-/i18next-chained-backend-4.6.2.tgz",
+ "integrity": "sha512-2P092fR+nAPQlGzPUoIIxbwo7PTBqQYgLxwv1XhSTQUAUoelLo5LkX+FqRxxSDg9WEAsrc8+2WL6mJtMGIa6WQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.23.2"
+ }
+ },
"node_modules/i18next-http-backend": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-2.5.0.tgz",
@@ -11444,9 +11723,10 @@
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
},
"node_modules/immer": {
- "version": "10.0.3",
- "resolved": "https://registry.npmjs.org/immer/-/immer-10.0.3.tgz",
- "integrity": "sha512-pwupu3eWfouuaowscykeckFmVTpqbzW+rXFCX8rQLkZzM9ftBmU/++Ra+o+L27mz03zJTlyV4UUr+fdKNffo4A==",
+ "version": "10.1.1",
+ "resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz",
+ "integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==",
+ "license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/immer"
@@ -16687,6 +16967,13 @@
"react": "^18.3.1"
}
},
+ "node_modules/react-fast-compare": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz",
+ "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/react-i18next": {
"version": "12.3.1",
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-12.3.1.tgz",
@@ -16720,6 +17007,21 @@
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==",
"dev": true
},
+ "node_modules/react-onclickoutside": {
+ "version": "6.13.1",
+ "resolved": "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.13.1.tgz",
+ "integrity": "sha512-LdrrxK/Yh9zbBQdFbMTXPp3dTSN9B+9YJQucdDu3JNKRrbdU+H+/TVONJoWtOwy4II8Sqf1y/DTI6w/vGPYW0w==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "individual",
+ "url": "https://github.com/Pomax/react-onclickoutside/blob/master/FUNDING.md"
+ },
+ "peerDependencies": {
+ "react": "^15.5.x || ^16.x || ^17.x || ^18.x",
+ "react-dom": "^15.5.x || ^16.x || ^17.x || ^18.x"
+ }
+ },
"node_modules/react-pdf": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/react-pdf/-/react-pdf-9.1.0.tgz",
@@ -16748,6 +17050,22 @@
}
}
},
+ "node_modules/react-popper": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.3.0.tgz",
+ "integrity": "sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "react-fast-compare": "^3.0.1",
+ "warning": "^4.0.2"
+ },
+ "peerDependencies": {
+ "@popperjs/core": "^2.0.0",
+ "react": "^16.8.0 || ^17 || ^18",
+ "react-dom": "^16.8.0 || ^17 || ^18"
+ }
+ },
"node_modules/react-router": {
"version": "5.3.4",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-5.3.4.tgz",
@@ -18013,6 +18331,19 @@
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.2.tgz",
"integrity": "sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg=="
},
+ "node_modules/supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/supports-preserve-symlinks-flag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
@@ -18490,10 +18821,11 @@
}
},
"node_modules/ts-api-utils": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.2.1.tgz",
- "integrity": "sha512-RIYA36cJn2WiH9Hy77hdF9r7oEwxAtB/TS9/S4Qd90Ap4z5FSiin5zEiTL44OII1Y3IIlEvxwxFUVgrHSZ/UpA==",
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz",
+ "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=16"
},
@@ -18729,10 +19061,11 @@
"dev": true
},
"node_modules/typescript": {
- "version": "5.3.3",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
- "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
+ "version": "5.7.2",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz",
+ "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==",
"dev": true,
+ "license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
diff --git a/package.json b/package.json
index fa9707f03..5f31cbe89 100644
--- a/package.json
+++ b/package.json
@@ -5,7 +5,8 @@
"main": "./lib/lib.js",
"types": "./lib/lib.d.ts",
"exports": {
- ".": "./lib/lib.js"
+ ".": "./lib/lib.js",
+ "./*": null
},
"engines": {
"node": "v18",
@@ -39,8 +40,7 @@
},
"files": [
"lib/",
- "COPYING",
- "README.md"
+ "THIRDPARTIES"
],
"keywords": [
"zextras"
@@ -64,14 +64,15 @@
"@tsconfig/node18": "^18.2.4",
"@types/jest": "^29.5.12",
"@types/lodash": "^4.17.4",
- "@types/node": "^18.19.54",
+ "@types/node": "^18.19.67",
"@types/react": "^18.3.11",
"@types/react-dom": "^18.3.0",
"@types/react-router-dom": "^5.3.3",
"@types/ua-parser-js": "^0.7.39",
"@types/webpack": "^5.28.5",
"@types/webpack-env": "^1.18.4",
- "@zextras/carbonio-ui-configs": "^1.0.3",
+ "@zextras/carbonio-search-ui": "github:zextras/carbonio-search-ui#devel",
+ "@zextras/carbonio-ui-configs": "^2.0.1",
"@zextras/carbonio-ui-sdk": "^1.7.10",
"autoprefixer": "^10.4.17",
"babel-loader": "^9.2.1",
@@ -106,11 +107,12 @@
"@sentry/browser": "^7.103.0",
"@tinymce/tinymce-react": "^4.3.2",
"@zextras/carbonio-design-system": "9.0.0-devel.7",
- "@zextras/carbonio-ui-preview": "^3.1.0-devel.2",
+ "@zextras/carbonio-ui-preview": "^3.1.0",
"darkreader": "^4.9.79",
"date-fns": "^4.1.0",
"history": "^5.3.0",
"i18next": "^22.5.1",
+ "i18next-chained-backend": "^4.6.2",
"i18next-http-backend": "^2.5.0",
"immer": "^10.0.3",
"lodash": "^4.17.21",
@@ -127,7 +129,7 @@
},
"peerDependencies": {
"@zextras/carbonio-design-system": "9.0.0-devel.7",
- "@zextras/carbonio-ui-preview": "3.1.0-devel.2",
+ "@zextras/carbonio-ui-preview": "^3.1.0",
"core-js": "^3.39.0",
"lodash": "^4.17.21",
"react": "^18.3.1",
diff --git a/src/boot/app/app-dependant-exports.ts b/src/boot/app/app-dependant-exports.ts
index 235a4d095..04765f19f 100644
--- a/src/boot/app/app-dependant-exports.ts
+++ b/src/boot/app/app-dependant-exports.ts
@@ -13,7 +13,6 @@ import {
normalizeBoardView,
normalizePrimaryAccessoryView,
normalizeRoute,
- normalizeSearchView,
normalizeSecondaryAccessoryView,
normalizeSettingsView,
normalizeUtilityView
@@ -29,7 +28,6 @@ import type {
BoardView,
CarbonioModule,
PrimaryAccessoryView,
- SearchView,
SecondaryAccessoryView,
SettingsView,
UtilityView
@@ -40,7 +38,6 @@ export type AppDependantExports = {
addRoute: (data: Partial) => ReturnType;
addBoardView: (data: Omit) => ReturnType;
addSettingsView: (data: Partial) => ReturnType;
- addSearchView: (data: Partial) => ReturnType;
addUtilityView: (data: Partial) => ReturnType;
addPrimaryAccessoryView: (
data: Partial
@@ -79,8 +76,6 @@ export const getAppDependantExports = (pkg: CarbonioModule): AppDependantExports
appStore.addBoardView(normalizeBoardView(data, pkg)),
addSettingsView: (data: Partial) =>
appStore.addSettingsView(normalizeSettingsView(data, pkg)),
- addSearchView: (data: Partial) =>
- appStore.addSearchView(normalizeSearchView(data, pkg)),
addUtilityView: (data: Partial) =>
appStore.addUtilityView(normalizeUtilityView(data, pkg)),
addPrimaryAccessoryView: (data: Partial) =>
diff --git a/src/boot/app/app-direct-exports.ts b/src/boot/app/app-direct-exports.ts
index e2bfa5f83..06431fb95 100644
--- a/src/boot/app/app-direct-exports.ts
+++ b/src/boot/app/app-direct-exports.ts
@@ -78,8 +78,6 @@ export {
export { getNotificationManager } from '../../notification/NotificationManager';
-export { runSearch } from '../../search/run-search';
-
export { useLocalStorage } from '../../shell/hooks/useLocalStorage';
export const {
@@ -88,7 +86,6 @@ export const {
removeRoute,
removeBoardView,
removeSettingsView,
- removeSearchView,
removeUtilityView,
removePrimaryAccessoryView,
removeSecondaryAccessoryView,
diff --git a/src/boot/app/default-views.test.tsx b/src/boot/app/default-views.test.tsx
index e81b87eaa..059770a1f 100644
--- a/src/boot/app/default-views.test.tsx
+++ b/src/boot/app/default-views.test.tsx
@@ -8,7 +8,7 @@ import React from 'react';
import { produce } from 'immer';
import { DefaultViewsRegister } from './default-views';
-import { SEARCH_APP_ID, SETTINGS_APP_ID, SHELL_APP_ID } from '../../constants';
+import { SETTINGS_APP_ID, SHELL_APP_ID } from '../../constants';
import { useAccountStore } from '../../store/account';
import { useAppStore } from '../../store/app';
import { useLoginConfigStore } from '../../store/login/store';
@@ -17,23 +17,6 @@ import type { AccountSettingsAttrs } from '../../types/account';
import type { AppRouteDescriptor, SettingsView } from '../../types/apps';
describe('DefaultViews', () => {
- it('should register search module', () => {
- setup();
- expect(useAppStore.getState().routes).toMatchObject>({
- [SEARCH_APP_ID]: {
- id: SEARCH_APP_ID,
- app: SEARCH_APP_ID,
- route: SEARCH_APP_ID,
- label: 'Search',
- position: 1000,
- visible: true,
- primaryBar: 'SearchModOutline',
- badge: { show: false },
- appView: expect.any(Function)
- }
- });
- });
-
it.each(['TRUE', undefined])(
'should register settings module if zimbraFeatureOptionsEnabled is %s',
(value) => {
diff --git a/src/boot/app/default-views.ts b/src/boot/app/default-views.ts
index 310be908c..4fc00aba1 100644
--- a/src/boot/app/default-views.ts
+++ b/src/boot/app/default-views.ts
@@ -8,8 +8,7 @@ import { useEffect, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
-import { SEARCH_APP_ID, SETTINGS_APP_ID, SHELL_APP_ID } from '../../constants';
-import { SearchAppView } from '../../search/search-app-view';
+import { SETTINGS_APP_ID, SHELL_APP_ID } from '../../constants';
import { WrappedAccountsSettings } from '../../settings/accounts-settings';
import GeneralSettings from '../../settings/general-settings';
import { useSettingsSubSections } from '../../settings/general-settings-sub-sections';
@@ -19,34 +18,6 @@ import { useAccountStore } from '../../store/account';
import { useAppStore } from '../../store/app';
import type { AppRouteDescriptor, SettingsView } from '../../types/apps';
-const useSearchModule = (): void => {
- const [t] = useTranslation();
-
- const searchRouteDescriptor = useMemo(
- () => ({
- id: SEARCH_APP_ID,
- app: SEARCH_APP_ID,
- route: SEARCH_APP_ID,
- appView: SearchAppView,
- badge: {
- show: false
- },
- label: t('search.app', 'Search'),
- position: 1000,
- visible: true,
- primaryBar: 'SearchModOutline'
- }),
- [t]
- );
- useEffect(() => {
- useAppStore.getState().addRoute(searchRouteDescriptor);
-
- return (): void => {
- useAppStore.getState().removeRoute(searchRouteDescriptor.id);
- };
- }, [searchRouteDescriptor]);
-};
-
const useSettingsModule = (): void => {
const [t] = useTranslation();
const settingsAttrs = useAccountStore((state) => state.settings.attrs);
@@ -116,8 +87,6 @@ const useSettingsModule = (): void => {
};
export const DefaultViewsRegister = (): null => {
- useSearchModule();
useSettingsModule();
-
return null;
};
diff --git a/src/constants/index.ts b/src/constants/index.ts
index 45c6fb184..fab160284 100644
--- a/src/constants/index.ts
+++ b/src/constants/index.ts
@@ -11,7 +11,6 @@ export const SETTINGS_APP_ID = 'settings';
* @deprecated This constant is not used and will be deleted in next releases
*/
export const ACCOUNTS_APP_ID = 'accounts';
-export const SEARCH_APP_ID = 'search';
export const ACTION_TYPES = {
/** @deprecated this action does not belong to shell, and therefore it will be removed in next releases */
@@ -73,7 +72,6 @@ export const DARK_READER_VALUES = ['auto', 'enabled', 'disabled'] as const;
export const LOCAL_STORAGE_SETTINGS_KEY = 'settings';
export const LOCAL_STORAGE_LAST_PRIMARY_KEY = 'config';
-export const LOCAL_STORAGE_SEARCH_KEY = 'search_suggestions';
export const LOCAL_STORAGE_BOARD_SIZE = 'board_size';
export const SCALING_OPTIONS = [
{ value: 75, label: 'xs' },
diff --git a/src/jest-polyfills.ts b/src/jest-polyfills.ts
index afb4b3f74..db2a05538 100644
--- a/src/jest-polyfills.ts
+++ b/src/jest-polyfills.ts
@@ -55,7 +55,7 @@ Object.defineProperty(window, 'ResizeObserver', {
Object.defineProperty(window.crypto, 'subtle', {
writable: true,
value: {
- digest(algorithm: AlgorithmIdentifier, data: BufferSource): Promise {
+ digest(algorithm: AlgorithmIdentifier, data: BufferSource): Promise {
return new Promise((resolve) => {
const decoder = new TextDecoder();
const dataString = decoder.decode(data);
diff --git a/src/lib.ts b/src/lib.ts
index 88de50c8e..c40c4b3a6 100644
--- a/src/lib.ts
+++ b/src/lib.ts
@@ -33,7 +33,6 @@ export declare const setAppContext: AppDependantExports['setAppContext'];
export declare const addRoute: AppDependantExports['addRoute'];
export declare const addBoardView: AppDependantExports['addBoardView'];
export declare const addSettingsView: AppDependantExports['addSettingsView'];
-export declare const addSearchView: AppDependantExports['addSearchView'];
export declare const addUtilityView: AppDependantExports['addUtilityView'];
export declare const addPrimaryAccessoryView: AppDependantExports['addPrimaryAccessoryView'];
export declare const addSecondaryAccessoryView: AppDependantExports['addSecondaryAccessoryView'];
@@ -66,7 +65,6 @@ export type {
BoardViewComponentProps,
AppViewComponentProps,
SettingsViewProps,
- SearchViewProps,
PrimaryAccessoryViewProps,
SecondaryAccessoryViewProps,
SettingsSubSection,
@@ -94,8 +92,6 @@ export type {
PopupNotificationConfig
} from './notification/NotificationManager';
-export type { QueryItem, QueryChip } from './types/search';
-
export type { Grant } from './types/misc';
export type {
diff --git a/src/search/module-selector.test.tsx b/src/search/module-selector.test.tsx
deleted file mode 100644
index 61b3efdf6..000000000
--- a/src/search/module-selector.test.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2024 Zextras
- *
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import React from 'react';
-
-import { screen } from '@testing-library/react';
-
-import { ModuleSelector } from './module-selector';
-import { useAppStore } from '../store/app';
-import { TESTID_SELECTORS } from '../tests/constants';
-import { setup } from '../tests/utils';
-
-describe('Search module selector', () => {
- it('should hide the component if there are no modules in the store', () => {
- useAppStore.setState((state) => ({
- views: { ...state.views, search: [] }
- }));
- setup();
- expect(screen.queryByTestId(TESTID_SELECTORS.headerModuleSelector)).not.toBeInTheDocument();
- });
-});
diff --git a/src/search/module-selector.tsx b/src/search/module-selector.tsx
deleted file mode 100644
index 20cb95635..000000000
--- a/src/search/module-selector.tsx
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2022 Zextras
- *
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-import React, { useCallback, useEffect, useMemo, useState } from 'react';
-
-import type { DropdownItem } from '@zextras/carbonio-design-system';
-import { Container, Row, Text, Icon, Dropdown } from '@zextras/carbonio-design-system';
-import styled from 'styled-components';
-
-import { useSearchModule } from './useSearchModule';
-import { SEARCH_APP_ID } from '../constants';
-import { pushHistory, useCurrentRoute } from '../history/hooks';
-import { useAppStore } from '../store/app';
-
-const SelectorContainer = styled(Container)`
- border-right: 0.0625rem solid ${({ theme }): string => theme.palette.gray4.regular};
- cursor: pointer;
- background: ${({ theme }): string => theme.palette.gray6.regular};
-
- &:hover {
- background: ${({ theme }): string => theme.palette.gray6.hover};
- }
-`;
-
-export const ModuleSelector = (): React.JSX.Element | null => {
- const currentRoute = useCurrentRoute();
- const searchViews = useAppStore((s) => s.views.search);
- const [module, updateModule] = useSearchModule();
- const searchView = useMemo(
- () => searchViews.find((m) => m.route === module),
- [module, searchViews]
- );
-
- const [open, setOpen] = useState(false);
-
- const dropdownItems = useMemo(
- (): DropdownItem[] =>
- searchViews.map(
- ({ id, label, icon, route }): DropdownItem => ({
- id,
- label,
- icon,
- onClick: (): void => {
- // open the search view and update the module of moduleSelector
- // order is important
- pushHistory({ route: SEARCH_APP_ID, path: '' });
- updateModule(route);
- }
- })
- ),
- [searchViews, updateModule]
- );
-
- useEffect(() => {
- // update the search module based on active route
- // it handles also back navigation that cannot be handled using primary icon clicks
- if (
- currentRoute?.route &&
- module !== currentRoute.route &&
- searchViews.find((m) => m.route === currentRoute.route)
- ) {
- updateModule(currentRoute.route);
- }
- }, [searchView, module, searchViews, updateModule, currentRoute?.route]);
-
- const openDropdown = useCallback(() => {
- setOpen(true);
- }, []);
-
- const closeDropdown = useCallback(() => {
- setOpen(false);
- }, []);
-
- if (!searchView) {
- return null;
- }
-
- // TODO: replace the Dropdown with a Select with the customLabelFactory
- return (
-
-
-
-
- {searchView?.label}
-
-
-
-
-
- );
-};
diff --git a/src/search/run-search.ts b/src/search/run-search.ts
deleted file mode 100644
index 5feaf65e8..000000000
--- a/src/search/run-search.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2022 Zextras
- *
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-import { useSearchStore } from './search-store';
-import { SEARCH_APP_ID } from '../constants';
-import { pushHistory } from '../history/hooks';
-import type { QueryChip } from '../types/search';
-
-export const runSearch = (query: Array, module: string): void => {
- useSearchStore.setState({ query, module, searchDisabled: false });
- pushHistory({ route: SEARCH_APP_ID, path: '' });
-};
diff --git a/src/search/search-app-view.tsx b/src/search/search-app-view.tsx
deleted file mode 100644
index e4c41876c..000000000
--- a/src/search/search-app-view.tsx
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2021 Zextras
- *
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import React, { useCallback, useMemo } from 'react';
-
-import {
- Button,
- Chip,
- Container,
- Divider,
- Icon,
- Padding,
- Text
-} from '@zextras/carbonio-design-system';
-import { map } from 'lodash';
-
-import { useSearchStore } from './search-store';
-import { useSearchModule } from './useSearchModule';
-import { AppContextProvider } from '../boot/app/app-context-provider';
-import { RESULT_LABEL_TYPE } from '../constants';
-import { useAppStore } from '../store/app';
-import { getT } from '../store/i18n/hooks';
-import { type SearchState } from '../types/search';
-import type { ValueOf } from '../utils/typeUtils';
-
-const useQuery = (): [query: SearchState['query'], updateQuery: SearchState['updateQuery']] =>
- useSearchStore((s) => [s.query, s.updateQuery]);
-
-const useDisableSearch = (): [
- isDisabled: SearchState['searchDisabled'],
- setDisabled: SearchState['setSearchDisabled']
-] => useSearchStore((s) => [s.searchDisabled, s.setSearchDisabled]);
-
-const getIconAndColor = (
- labelType: ValueOf
-): [icon: string, color: string] => {
- if (labelType === RESULT_LABEL_TYPE.warning) {
- return ['AlertTriangle', 'warning'];
- }
- if (labelType === RESULT_LABEL_TYPE.error) {
- return ['CloseSquare', 'error'];
- }
- return ['', ''];
-};
-
-interface ResultsHeaderProps {
- label: string;
- labelType?: ValueOf;
-}
-
-const ResultsHeader = ({
- label,
- labelType = RESULT_LABEL_TYPE.normal
-}: ResultsHeaderProps): React.JSX.Element => {
- const t = getT();
- const [query, updateQuery] = useQuery();
- const [, setDisabled] = useDisableSearch();
-
- const resetQuery = useCallback(() => {
- updateQuery([]);
- setDisabled(false);
- }, [updateQuery, setDisabled]);
-
- const labelTypeElem = useMemo(() => {
- if (labelType === RESULT_LABEL_TYPE.normal) {
- return <>>;
- }
-
- const [icon, color] = getIconAndColor(labelType);
- return (
-
-
-
- );
- }, [labelType]);
-
- const chipItems = useMemo(
- () =>
- map(query, (queryChip, index) => (
-
-
-
- )),
- [query]
- );
-
- return (
- <>
-
-
- {labelTypeElem}
- {label}
- {chipItems}
-
- {query?.length > 0 && (
-
-
-
- )}
-
-
- >
- );
-};
-
-export const SearchAppView = (): React.JSX.Element => {
- const searchViews = useAppStore((s) => s.views.search);
- const [module] = useSearchModule();
-
- const searchView = useMemo(
- () => searchViews.find((m) => m.route === module),
- [module, searchViews]
- );
-
- return (
- <>
- {searchView && (
-
-
-
- )}
- >
- );
-};
diff --git a/src/search/search-bar.test.tsx b/src/search/search-bar.test.tsx
deleted file mode 100644
index 8dd77e398..000000000
--- a/src/search/search-bar.test.tsx
+++ /dev/null
@@ -1,487 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2023 Zextras
- *
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-import React from 'react';
-
-import { act, waitFor } from '@testing-library/react';
-import { Link } from 'react-router-dom';
-
-import { SearchAppView } from './search-app-view';
-import { SearchBar } from './search-bar';
-import { useSearchStore } from './search-store';
-import { SEARCH_MODULE_KEY } from './useSearchModule';
-import { SEARCH_APP_ID } from '../constants';
-import * as useLocalStorage from '../shell/hooks/useLocalStorage';
-import { useAppStore } from '../store/app';
-import { ICONS, TESTID_SELECTORS } from '../tests/constants';
-import {
- generateCarbonioModule,
- generateModuleRouteDescriptor,
- setupAppStore
-} from '../tests/test-app-utils';
-import { setup, screen, within } from '../tests/utils';
-import type { SearchView } from '../types/apps';
-
-describe('Search bar', () => {
- describe('Clear search', () => {
- it('should hide the clear button, by default, when the search input is empty or not valued', () => {
- setup();
- const inputElement = screen.getByRole('textbox', { name: /search in/i });
- expect(inputElement).toBeVisible();
- expect(inputElement).toHaveValue('');
- expect(
- screen.queryByRoleWithIcon('button', { icon: ICONS.clearSearch })
- ).not.toBeInTheDocument();
- });
-
- it('should clear the input element when the user clicks on clear button', async () => {
- const { user } = setup();
- const inputElement = screen.getByRole('textbox', { name: /search in/i });
- const textContent = 'test';
- await user.type(inputElement, textContent);
- await user.keyboard(',');
- await user.type(inputElement, 'test2');
- await user.click(screen.getByRoleWithIcon('button', { icon: ICONS.clearSearch }));
- expect(inputElement).toHaveValue('');
- expect(inputElement).toHaveFocus();
- expect(screen.queryByText(textContent)).not.toBeInTheDocument();
- });
- });
-
- describe('Search button', () => {
- it('should disable the search button, by default, when the input element is empty and there are no chips', async () => {
- setup();
- const inputElement = screen.getByRole('textbox', { name: /search in/i });
- expect(inputElement).toBeVisible();
- expect(inputElement).toHaveValue('');
- const searchButton = screen.getByRoleWithIcon('button', { icon: ICONS.search });
- expect(searchButton).toBeVisible();
- expect(searchButton).toBeDisabled();
- });
-
- it('should enable the search button when the user starts typing inside the input element', async () => {
- const { user } = setup();
- await user.type(screen.getByRole('textbox', { name: /search in/i }), 'test');
- const searchButton = screen.getByRoleWithIcon('button', { icon: ICONS.search });
- await waitFor(() => expect(searchButton).toBeEnabled());
- jest.advanceTimersToNextTimer();
- await user.hover(searchButton);
- act(() => {
- // run timers of tooltip
- jest.advanceTimersToNextTimer();
- });
- expect(
- await screen.findByText(/Type or choose some keywords to start a search/i)
- ).toBeVisible();
- });
-
- it.each(['[Enter]', ',', '[Space]'])(
- 'should enable the search button when the user presses keyboard key (%s) to add the chips',
- async (key) => {
- const { user } = setup();
- const inputElement = screen.getByRole('textbox', { name: /search in/i });
- const chip1 = 'test';
- await user.type(inputElement, chip1);
- await user.keyboard(key);
- expect(screen.getByText(chip1)).toBeVisible();
- expect(inputElement).toHaveValue('');
- const searchButton = screen.getByRoleWithIcon('button', { icon: ICONS.search });
- expect(searchButton).toBeEnabled();
- jest.advanceTimersToNextTimer();
- await user.hover(searchButton);
- act(() => {
- // run timers of tooltip
- jest.advanceTimersToNextTimer();
- });
- expect(await screen.findByText(/Start search/i)).toBeVisible();
- }
- );
-
- it('should render the chips when the user clicks on the search button', async () => {
- const { user } = setup();
- const inputElement = screen.getByRole('textbox', { name: /search in/i });
- const searchButton = screen.getByRoleWithIcon('button', { icon: ICONS.search });
- const chip1 = 'test';
- const chip2 = 'test2';
- await user.type(inputElement, chip1);
- await user.click(searchButton);
- await user.type(inputElement, 'test2');
- await user.click(searchButton);
- expect(screen.getByText(chip1)).toBeVisible();
- expect(screen.getByText(chip2)).toBeVisible();
- expect(inputElement).not.toHaveFocus();
- expect(inputElement).toHaveValue('');
- });
- });
-
- describe('Dropdown suggestions', () => {
- it('should render the last 5 words of the suggestion array when the user clicks on the input element', async () => {
- const app = generateCarbonioModule();
- const mockUseLocalStorage = jest.spyOn(useLocalStorage, 'useLocalStorage');
- const route = 'mails';
- useSearchStore.setState({
- module: 'mails'
- });
- useAppStore.getState().addSearchView({
- app: app.name,
- icon: app.icon,
- route,
- label: app.display,
- position: app.priority,
- id: app.name,
- component: () => {app.name}
- });
- mockUseLocalStorage.mockReturnValue([
- [
- { value: 'test1', label: 'test1', icon: 'ClockOutline', app: 'mails', id: 'test1' },
- { value: 'test2', label: 'test2', icon: 'ClockOutline', app: 'mails', id: 'test2' },
- { value: 'test3', label: 'test3', icon: 'ClockOutline', app: 'mails', id: 'test3' },
- { value: 'test4', label: 'test4', icon: 'ClockOutline', app: 'mails', id: 'test4' },
- { value: 'test5', label: 'test5', icon: 'ClockOutline', app: 'mails', id: 'test5' },
- { value: 'test6', label: 'test6', icon: 'ClockOutline', app: 'files', id: 'test6' },
- { value: 'release', label: 'release', icon: 'ClockOutline', app: 'mails', id: 'release' }
- ],
- jest.fn()
- ]);
- const { user } = setup(, {
- initialRouterEntries: [`/${SEARCH_APP_ID}`]
- });
- await user.click(screen.getByRole('textbox', { name: `Search in ${app.display}` }));
- const dropdown = await screen.findByTestId(TESTID_SELECTORS.dropdown);
- expect(within(dropdown).getByText('release')).toBeVisible();
- expect(within(dropdown).queryByText('test6')).not.toBeInTheDocument();
- expect(within(dropdown).getByText('test5')).toBeVisible();
- expect(within(dropdown).getByText('test4')).toBeVisible();
- expect(within(dropdown).getByText('test3')).toBeVisible();
- expect(within(dropdown).getByText('test2')).toBeVisible();
- });
-
- it('should render the suggestions when the user starts typing in the input element', async () => {
- const app = generateCarbonioModule();
- const mockUseLocalStorage = jest.spyOn(useLocalStorage, 'useLocalStorage');
- const route = 'mails';
- useSearchStore.setState({
- module: 'mails'
- });
- useAppStore.getState().addSearchView({
- app: app.name,
- icon: app.icon,
- route,
- label: app.display,
- position: app.priority,
- id: app.name,
- component: () => {app.name}
- });
- mockUseLocalStorage.mockReturnValue([
- [
- { value: 'test', label: 'test', icon: 'ClockOutline', app: 'mails', id: 'test' },
- { value: 'test2', label: 'test2', icon: 'ClockOutline', app: 'mails', id: 'test2' },
- { value: 'test3', label: 'test3', icon: 'ClockOutline', app: 'files', id: 'test3' },
- { value: 'release', label: 'release', icon: 'ClockOutline', app: 'mails', id: 'release' }
- ],
- jest.fn()
- ]);
- const { user } = setup(, {
- initialRouterEntries: [`/${SEARCH_APP_ID}`]
- });
- await user.type(screen.getByRole('textbox', { name: `Search in ${app.display}` }), 't');
- const dropdown = await screen.findByTestId(TESTID_SELECTORS.dropdown);
- expect(within(dropdown).getByText('test')).toBeVisible();
- expect(within(dropdown).getByText('test2')).toBeVisible();
- expect(within(dropdown).queryByText('test3')).not.toBeInTheDocument();
- await waitFor(() => expect(within(dropdown).queryByText('release')).not.toBeInTheDocument());
- });
-
- it('should create chip when the user clicks on the dropdown suggestion', async () => {
- const app = generateCarbonioModule();
- const mockUseLocalStorage = jest.spyOn(useLocalStorage, 'useLocalStorage');
- const route = 'mails';
- useSearchStore.setState({
- module: 'mails'
- });
- useAppStore.getState().addSearchView({
- app: app.name,
- icon: app.icon,
- route,
- label: app.display,
- position: app.priority,
- id: app.name,
- component: () => {app.name}
- });
- mockUseLocalStorage.mockReturnValue([
- [{ value: 'test', label: 'test', icon: 'ClockOutline', app: 'mails', id: 'test' }],
- jest.fn()
- ]);
- const { user } = setup(, {
- initialRouterEntries: [`/${SEARCH_APP_ID}`]
- });
- await user.type(screen.getByRole('textbox', { name: `Search in ${app.display}` }), 't');
- const dropdown = await screen.findByTestId(TESTID_SELECTORS.dropdown);
- await user.click(within(dropdown).getByText('test'));
- expect(dropdown).not.toBeInTheDocument();
- // chip is created
- expect(screen.getByText('test')).toBeVisible();
- });
- });
-
- test('should render the module selector and the input of the search bar', async () => {
- const app = generateCarbonioModule();
- const route = 'appRoute';
-
- useAppStore.getState().addSearchView({
- app: app.name,
- icon: app.icon,
- route,
- label: app.display,
- position: app.priority,
- id: app.name,
- component: () => {app.name}
- });
- useAppStore.getState().addRoute({
- id: SEARCH_APP_ID,
- app: SEARCH_APP_ID,
- route: SEARCH_APP_ID,
- appView: SearchAppView,
- badge: {
- show: false
- },
- label: 'Search',
- position: 1000,
- visible: true,
- primaryBar: 'SearchModOutline'
- });
-
- setup(, { initialRouterEntries: [`/${SEARCH_APP_ID}`] });
- expect(screen.getByText(app.display)).toBeVisible();
- expect(screen.getByRole('textbox', { name: `Search in ${app.display}` })).toBeVisible();
- });
-
- it('should show the label of the module if the user is already inside that module', () => {
- const app = generateCarbonioModule({ priority: 1 });
- const searchRoute = generateModuleRouteDescriptor({
- label: app.name,
- position: 3,
- id: app.name,
- route: app.name
- });
- useSearchStore.setState({
- module: undefined
- });
- setupAppStore([app], [searchRoute]);
- useAppStore.getState().addSearchView({
- app: app.name,
- icon: app.icon,
- id: app.name,
- label: app.display,
- position: app.priority,
- route: app.name,
- component: () => {app.name}
- });
- setup(, { initialRouterEntries: [`/${app.name}`] });
- const selector = screen.getByTestId(TESTID_SELECTORS.headerModuleSelector);
- expect(within(selector).getByText(app.display)).toBeVisible();
- expect(screen.getByRole('textbox', { name: `Search in ${app.display}` })).toBeVisible();
- });
-
- it('should not update module if the user navigates to a module without search', async () => {
- const app1 = generateCarbonioModule({ priority: 1 });
- const app2 = generateCarbonioModule({ priority: 2 });
- const app1Route = generateModuleRouteDescriptor({
- label: app1.name,
- position: 1,
- id: app1.name,
- route: app1.name
- });
- const app2Route = generateModuleRouteDescriptor({
- label: app2.name,
- position: 2,
- id: app2.name,
- route: app2.name
- });
- setupAppStore([app1, app2], [app1Route, app2Route]);
- const app1SearchView = {
- app: app1.name,
- icon: app1.icon,
- id: app1.name,
- label: app1.display,
- position: app1.priority,
- route: app1.name,
- component: (): React.JSX.Element => {app1.name}
- } satisfies SearchView;
- useAppStore.getState().addSearchView(app1SearchView);
- const { user } = setup(
- <>
-
- go to app2
- >,
- { initialRouterEntries: [`/${app1Route.route}`] }
- );
- await user.click(screen.getByRole('link', { name: 'go to app2' }));
- expect(screen.getByRole('textbox', { name: `Search in ${app1.display}` })).toBeVisible();
- const selector = screen.getByTestId(TESTID_SELECTORS.headerModuleSelector);
- expect(within(selector).getByText(app1SearchView.label)).toBeVisible();
- });
-
- it('should show the module with the lowest priority if both store and session storage are undefined', () => {
- const app1 = generateCarbonioModule({ priority: 1 });
- const app2 = generateCarbonioModule({ priority: 2 });
- const app1Route = generateModuleRouteDescriptor({
- label: app1.name,
- position: 1,
- id: app1.name,
- route: app1.name
- });
- const app2Route = generateModuleRouteDescriptor({
- label: app2.name,
- position: 2,
- id: app2.name,
- route: app2.name
- });
- setupAppStore([app1, app2], [app1Route, app2Route]);
- const app1SearchView = {
- app: app1.name,
- icon: app1.icon,
- id: app1.name,
- label: app1.display,
- position: app1.priority,
- route: app1.name,
- component: (): React.JSX.Element => {app1.name}
- } satisfies SearchView;
- const app2SearchView = {
- app: app2.name,
- icon: app2.icon,
- id: app2.name,
- label: app2.display,
- position: app2.priority,
- route: app2.name,
- component: (): React.JSX.Element => {app2.name}
- } satisfies SearchView;
- useAppStore.getState().addSearchView(app1SearchView);
- useAppStore.getState().addSearchView(app2SearchView);
- setup();
- const selector = screen.getByTestId(TESTID_SELECTORS.headerModuleSelector);
- expect(within(selector).getByText(app1.display)).toBeVisible();
- expect(screen.getByRole('textbox', { name: `Search in ${app1.display}` })).toBeVisible();
- });
-
- it('should show the module with the lowest priority if the session storage has an invalid value(sasso)', () => {
- const app1 = generateCarbonioModule({ priority: 1 });
- const app2 = generateCarbonioModule({ priority: 2 });
- const app1Route = generateModuleRouteDescriptor({
- label: app1.name,
- position: 1,
- id: app1.name,
- route: app1.name
- });
- const app2Route = generateModuleRouteDescriptor({
- label: app2.name,
- position: 2,
- id: app2.name,
- route: app2.name
- });
- setupAppStore([app1, app2], [app1Route, app2Route]);
- const app1SearchView = {
- app: app1.name,
- icon: app1.icon,
- id: app1.name,
- label: app1.display,
- position: app1.priority,
- route: app1.name,
- component: (): React.JSX.Element => {app1.name}
- } satisfies SearchView;
- const app2SearchView = {
- app: app2.name,
- icon: app2.icon,
- id: app2.name,
- label: app2.display,
- position: app2.priority,
- route: app2.name,
- component: (): React.JSX.Element => {app2.name}
- } satisfies SearchView;
- sessionStorage.setItem(SEARCH_MODULE_KEY, 'sasso');
- useAppStore.getState().addSearchView(app1SearchView);
- useAppStore.getState().addSearchView(app2SearchView);
- setup();
- const selector = screen.getByTestId(TESTID_SELECTORS.headerModuleSelector);
- expect(within(selector).getByText(app1.display)).toBeVisible();
- expect(screen.getByRole('textbox', { name: `Search in ${app1.display}` })).toBeVisible();
- });
-
- it('should show the module matching the session storage if the module has a searchView', () => {
- const app1 = generateCarbonioModule({ priority: 1 });
- const app2 = generateCarbonioModule({ priority: 2 });
- const app1Route = generateModuleRouteDescriptor({
- label: app1.name,
- position: 1,
- id: app1.name,
- route: app1.name
- });
- const app2Route = generateModuleRouteDescriptor({
- label: app2.name,
- position: 2,
- id: app2.name,
- route: app2.name
- });
- setupAppStore([app1, app2], [app1Route, app2Route]);
- const app1SearchView = {
- app: app1.name,
- icon: app1.icon,
- id: app1.name,
- label: app1.display,
- position: app1.priority,
- route: app1.name,
- component: (): React.JSX.Element => {app1.name}
- } satisfies SearchView;
- const app2SearchView = {
- app: app2.name,
- icon: app2.icon,
- id: app2.name,
- label: app2.display,
- position: app2.priority,
- route: app2.name,
- component: (): React.JSX.Element => {app2.name}
- } satisfies SearchView;
- sessionStorage.setItem(SEARCH_MODULE_KEY, app2.name);
- useAppStore.getState().addSearchView(app1SearchView);
- useAppStore.getState().addSearchView(app2SearchView);
- setup();
- const selector = screen.getByTestId(TESTID_SELECTORS.headerModuleSelector);
- expect(within(selector).getByText(app2.display)).toBeVisible();
- expect(screen.getByRole('textbox', { name: `Search in ${app2.display}` })).toBeVisible();
- });
-
- it('should show the module with the lowest priority if the session storage module has not a searchView(tasks/chats)', () => {
- const app1 = generateCarbonioModule({ priority: 1 });
- const app2 = generateCarbonioModule({ priority: 2 });
- const app1Route = generateModuleRouteDescriptor({
- label: app1.name,
- position: 1,
- id: app1.name,
- route: app1.name
- });
- const app2Route = generateModuleRouteDescriptor({
- label: app2.name,
- position: 2,
- id: app2.name,
- route: app2.name
- });
- setupAppStore([app1, app2], [app1Route, app2Route]);
- const app1SearchView = {
- app: app1.name,
- icon: app1.icon,
- id: app1.name,
- label: app1.display,
- position: app1.priority,
- route: app1.name,
- component: (): React.JSX.Element => {app1.name}
- } satisfies SearchView;
- sessionStorage.setItem(SEARCH_MODULE_KEY, app2.name);
- useAppStore.getState().addSearchView(app1SearchView);
- setup();
- const selector = screen.getByTestId(TESTID_SELECTORS.headerModuleSelector);
- expect(within(selector).getByText(app1.display)).toBeVisible();
- expect(screen.getByRole('textbox', { name: `Search in ${app1.display}` })).toBeVisible();
- });
-});
diff --git a/src/search/search-bar.tsx b/src/search/search-bar.tsx
deleted file mode 100644
index ef2cea9d4..000000000
--- a/src/search/search-bar.tsx
+++ /dev/null
@@ -1,450 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2021 Zextras
- *
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
-
-import type { ChipInputProps } from '@zextras/carbonio-design-system';
-import {
- ChipInput,
- Container,
- IconButton,
- Padding,
- Tooltip
-} from '@zextras/carbonio-design-system';
-import { filter, find, map, reduce, some } from 'lodash';
-import { useHistory } from 'react-router-dom';
-import styled from 'styled-components';
-
-import { ModuleSelector } from './module-selector';
-import { useSearchStore } from './search-store';
-import { useSearchModule } from './useSearchModule';
-import { LOCAL_STORAGE_SEARCH_KEY, SEARCH_APP_ID } from '../constants';
-import { useLocalStorage } from '../shell/hooks/useLocalStorage';
-import { useAppStore } from '../store/app';
-import { getT } from '../store/i18n/hooks';
-import type { QueryChip, QueryItem } from '../types/search';
-
-const OutlinedIconButton = styled(IconButton)`
- border: 0.0625rem solid
- ${({ theme, disabled }): string =>
- disabled ? theme.palette.primary.disabled : theme.palette.primary.regular};
- display: block;
- & svg {
- border: none;
- }
-`;
-
-const StyledChipInput = styled(ChipInput)`
- cursor: pointer;
- overflow-x: hidden;
- padding: 0 1rem;
- &:hover {
- outline: none;
- background: ${({ theme, disabled }): string =>
- disabled ? 'gray5' : theme.palette.gray5.hover};
- }
-`;
-
-const StyledContainer = styled(Container)`
- height: 2.625rem;
- overflow-y: hidden;
- &:first-child {
- transform: translateY(-0.125rem);
- }
-`;
-
-type SearchOption = NonNullable[number] & QueryItem;
-
-export const SearchBar = (): React.JSX.Element => {
- const inputRef = useRef(null);
- const t = getT();
- const [storedSuggestions, setStoredSuggestions] = useLocalStorage(
- LOCAL_STORAGE_SEARCH_KEY,
- []
- );
- const [inputTyped, setInputTyped] = useState('');
- const history = useHistory();
- const [currentSearchModuleRoute] = useSearchModule();
- const { updateQuery, query, searchDisabled, setSearchDisabled, tooltip } = useSearchStore();
- const modules = useAppStore((s) => s.views.search);
- const moduleLabel = useMemo(
- () =>
- modules.find(({ route }) => route === currentSearchModuleRoute)?.label ||
- currentSearchModuleRoute,
- [currentSearchModuleRoute, modules]
- );
-
- const [isTyping, setIsTyping] = useState(false);
-
- const [options, setOptions] = useState([]);
-
- const [inputHasFocus, setInputHasFocus] = useState(false);
-
- const [searchInputValue, setSearchInputValue] = useState(query);
-
- const showClear = useMemo(
- () =>
- searchInputValue.length > 0 ||
- (inputRef.current?.value && inputRef.current?.value?.length > 0),
- [searchInputValue.length]
- );
- const clearSearch = useCallback((): void => {
- if (inputRef.current) {
- inputRef.current.value = '';
- inputRef.current?.focus();
- }
- setIsTyping(false);
- setSearchInputValue([]);
- setSearchDisabled(false);
- updateQuery([]);
- setInputTyped('');
- }, [setSearchDisabled, updateQuery]);
-
- const onSearch = useCallback(() => {
- updateQuery((currentQuery) => {
- const ref = inputRef?.current;
- if (ref) {
- ref.value = '';
- }
- if (inputTyped.length > 0) {
- const newInputValue: typeof searchInputValue = [
- ...searchInputValue,
- ...map(
- inputTyped.split(' '),
- (label, id): QueryChip => ({
- id: `${id}`,
- label,
- hasAvatar: false
- })
- )
- ];
- setSearchInputValue(newInputValue);
- setInputTyped('');
- return reduce(
- newInputValue,
- (acc, newInputChip) => {
- if (
- !some(
- currentQuery,
- (currentQueryChip) => currentQueryChip.label === newInputChip.label
- )
- ) {
- acc.push(newInputChip);
- }
- return acc;
- },
- filter(currentQuery, (currentQueryChip) =>
- some(
- searchInputValue,
- (searchInputChip) => searchInputChip.label === currentQueryChip.label
- )
- )
- );
- }
-
- setInputTyped('');
-
- return reduce(
- searchInputValue,
- (acc, searchInputChip) => {
- if (
- !some(
- currentQuery,
- (currentQueryChip) => currentQueryChip.label === searchInputChip.label
- )
- ) {
- acc.push(searchInputChip);
- }
- return acc;
- },
-
- filter(currentQuery, (currentQueryChip: QueryChip) =>
- some(
- searchInputValue,
- (searchInputChip) => searchInputChip.label === currentQueryChip.label
- )
- )
- );
- });
- // TODO: perform a navigation only when coming from a different module (not the search one)
- history.push(`/${SEARCH_APP_ID}`);
- }, [updateQuery, history, inputTyped, searchInputValue]);
-
- const appSuggestions = useMemo(
- () =>
- filter(storedSuggestions, (v) => v.app === currentSearchModuleRoute)
- .reverse()
- .map(
- (item): SearchOption => ({
- ...item,
- disabled: searchDisabled,
- onClick: (): void => {
- const newInputChip: QueryChip = { ...item, hasAvatar: false, onClick: undefined };
- setSearchInputValue((prevState) => [...prevState, newInputChip]);
- }
- })
- ),
- [storedSuggestions, currentSearchModuleRoute, searchDisabled]
- );
-
- const updateOptions = useCallback(
- (textContent: string, queryChips: QueryChip[]): void => {
- if (textContent.length > 0) {
- setOptions(
- appSuggestions
- .filter(
- (suggestion) =>
- textContent &&
- suggestion.label?.includes(textContent) &&
- !some(queryChips, (queryChip) => queryChip.value === suggestion.label)
- )
- .slice(0, 5)
- );
- } else {
- setOptions(appSuggestions.slice(0, 5));
- }
- },
- [appSuggestions]
- );
-
- const onQueryChange = useCallback>(
- (newQuery) => {
- // FIXME: move the saving of suggestions after the search occurs.
- // The saving logic should not be placed here because the onChange is called even when a chip is removed from the chipInput.
- // So, at the moment, keywords never searched for are saved too.
- const lastChipLabel = newQuery[newQuery.length - 1]?.label;
- if (
- lastChipLabel &&
- typeof lastChipLabel === 'string' &&
- currentSearchModuleRoute &&
- !find(appSuggestions, (suggestion) => suggestion.label === lastChipLabel)
- ) {
- setStoredSuggestions((prevState) => {
- const newSuggestion: SearchOption = {
- value: lastChipLabel,
- label: lastChipLabel,
- icon: 'ClockOutline',
- app: currentSearchModuleRoute,
- id: lastChipLabel
- };
- return [...prevState, newSuggestion];
- });
- }
-
- // FIXME: remove the cast (by making ChipItem support generics?)
- setSearchInputValue(newQuery as QueryChip[]);
- },
- [appSuggestions, currentSearchModuleRoute, setStoredSuggestions]
- );
-
- const onInputType = useCallback>(
- (ev) => {
- if (!ev.textContent) {
- setIsTyping(false);
- } else {
- setIsTyping(true);
- }
- setInputTyped(ev.textContent || '');
- updateOptions(ev.textContent || '', query);
- },
- [query, updateOptions]
- );
-
- useEffect(() => {
- if (currentSearchModuleRoute) {
- const suggestions = filter(
- appSuggestions,
- (suggestion) => suggestion.app === currentSearchModuleRoute
- ).slice(0, 5);
-
- setOptions(suggestions);
- }
- }, [appSuggestions, currentSearchModuleRoute]);
-
- const containerRef = useRef(null);
- const addFocus = useCallback(() => setInputHasFocus(true), []);
- const removeFocus = useCallback(() => setInputHasFocus(false), []);
-
- useEffect(() => {
- const ref = inputRef.current;
- const runSearchOnKeyUp = (ev: KeyboardEvent): void => {
- if (ev.key === 'Enter') {
- onSearch();
- removeFocus();
- }
- };
- if (ref) {
- ref.addEventListener('keyup', runSearchOnKeyUp);
- }
-
- return (): void => {
- if (ref) {
- ref.removeEventListener('keyup', runSearchOnKeyUp);
- }
- };
- }, [onSearch, removeFocus]);
-
- const disableOptions = useMemo(() => options.length <= 0 || isTyping, [options, isTyping]);
-
- const placeholder = useMemo(
- () =>
- inputHasFocus && currentSearchModuleRoute
- ? t('search.active_input_label', 'Separate your keywords by a comma or pressing TAB')
- : t('search.idle_input_label', 'Search in {{module}}', {
- module: moduleLabel
- }),
- [currentSearchModuleRoute, inputHasFocus, moduleLabel, t]
- );
-
- const clearButtonPlaceholder = useMemo(
- () =>
- showClear || isTyping
- ? t('search.clear', 'Clear search input')
- : t('search.already_clear', 'Search input is already clear'),
- [showClear, t, isTyping]
- );
-
- const searchButtonsAreDisabled = useMemo(
- () => (isTyping ? false : !showClear),
- [showClear, isTyping]
- );
-
- const searchBtnTooltipLabel = useMemo(() => {
- if (!searchButtonsAreDisabled && searchInputValue.length > 0) {
- return t('search.start', 'Start search');
- }
- if (inputHasFocus) {
- return t(
- 'search.type_or_choose_suggestion',
- 'Type or choose some keywords to start a search'
- );
- }
- if (query.length > 0) {
- return t('label.edit_to_start_search', 'Edit your search to start a new one');
- }
- return t('search.type_to_start_search', 'Type some keywords to start a search');
- }, [searchButtonsAreDisabled, searchInputValue.length, inputHasFocus, query.length, t]);
-
- const onChipAdd = useCallback>(
- (newChip) => {
- setIsTyping(false);
- setInputTyped('');
- if (currentSearchModuleRoute) {
- const suggestions = filter(
- appSuggestions,
- (suggestion) => suggestion?.app === currentSearchModuleRoute
- ).slice(0, 5);
-
- setOptions(suggestions);
- }
- return {
- label: typeof newChip === 'string' ? newChip : '',
- value: newChip,
- hasAvatar: false
- };
- },
- [appSuggestions, currentSearchModuleRoute]
- );
-
- useEffect(() => {
- setSearchInputValue(map(query, (queryChip) => ({ ...queryChip, disabled: searchDisabled })));
- }, [searchDisabled, query]);
-
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {!searchButtonsAreDisabled && (
-
-
-
-
-
- )}
-
-
-
-
-
-
-
-
-
- );
-};
diff --git a/src/search/search-store.tsx b/src/search/search-store.tsx
deleted file mode 100644
index a01eb18ec..000000000
--- a/src/search/search-store.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2021 Zextras
- *
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import { isFunction } from 'lodash';
-import { create } from 'zustand';
-
-import type { QueryChip, SearchState } from '../types/search';
-
-// extra currying as suggested in https://github.com/pmndrs/zustand/blob/main/docs/guides/typescript.md#basic-usage
-export const useSearchStore = create()((set, get) => ({
- query: [],
- searchDisabled: false,
- tooltip: undefined,
- setSearchDisabled: (searchDisabled: boolean, tooltip?: string): void =>
- set({ searchDisabled, tooltip }),
- updateQuery: (query: Array | ((q: Array) => Array)): void =>
- set({ query: isFunction(query) ? query(get().query) : query }),
- updateModule: (module: string): void => set({ module })
-}));
diff --git a/src/search/useSearchModule.ts b/src/search/useSearchModule.ts
deleted file mode 100644
index bc2357a6d..000000000
--- a/src/search/useSearchModule.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2024 Zextras
- *
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import { useSearchStore } from './search-store';
-
-export const SEARCH_MODULE_KEY = 'search_module';
-
-export const setSearchModule = (newModule: string): void => {
- sessionStorage.setItem(SEARCH_MODULE_KEY, newModule);
- useSearchStore.getState().updateModule(newModule);
-};
-export const useSearchModule = (): [
- module: string | undefined,
- setModule: (module: string) => void
-] => {
- const { module } = useSearchStore();
-
- return [module ?? sessionStorage.getItem(SEARCH_MODULE_KEY) ?? undefined, setSearchModule];
-};
diff --git a/src/shell/shell-header.tsx b/src/shell/shell-header.tsx
index 22fc9ba57..30bc4766f 100644
--- a/src/shell/shell-header.tsx
+++ b/src/shell/shell-header.tsx
@@ -13,14 +13,14 @@ import {
Responsive,
useScreenMode
} from '@zextras/carbonio-design-system';
+import type { SearchBar as SearchUISearchBar } from '@zextras/carbonio-search-ui';
import styled from 'styled-components';
import { CreationButton } from './creation-button';
import { Logo } from './logo';
import { BOARD_CONTAINER_ZINDEX, HEADER_BAR_HEIGHT } from '../constants';
import { useDarkMode } from '../dark-mode/use-dark-mode';
-import { SearchBar } from '../search/search-bar';
-import { useAppStore } from '../store/app';
+import { useIntegratedComponent } from '../store/integrations/hooks';
const StyledLogo = styled(Logo)`
height: 2rem;
@@ -38,7 +38,8 @@ const ShellHeader = ({ children }: ShellHeaderProps): React.JSX.Element => {
const { darkReaderStatus } = useDarkMode();
const screenMode = useScreenMode();
- const searchEnabled = useAppStore((s) => s.views.search.length > 0);
+ const [SearchBar, isSearchBarAvailable] =
+ useIntegratedComponent('search-bar');
return (
{
- {searchEnabled && }
+ {isSearchBarAvailable && }
{children}
diff --git a/src/shell/shell-primary-bar.test.tsx b/src/shell/shell-primary-bar.test.tsx
index cc2fc4537..28619d2a4 100644
--- a/src/shell/shell-primary-bar.test.tsx
+++ b/src/shell/shell-primary-bar.test.tsx
@@ -5,7 +5,7 @@
*/
import React from 'react';
-import { act, screen, within } from '@testing-library/react';
+import { act, screen } from '@testing-library/react';
import { Button, Text } from '@zextras/carbonio-design-system';
import { produce } from 'immer';
import { Route, Switch, useParams, useRouteMatch } from 'react-router-dom';
@@ -14,10 +14,9 @@ import AppViewContainer from './app-view-container';
import ShellPrimaryBar from './shell-primary-bar';
import { DefaultViewsRegister } from '../boot/app/default-views';
import { usePushHistoryCallback } from '../history/hooks';
-import { ModuleSelector } from '../search/module-selector';
import { useAccountStore } from '../store/account';
import { useAppStore } from '../store/app';
-import { ICONS, TESTID_SELECTORS } from '../tests/constants';
+import { ICONS } from '../tests/constants';
import { setup } from '../tests/utils';
import type { AccountState } from '../types/account';
import type { PrimaryBarView } from '../types/apps';
@@ -25,7 +24,6 @@ import type { PrimaryBarView } from '../types/apps';
const ShellWrapper = (): React.JSX.Element => (
<>
-
>
@@ -212,224 +210,6 @@ describe('Shell primary bar', () => {
expect(screen.queryByText('files view')).not.toBeInTheDocument();
});
- test('When I move to the search module from any other component the search header selector and search view are switched based on the module from which I originate', async () => {
- const { getByRoleWithIcon, user } = setup();
-
- act(() => {
- useAppStore.getState().setApps([
- {
- commit: '',
- description: 'Mails module',
- display: 'Mails',
- icon: 'MailModOutline',
- js_entrypoint: '',
- name: 'carbonio-mails-ui',
- priority: 1,
- type: 'carbonio',
- version: '0.0.1'
- },
- {
- commit: '',
- description: 'Files module',
- display: 'Files',
- icon: 'DriveOutline',
- js_entrypoint: '',
- name: 'carbonio-files-ui',
- priority: 2,
- type: 'carbonio',
- version: '0.0.1'
- }
- ]);
- useAppStore.getState().addRoute({
- id: 'mails',
- route: 'mails',
- position: 1,
- visible: true,
- label: 'Mails',
- primaryBar: 'MailModOutline',
- appView: MailsView,
- badge: { show: false },
- app: 'carbonio-mails-ui'
- });
-
- useAppStore.getState().addRoute({
- id: 'files',
- route: 'files',
- position: 2,
- visible: true,
- label: 'Files',
- primaryBar: 'DriveOutline',
- appView: FilesView,
- badge: { show: false },
- app: 'carbonio-files-ui'
- });
-
- useAppStore.getState().addSearchView({
- route: 'files',
- component: (): React.JSX.Element => files search view,
- label: 'Files',
- id: 'files',
- app: 'carbonio-files-ui',
- icon: 'DriveOutline',
- position: 2
- });
- useAppStore.getState().addSearchView({
- route: 'mails',
- component: (): React.JSX.Element => mails search view,
- label: 'Mails',
- id: 'mails',
- app: 'carbonio-mails-ui',
- icon: 'MailModOutline',
- position: 1
- });
- });
-
- const mailsIcon = getByRoleWithIcon('button', { icon: 'MailModOutline' });
- expect(mailsIcon).toBeVisible();
- expect(mailsIcon).toBeEnabled();
- const filesIcon = getByRoleWithIcon('button', { icon: 'DriveOutline' });
- expect(filesIcon).toBeVisible();
- expect(filesIcon).toBeEnabled();
- const searchIcon = getByRoleWithIcon('button', { icon: 'SearchModOutline' });
- expect(searchIcon).toBeVisible();
- expect(searchIcon).toBeEnabled();
-
- expect(screen.getByText('default mails view')).toBeVisible();
- expect(screen.queryByText('files view')).not.toBeInTheDocument();
-
- await user.click(filesIcon);
- expect(screen.getByText('files view')).toBeVisible();
- expect(screen.queryByText('default mails view')).not.toBeInTheDocument();
-
- await user.click(searchIcon);
-
- expect(screen.getByText('files search view')).toBeVisible();
- expect(
- within(screen.getByTestId(TESTID_SELECTORS.headerModuleSelector)).getByText('Files')
- ).toBeVisible();
-
- expect(screen.queryByText('mails search view')).not.toBeInTheDocument();
- expect(screen.queryByText('default mails view')).not.toBeInTheDocument();
- expect(screen.queryByText('files view')).not.toBeInTheDocument();
- });
-
- test('When I move to the search module from any other component the search header selector and search view are switched based on the module from which I originate, also if is not the first access to search module', async () => {
- const { getByRoleWithIcon, user } = setup();
-
- act(() => {
- useAppStore.getState().setApps([
- {
- commit: '',
- description: 'Mails module',
- display: 'Mails',
- icon: 'MailModOutline',
- js_entrypoint: '',
- name: 'carbonio-mails-ui',
- priority: 1,
- type: 'carbonio',
- version: '0.0.1'
- },
- {
- commit: '',
- description: 'Files module',
- display: 'Files',
- icon: 'DriveOutline',
- js_entrypoint: '',
- name: 'carbonio-files-ui',
- priority: 2,
- type: 'carbonio',
- version: '0.0.1'
- }
- ]);
- useAppStore.getState().addRoute({
- id: 'mails',
- route: 'mails',
- position: 1,
- visible: true,
- label: 'Mails',
- primaryBar: 'MailModOutline',
- appView: MailsView,
- badge: { show: false },
- app: 'carbonio-mails-ui'
- });
-
- useAppStore.getState().addRoute({
- id: 'files',
- route: 'files',
- position: 2,
- visible: true,
- label: 'Files',
- primaryBar: 'DriveOutline',
- appView: FilesView,
- badge: { show: false },
- app: 'carbonio-files-ui'
- });
-
- useAppStore.getState().addSearchView({
- route: 'files',
- component: (): React.JSX.Element => files search view,
- label: 'Files',
- id: 'files',
- app: 'carbonio-files-ui',
- icon: 'DriveOutline',
- position: 2
- });
- useAppStore.getState().addSearchView({
- route: 'mails',
- component: (): React.JSX.Element => mails search view,
- label: 'Mails',
- id: 'mails',
- app: 'carbonio-mails-ui',
- icon: 'MailModOutline',
- position: 1
- });
- });
-
- const mailsIcon = getByRoleWithIcon('button', { icon: 'MailModOutline' });
- expect(mailsIcon).toBeVisible();
- expect(mailsIcon).toBeEnabled();
- const filesIcon = getByRoleWithIcon('button', { icon: 'DriveOutline' });
- expect(filesIcon).toBeVisible();
- expect(filesIcon).toBeEnabled();
- const searchIcon = getByRoleWithIcon('button', { icon: 'SearchModOutline' });
- expect(searchIcon).toBeVisible();
- expect(searchIcon).toBeEnabled();
-
- expect(screen.getByText('default mails view')).toBeVisible();
- expect(screen.queryByText('files view')).not.toBeInTheDocument();
-
- await user.click(filesIcon);
- expect(screen.getByText('files view')).toBeVisible();
- expect(screen.queryByText('default mails view')).not.toBeInTheDocument();
-
- await user.click(searchIcon);
-
- expect(screen.getByText('files search view')).toBeVisible();
- expect(
- within(screen.getByTestId(TESTID_SELECTORS.headerModuleSelector)).getByText('Files')
- ).toBeVisible();
-
- expect(screen.queryByText('mails search view')).not.toBeInTheDocument();
- expect(screen.queryByText('default mails view')).not.toBeInTheDocument();
- expect(screen.queryByText('files view')).not.toBeInTheDocument();
-
- await user.click(mailsIcon);
- expect(screen.getByText('default mails view')).toBeVisible();
- expect(screen.queryByText('files view')).not.toBeInTheDocument();
- expect(screen.queryByText('files search view')).not.toBeInTheDocument();
-
- await user.click(searchIcon);
-
- expect(screen.getByText('mails search view')).toBeVisible();
- expect(
- within(screen.getByTestId(TESTID_SELECTORS.headerModuleSelector)).getByText('Mails')
- ).toBeVisible();
-
- expect(screen.queryByText('files search view')).not.toBeInTheDocument();
- expect(screen.queryByText('default mails view')).not.toBeInTheDocument();
- expect(screen.queryByText('files view')).not.toBeInTheDocument();
- });
-
test('When zimbraFeatureOptionsEnabled is TRUE the setting icon is visible in primary bar', async () => {
useAccountStore.setState(
produce((state: AccountState) => {
@@ -547,7 +327,7 @@ describe('Shell primary bar', () => {
label: 'App One',
route: 'app1',
position: 1,
- badge: { show: true, icon: ICONS.search, showCount: true },
+ badge: { show: true, icon: 'Airplane', showCount: true },
visible: true,
component: 'People'
}
@@ -557,7 +337,7 @@ describe('Shell primary bar', () => {
}));
setup();
expect(screen.queryByTestId('badge-counter')).not.toBeInTheDocument();
- expect(screen.getByTestId(`icon: ${ICONS.search}`)).toBeVisible();
+ expect(screen.getByTestId('icon: Airplane')).toBeVisible();
});
});
});
diff --git a/src/shell/shell-primary-bar.tsx b/src/shell/shell-primary-bar.tsx
index 3d4961679..c6c57fa7b 100644
--- a/src/shell/shell-primary-bar.tsx
+++ b/src/shell/shell-primary-bar.tsx
@@ -13,7 +13,7 @@ import styled from 'styled-components';
import BadgeWrap from './badge-wrap';
import { AppContextProvider } from '../boot/app/app-context-provider';
-import { BOARD_CONTAINER_ZINDEX, PRIMARY_BAR_WIDTH, SEARCH_APP_ID } from '../constants';
+import { BOARD_CONTAINER_ZINDEX, PRIMARY_BAR_WIDTH } from '../constants';
import { useCurrentRoute } from '../history/hooks';
import { useAppStore } from '../store/app';
import { minimizeBoards, reopenBoards, useBoardStore } from '../store/boards';
@@ -115,7 +115,7 @@ const ShellPrimaryBar = (): React.JSX.Element | null => {
}, [primaryBarViews]);
useEffect(() => {
- if (activeRoute && activeRoute.id !== SEARCH_APP_ID) {
+ if (activeRoute) {
routesRef.current = {
...routesRef.current,
[activeRoute.id]: `${trim(pathname, '/')}${search}`
diff --git a/src/store/app/store.ts b/src/store/app/store.ts
index e3e3cd55d..85d290d11 100644
--- a/src/store/app/store.ts
+++ b/src/store/app/store.ts
@@ -12,8 +12,6 @@ import { create } from 'zustand';
import { normalizeApp } from './utils';
import { SHELL_APP_ID } from '../../constants';
-import { useSearchStore } from '../../search/search-store';
-import { SEARCH_MODULE_KEY } from '../../search/useSearchModule';
import type {
AppRoute,
AppRouteDescriptor,
@@ -23,7 +21,6 @@ import type {
CarbonioModule,
PrimaryAccessoryView,
PrimaryBarView,
- SearchView,
SecondaryAccessoryView,
SecondaryBarView,
SettingsView,
@@ -43,7 +40,6 @@ export type AppState = {
board: Array;
utilityBar: Array;
settings: Array;
- search: Array;
primaryBarAccessories: Array;
secondaryBarAccessories: Array;
};
@@ -61,8 +57,6 @@ export type AppActions = {
removeBoardView: (id: string) => void;
addSettingsView: (data: SettingsView) => string;
removeSettingsView: (id: string) => void;
- addSearchView: (data: SearchView) => string;
- removeSearchView: (id: string) => void;
addUtilityView: (data: UtilityView) => string;
removeUtilityView: (id: string) => void;
addPrimaryAccessoryView: (data: PrimaryAccessoryView) => string;
@@ -125,7 +119,6 @@ const initialState: AppState = {
board: [],
utilityBar: [],
settings: [],
- search: [],
primaryBarAccessories: [],
secondaryBarAccessories: []
}
@@ -276,45 +269,6 @@ export const useAppStore = create()((set, get) => ({
})
);
},
- addSearchView: (data): string => {
- const {
- focusMode,
- views: { search }
- } = get();
-
- const lastSearchModule = sessionStorage.getItem(SEARCH_MODULE_KEY) ?? undefined;
- const currentSearchModule = useSearchStore.getState().module;
-
- if (currentSearchModule !== lastSearchModule || currentSearchModule === undefined) {
- const currentModuleSearchView = search.find(
- (searchView) => searchView.route === currentSearchModule
- );
- if (
- !currentModuleSearchView ||
- data.position < currentModuleSearchView?.position ||
- data.route === lastSearchModule
- ) {
- useSearchStore.getState().updateModule(data.route);
- }
- }
-
- if (focusMode && data.route !== focusMode) {
- return FOCUS_MODE_RESPONSE;
- }
- set(
- produce((state) => {
- addAndSort(state.views.search, data);
- })
- );
- return data.id;
- },
- removeSearchView: (id): void => {
- set(
- produce((state) => {
- removeById(state.views.search, id);
- })
- );
- },
addUtilityView: (data): string => {
set(
produce((state) => {
diff --git a/src/store/app/utils.tsx b/src/store/app/utils.tsx
index ab3ec505f..ae56c2682 100644
--- a/src/store/app/utils.tsx
+++ b/src/store/app/utils.tsx
@@ -14,7 +14,6 @@ import type {
BoardView,
CarbonioModule,
PrimaryAccessoryView,
- SearchView,
SecondaryAccessoryView,
SettingsView,
UtilityView
@@ -81,18 +80,6 @@ export const normalizeSettingsView = (
subSections: data.subSections ?? []
};
};
-export const normalizeSearchView = (data: Partial, app: CarbonioModule): SearchView => {
- const route = trim(data.route ?? app.name, '/');
- return {
- app: app.name,
- route,
- id: data?.id ?? route,
- component: data?.component ?? FallbackView,
- label: data.label ?? app.display,
- icon: data.icon ?? app.icon,
- position: data.position ?? app.priority ?? 99
- };
-};
export const normalizeUtilityView = (
data: Partial,
app: CarbonioModule
diff --git a/src/store/i18n/store.ts b/src/store/i18n/store.ts
index f7135cd3c..7900149ac 100644
--- a/src/store/i18n/store.ts
+++ b/src/store/i18n/store.ts
@@ -6,7 +6,8 @@
import type { i18n, InitOptions } from 'i18next';
import i18next from 'i18next';
-import Backend from 'i18next-http-backend';
+import ChainedBackend from 'i18next-chained-backend';
+import HttpBackend from 'i18next-http-backend';
import { produce } from 'immer';
import { dropRight, forEach, reduce } from 'lodash';
import { initReactI18next } from 'react-i18next';
@@ -94,17 +95,26 @@ export const useI18nStore = create()((set) => ({
newI18n
// load translation using http -> see /public/locales (i.e. https://github.com/i18next/react-i18next/tree/master/example/react/public/locales)
// learn more: https://github.com/i18next/i18next-http-backend
- .use(Backend)
+ .use(ChainedBackend)
// init i18next
// for all options read: https://www.i18next.com/overview/configuration-options
.init({
...defaultI18nInitOptions,
lng: locale,
backend: {
- loadPath:
- app.name === SHELL_APP_ID
- ? `${BASE_PATH}/i18n/{{lng}}.json`
- : `${dropRight(app.js_entrypoint.split('/')).join('/')}/i18n/{{lng}}.json`
+ backends: [HttpBackend, HttpBackend],
+ backendOptions: [
+ {
+ loadPath:
+ app.name === SHELL_APP_ID
+ ? `${BASE_PATH}/i18n/{{lng}}.json`
+ : `${dropRight(app.js_entrypoint.split('/')).join('/')}/i18n/{{lng}}.json`
+ },
+ {
+ // fallback to shell for every module
+ loadPath: `${BASE_PATH}/i18n/{{lng}}.json`
+ }
+ ]
}
});
// eslint-disable-next-line no-param-reassign
@@ -121,7 +131,7 @@ export const useI18nStore = create()((set) => ({
}));
defaultI18n
- .use(Backend)
+ .use(HttpBackend)
// pass the i18n instance to react-i18next.
.use(initReactI18next)
// init i18next
diff --git a/src/store/integrations/getters.tsx b/src/store/integrations/getters.tsx
index 89b6bf41c..5e0e5a774 100644
--- a/src/store/integrations/getters.tsx
+++ b/src/store/integrations/getters.tsx
@@ -15,16 +15,23 @@ import {
import type { Action } from '../../types/integrations';
import type { AnyFunction } from '../../utils/typeUtils';
-export const getIntegratedFunction = (id: string): [AnyFunction, boolean] => {
+export const getIntegratedFunction = (
+ id: string
+): [TFunction, boolean] => {
const integration = useIntegrationsStore.getState().functions?.[id];
- return integration ? [integration, true] : [(): void => undefined, false];
+ return integration
+ ? [integration as TFunction, true]
+ : [((): void => undefined) as TFunction, false];
};
-export const getIntegratedComponent = (
+export const getIntegratedComponent = <
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ TComponent extends React.ComponentType = React.ComponentType>
+>(
id: string
-): [React.FunctionComponent>, boolean] => {
+): [TComponent, boolean] => {
const integration = useIntegrationsStore.getState().components?.[id];
- return buildIntegrationComponent(integration);
+ return buildIntegrationComponent(integration);
};
export const getActions = (
diff --git a/src/store/integrations/hooks.tsx b/src/store/integrations/hooks.tsx
index b57e1966a..a689d8500 100644
--- a/src/store/integrations/hooks.tsx
+++ b/src/store/integrations/hooks.tsx
@@ -17,16 +17,21 @@ import {
import type { Action } from '../../types/integrations';
import type { AnyFunction } from '../../utils/typeUtils';
-export const useIntegratedFunction = (id: string): [AnyFunction, boolean] => {
+export const useIntegratedFunction = (
+ id: string
+): [TFunction, boolean] => {
const integration = useIntegrationsStore((s) => s.functions?.[id]);
return buildIntegrationFunction(integration);
};
-export const useIntegratedComponent = (
+export const useIntegratedComponent = <
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ TComponent extends React.ComponentType = React.ComponentType>
+>(
id: string
-): [React.FunctionComponent>, boolean] => {
+): [TComponent, boolean] => {
const integration = useIntegrationsStore((s) => s.components?.[id]);
- return useMemo(() => buildIntegrationComponent(integration), [integration]);
+ return useMemo(() => buildIntegrationComponent(integration), [integration]);
};
export const useActions = (
diff --git a/src/store/integrations/utils.tsx b/src/store/integrations/utils.tsx
index a8084e575..64721140f 100644
--- a/src/store/integrations/utils.tsx
+++ b/src/store/integrations/utils.tsx
@@ -12,24 +12,28 @@ import { AppContextProvider } from '../../boot/app/app-context-provider';
import type { Action } from '../../types/integrations';
import type { AnyFunction } from '../../utils/typeUtils';
-export function buildIntegrationComponent(
+export function buildIntegrationComponent(
integration: IntegrationsState['components'][string]
-): [React.FunctionComponent>, boolean] {
+): [TComponent, boolean] {
if (integration) {
- const IntegrationComponent = (props: Record): React.JSX.Element => (
+ const IntegrationComponent = (
+ props: React.ComponentPropsWithRef
+ ): React.JSX.Element => (
);
- return [IntegrationComponent, true];
+ return [IntegrationComponent as TComponent, true];
}
- return [(): null => null, false];
+ return [((): null => null) as unknown as TComponent, false];
}
-export function buildIntegrationFunction(
+export function buildIntegrationFunction(
integration: IntegrationsState['functions'][string]
-): [AnyFunction, boolean] {
- return integration ? [integration, true] : [(): void => undefined, false];
+): [TFunction, boolean] {
+ return integration
+ ? [integration as TFunction, true]
+ : [((): void => undefined) as TFunction, false];
}
export function buildIntegrationActions(
diff --git a/src/tests/constants.ts b/src/tests/constants.ts
index 54946ad8c..c89a97fdb 100644
--- a/src/tests/constants.ts
+++ b/src/tests/constants.ts
@@ -138,9 +138,7 @@ export const ICONS = {
reduceBoard: 'CollapseOutline',
resetBoardSize: 'DiagonalArrowLeftDown',
unCollapseBoard: 'BoardOpen',
- search: 'Search',
- settings: 'SettingsModOutline',
- clearSearch: 'BackspaceOutline'
+ settings: 'SettingsModOutline'
};
export const TESTID_SELECTORS = {
diff --git a/src/types/apps/index.ts b/src/types/apps/index.ts
index 8c375a3ca..9643f9462 100644
--- a/src/types/apps/index.ts
+++ b/src/types/apps/index.ts
@@ -10,8 +10,6 @@ import type React from 'react';
import type { DefaultTheme } from 'styled-components';
-import type { QueryChip } from '../search';
-
export type CarbonioModule = {
commit: string;
description: string;
@@ -61,11 +59,6 @@ export type SecondaryBarComponentProps = { expanded: boolean };
export type AppViewComponentProps = {};
export type BoardViewComponentProps = {};
export type SettingsViewProps = {};
-export type SearchViewProps = {
- useQuery: () => [QueryChip[], Function];
- ResultsHeader: React.ComponentType<{ label: string }>;
- useDisableSearch: () => [boolean, Function];
-};
export type PrimaryAccessoryViewProps = {};
export type SecondaryAccessoryViewProps = { expanded: boolean };
export type PanelMode = 'closed' | 'overlap' | 'open';
@@ -98,12 +91,6 @@ export type SettingsView = CarbonioView & {
subSections?: Array;
};
-export type SearchView = CarbonioView & {
- icon: string;
- label: string;
- position: number;
-};
-
export type PrimaryAccessoryView = CarbonioAccessoryView & {
component: string | ComponentType;
onClick?: (ev: KeyboardEvent | React.MouseEvent | undefined) => void;
diff --git a/src/types/search/index.ts b/src/types/search/index.ts
deleted file mode 100644
index cf901b732..000000000
--- a/src/types/search/index.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2021 Zextras
- *
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import type { QueryChip } from './items';
-
-export * from './items';
-export type SearchState = {
- query: Array;
- module?: string;
- searchDisabled: boolean;
- tooltip?: string;
- setSearchDisabled: (searchDisabled: boolean) => void;
- updateQuery: (query: Array | ((q: Array) => Array)) => void;
- updateModule: (module: string) => void;
-};
diff --git a/src/types/search/items.ts b/src/types/search/items.ts
deleted file mode 100644
index d7f5fdf71..000000000
--- a/src/types/search/items.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2022 Zextras
- *
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-import type { ChipItem } from '@zextras/carbonio-design-system';
-
-export interface QueryItem {
- value?: string;
- app?: string;
-}
-
-export type QueryChip = ChipItem & QueryItem;
diff --git a/tsconfig.json b/tsconfig.json
index 90cf8085f..8a537df86 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,15 +1,11 @@
{
- "extends": "@zextras/carbonio-ui-configs/rules/typescript.json",
+ "extends": "@zextras/carbonio-ui-configs/rules/tsconfig.type-check.json",
"compilerOptions": {
- "skipLibCheck": true,
- "resolveJsonModule": true
+ "noUncheckedIndexedAccess": false
},
"exclude": [
"node_modules",
"dist",
"lib"
- ],
- "ts-node": {
- "transpileOnly": true
- }
+ ]
}