-
Notifications
You must be signed in to change notification settings - Fork 172
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Remove discover] Migrate AngularJS routing to React #6680
Comments
Update 16/05/2024The documentation available in the code is analyzed in the Example Routing_Example (Example/Routing_Example), however, the PATH with the Routing Development Documentation presented by the Readme no longer exists: |
Research 17/05/2024The possibility of using |
Development branch: |
Update 20/05/2024
|
Update 2024/05/21
|
Update 21/05/2024The AppMountParameters interface/** @public */
export interface AppMountParameters<HistoryLocationState = unknown> {
/**
* The container element to render the application into.
*/
element: HTMLElement;
/**
* A scoped history instance for your application. Should be used to wire up
* your applications Router.
*
* @example
* How to configure react-router with a base path:
*
* ```ts
* // inside your plugin's setup function
* export class MyPlugin implements Plugin {
* setup({ application }) {
* application.register({
* id: 'my-app',
* appRoute: '/my-app',
* async mount(params) {
* const { renderApp } = await import('./application');
* return renderApp(params);
* },
* });
* }
* }
* ```
*
* ```ts
* // application.tsx
* import React from 'react';
* import ReactDOM from 'react-dom';
* import { Router, Route } from 'react-router-dom';
*
* import { CoreStart, AppMountParameters } from 'src/core/public';
* import { MyPluginDepsStart } from './plugin';
*
* export renderApp = ({ element, history }: AppMountParameters) => {
* ReactDOM.render(
* <Router history={history}>
* <Route path="/" exact component={HomePage} />
* </Router>,
* element
* );
*
* return () => ReactDOM.unmountComponentAtNode(element);
* }
* ```
*/
history: ScopedHistory<HistoryLocationState>;
/**
* The route path for configuring navigation to the application.
* This string should not include the base path from HTTP.
*
* @deprecated Use {@link AppMountParameters.history} instead.
*
* @example
*
* How to configure react-router with a base path:
*
* ```ts
* // inside your plugin's setup function
* export class MyPlugin implements Plugin {
* setup({ application }) {
* application.register({
* id: 'my-app',
* appRoute: '/my-app',
* async mount(params) {
* const { renderApp } = await import('./application');
* return renderApp(params);
* },
* });
* }
* }
* ```
*
* ```ts
* // application.tsx
* import React from 'react';
* import ReactDOM from 'react-dom';
* import { BrowserRouter, Route } from 'react-router-dom';
*
* import { CoreStart, AppMountParameters } from 'src/core/public';
* import { MyPluginDepsStart } from './plugin';
*
* export renderApp = ({ appBasePath, element }: AppMountParameters) => {
* ReactDOM.render(
* // pass `appBasePath` to `basename`
* <BrowserRouter basename={appBasePath}>
* <Route path="/" exact component={HomePage} />
* </BrowserRouter>,
* element
* );
*
* return () => ReactDOM.unmountComponentAtNode(element);
* }
* ```
*/
appBasePath: string;
/**
* A function that can be used to register a handler that will be called
* when the user is leaving the current application, allowing to
* prompt a confirmation message before actually changing the page.
*
* This will be called either when the user goes to another application, or when
* trying to close the tab or manually changing the url.
*
* @example
*
* ```ts
* // application.tsx
* import React from 'react';
* import ReactDOM from 'react-dom';
* import { BrowserRouter, Route } from 'react-router-dom';
*
* import { CoreStart, AppMountParameters } from 'src/core/public';
* import { MyPluginDepsStart } from './plugin';
*
* export renderApp = ({ element, history, onAppLeave }: AppMountParameters) => {
* const { renderApp, hasUnsavedChanges } = await import('./application');
* onAppLeave(actions => {
* if(hasUnsavedChanges()) {
* return actions.confirm('Some changes were not saved. Are you sure you want to leave?');
* }
* return actions.default();
* });
* return renderApp({ element, history });
* }
* ```
*/
onAppLeave: (handler: AppLeaveHandler) => void;
/**
* A function that can be used to set the mount point used to populate the application action container
* in the chrome header.
*
* Calling the handler multiple time will erase the current content of the action menu with the mount from the latest call.
* Calling the handler with `undefined` will unmount the current mount point.
* Calling the handler after the application has been unmounted will have no effect.
*
* @example
*
* ```ts
* // application.tsx
* import React from 'react';
* import ReactDOM from 'react-dom';
* import { BrowserRouter, Route } from 'react-router-dom';
*
* import { CoreStart, AppMountParameters } from 'src/core/public';
* import { MyPluginDepsStart } from './plugin';
*
* export renderApp = ({ element, history, setHeaderActionMenu }: AppMountParameters) => {
* const { renderApp } = await import('./application');
* const { renderActionMenu } = await import('./action_menu');
* setHeaderActionMenu((element) => {
* return renderActionMenu(element);
* })
* return renderApp({ element, history });
* }
* ```
*/
setHeaderActionMenu: (menuMount: MountPoint | undefined) => void;
/**
* Optional datasource id to pass while mounting app
*/
dataSourceId?: string;
} ScopedHistory class/**
* A wrapper around a `History` instance that is scoped to a particular base path of the history stack. Behaves
* similarly to the `basename` option except that this wrapper hides any history stack entries from outside the scope
* of this base path.
*
* This wrapper also allows Core and Plugins to share a single underlying global `History` instance without exposing
* the history of other applications.
*
* The {@link ScopedHistory.createSubHistory | createSubHistory} method is particularly useful for applications that
* contain any number of "sub-apps" which should not have access to the main application's history or basePath.
*
* @public
*/
export class ScopedHistory<HistoryLocationState = unknown>
implements History<HistoryLocationState> {
/**
* Tracks whether or not the user has left this history's scope. All methods throw errors if called after scope has
* been left.
*/
private isActive = true;
/**
* All active listeners on this history instance.
*/
private listeners = new Set<LocationListener<HistoryLocationState>>();
/**
* Array of the local history stack. Only stores {@link Location.key} to use tracking an index of the current
* position of the window in the history stack.
*/
private locationKeys: Array<string | undefined> = [];
/**
* The key of the current position of the window in the history stack.
*/
private currentLocationKeyIndex: number = 0;
constructor(private readonly parentHistory: History, private readonly basePath: string) {
const parentPath = this.parentHistory.location.pathname;
if (!parentPath.startsWith(basePath)) {
throw new Error(
`Browser location [${parentPath}] is not currently in expected basePath [${basePath}]`
);
}
this.locationKeys.push(this.parentHistory.location.key);
this.setupHistoryListener();
}
/**
* Creates a `ScopedHistory` for a subpath of this `ScopedHistory`. Useful for applications that may have sub-apps
* that do not need access to the containing application's history.
*
* @param basePath the URL path scope for the sub history
*/
public createSubHistory = <SubHistoryLocationState = unknown>(
basePath: string
): ScopedHistory<SubHistoryLocationState> => {
return new ScopedHistory<SubHistoryLocationState>(this, basePath);
};
/**
* The number of entries in the history stack, including all entries forwards and backwards from the current location.
*/
public get length() {
this.verifyActive();
return this.locationKeys.length;
}
/**
* The current location of the history stack.
*/
public get location() {
this.verifyActive();
return this.stripBasePath(this.parentHistory.location as Location<HistoryLocationState>);
}
/**
* The last action dispatched on the history stack.
*/
public get action() {
this.verifyActive();
return this.parentHistory.action;
}
/**
* Pushes a new location onto the history stack. If there are forward entries in the stack, they will be removed.
*
* @param pathOrLocation a string or location descriptor
* @param state
*/
public push = (
pathOrLocation: Path | LocationDescriptorObject<HistoryLocationState>,
state?: HistoryLocationState
): void => {
this.verifyActive();
if (typeof pathOrLocation === 'string') {
this.parentHistory.push(this.prependBasePath(pathOrLocation), state);
} else {
this.parentHistory.push(this.prependBasePath(pathOrLocation));
}
};
/**
* Replaces the current location in the history stack. Does not remove forward or backward entries.
*
* @param pathOrLocation a string or location descriptor
* @param state
*/
public replace = (
pathOrLocation: Path | LocationDescriptorObject<HistoryLocationState>,
state?: HistoryLocationState
): void => {
this.verifyActive();
if (typeof pathOrLocation === 'string') {
this.parentHistory.replace(this.prependBasePath(pathOrLocation), state);
} else {
this.parentHistory.replace(this.prependBasePath(pathOrLocation));
}
};
/**
* Send the user forward or backwards in the history stack.
*
* @param n number of positions in the stack to go. Negative numbers indicate number of entries backward, positive
* numbers for forwards. If passed 0, the current location will be reloaded. If `n` exceeds the number of
* entries available, this is a no-op.
*/
public go = (n: number): void => {
this.verifyActive();
if (n === 0) {
this.parentHistory.go(n);
} else if (n < 0) {
if (this.currentLocationKeyIndex + 1 + n >= 1) {
this.parentHistory.go(n);
}
} else if (n <= this.currentLocationKeyIndex + this.locationKeys.length - 1) {
this.parentHistory.go(n);
}
// no-op if no conditions above are met
};
/**
* Send the user one location back in the history stack. Equivalent to calling
* {@link ScopedHistory.go | ScopedHistory.go(-1)}. If no more entries are available backwards, this is a no-op.
*/
public goBack = () => {
this.verifyActive();
this.go(-1);
};
/**
* Send the user one location forward in the history stack. Equivalent to calling
* {@link ScopedHistory.go | ScopedHistory.go(1)}. If no more entries are available forwards, this is a no-op.
*/
public goForward = () => {
this.verifyActive();
this.go(1);
};
/**
* Not supported. Use {@link AppMountParameters.onAppLeave}.
*
* @remarks
* We prefer that applications use the `onAppLeave` API because it supports a more graceful experience that prefers
* a modal when possible, falling back to a confirm dialog box in the beforeunload case.
*/
public block = (
prompt?: boolean | string | TransitionPromptHook<HistoryLocationState>
): UnregisterCallback => {
throw new Error(
`history.block is not supported. Please use the AppMountParameters.onAppLeave API.`
);
};
/**
* Adds a listener for location updates.
*
* @param listener a function that receives location updates.
* @returns an function to unsubscribe the listener.
*/
public listen = (
listener: (location: Location<HistoryLocationState>, action: Action) => void
): UnregisterCallback => {
this.verifyActive();
this.listeners.add(listener);
return () => {
this.listeners.delete(listener);
};
};
/**
* Creates an href (string) to the location.
* If `prependBasePath` is true (default), it will prepend the location's path with the scoped history basePath.
*
* @param location
* @param prependBasePath
*/
public createHref = (
location: LocationDescriptorObject<HistoryLocationState>,
{ prependBasePath = true }: { prependBasePath?: boolean } = {}
): Href => {
this.verifyActive();
if (prependBasePath) {
location = this.prependBasePath(location);
if (location.pathname === undefined) {
// we always want to create an url relative to the basePath
// so if pathname is not present, we use the history's basePath as default
// we are doing that here because `prependBasePath` should not
// alter pathname for other method calls
location.pathname = this.basePath;
}
}
return this.parentHistory.createHref(location);
};
private prependBasePath(path: Path): Path;
private prependBasePath(
location: LocationDescriptorObject<HistoryLocationState>
): LocationDescriptorObject<HistoryLocationState>;
/**
* Prepends the scoped base path to the Path or Location
*/
private prependBasePath(
pathOrLocation: Path | LocationDescriptorObject<HistoryLocationState>
): Path | LocationDescriptorObject<HistoryLocationState> {
if (typeof pathOrLocation === 'string') {
return this.prependBasePathToString(pathOrLocation);
} else {
return {
...pathOrLocation,
pathname:
pathOrLocation.pathname !== undefined
? this.prependBasePathToString(pathOrLocation.pathname)
: undefined,
};
}
}
/**
* Prepends the base path to string.
*/
private prependBasePathToString(path: string): string {
return path.length ? `${this.basePath}/${path}`.replace(/\/{2,}/g, '/') : this.basePath;
}
/**
* Removes the base path from a location.
*/
private stripBasePath(location: Location<HistoryLocationState>): Location<HistoryLocationState> {
return {
...location,
pathname: location.pathname.replace(new RegExp(`^${this.basePath}`), ''),
};
}
/** Called on each public method to ensure that we have not fallen out of scope yet. */
private verifyActive() {
if (!this.isActive) {
throw new Error(
`ScopedHistory instance has fell out of navigation scope for basePath: ${this.basePath}`
);
}
}
/**
* Sets up the listener on the parent history instance used to follow navigation updates and track our internal
* state. Also forwards events to child listeners with the base path stripped from the location.
*/
private setupHistoryListener() {
const unlisten = this.parentHistory.listen((location, action) => {
// If the user navigates outside the scope of this basePath, tear it down.
if (!location.pathname.startsWith(this.basePath)) {
unlisten();
this.isActive = false;
return;
}
/**
* Track location keys using the same algorithm the browser uses internally.
* - On PUSH, remove all items that came after the current location and append the new location.
* - On POP, set the current location, but do not change the entries.
* - On REPLACE, override the location for the current index with the new location.
*/
if (action === 'PUSH') {
this.locationKeys = [
...this.locationKeys.slice(0, this.currentLocationKeyIndex + 1),
location.key,
];
this.currentLocationKeyIndex = this.locationKeys.indexOf(location.key); // should always be the last index
} else if (action === 'POP') {
this.currentLocationKeyIndex = this.locationKeys.indexOf(location.key);
} else if (action === 'REPLACE') {
this.locationKeys[this.currentLocationKeyIndex] = location.key;
} else {
throw new Error(`Unrecognized history action: ${action}`);
}
[...this.listeners].forEach((listener) => {
listener(this.stripBasePath(location as Location<HistoryLocationState>), action);
});
});
}
} Likewise, in the same file ( |
Update 2024/05/22
|
Update 22/05/2024Taking into account the needs to manage navigation in the app with respect to history and taking into account the possibility of using the OSD history mechanism. It is decided to generate a NavigationService diagramclassDiagram
class NavigationService {
- static instance: NavigationService
- history: History
+ static getInstance(history?: History): NavigationService
- constructor(history: History)
+ navigate(path: string, state?: any): void
+ replace(path: string, state?: any): void
+ goBack(): void
+ goForward(): void
+ go(n: number): void
+ getLocation(): Location
+ listen(listener: (location: Location, action: Action) => void): void
}
class History {
+ push(path: string, state?: any): void
+ replace(path: string, state?: any): void
+ goBack(): void
+ goForward(): void
+ go(n: number): void
+ location: Location
+ listen(listener: (location: Location, action: Action) => void): void
}
NavigationService --> History : uses
|
Update 2024/05/23
|
Update 2024/05/24
|
Route based on search URL parametersThe current URLs of the application uses a combination of paths and search parameters of the URL to display a view. For example, the The router provided by Example the implementation of the router based on the search parameters replacing the routing based on the state of the component: import { Redirect, Route, Switch } from '../../router-search';
// ...
export const ViewRouter = () => {
// Bussiness logic of component
return (
<Switch>
<Route path='?tab=syscollector'>
<MainModuleAgent agent={agent} section={tab} />
<MainSyscollector agent={agent} />
</Route>
<Route path='?tab=stats'>
<MainModuleAgent agent={agent} section={tab} />
<MainAgentStats agent={agent} />
</Route>
<Route path='?tab=configuration'>
<MainModuleAgent agent={agent} section={tab} />
<MainAgentStats agent={agent} />
</Route>
<Route path='?tab=welcome'>
<AgentsWelcome
switchTab={switchTab}
agent={agent}
pinAgent={pinnedAgentManager.pinAgent}
unPinAgent={pinnedAgentManager.unPinAgent}
/>
</Route>
<Redirect to='?tab=welcome'></Redirect>
</Switch>
)
} As these components have a similar interface and names to the provided by Moreover, a new hook was created to detect the changes on the search parameters, called Example: const CustomComponent = () => {
// The search contains all the search parameters as an object
const search = useRouterSearch();
return // render components
}
|
Update 2024/05/27
|
Update 28/05/2024
TODO: |
Update 29/05/2024
classDiagram
class NavigationService {
- static instance: NavigationService
- history: History
+ static getInstance(history?: History): NavigationService
+ getHistory(): History
+ getLocation(): Location
+ getHash(): string
+ getPathname(): string
+ getSearch(): string
+ getState(): any
+ getParams(): URLSearchParams
+ renewURL(params?: URLSearchParams): void
+ navigate(path: string, state?: any): void
+ replace(path: string, state?: any): void
+ goBack(): void
+ goForward(): void
+ go(n: number): void
+ reload(): void
+ listen(listener: (location: Location, action: Action) => void): () => void
}
class HashHistory {
+ push(path: string, state?: any): void
+ replace(path: string, state?: any): void
+ goBack(): void
+ goForward(): void
+ go(n: number): void
+ location: Location
+ listen(listener: (location: Location, action: Action) => void): void
}
NavigationService --> HashHistory : uses
TODO:
|
Update selected API or alerts index patternThe current application reloads the page when changing the selected API or index pattern using the selectors of the header. It should not be necessary if the application was designed to react to these changes and reloading the required data with the new selection. We decided to research about the possibility to avoid the page is refreshed when changing the selected API or index pattern, but it is not possible with out adapting the component to react to this changes. This is out of the scope of this issue, so we could manage it in another issue if we consider. So, I replaced the reload of AngularJS by a refresh of the page based on |
Update 30/05/2024
TODO:
navigateToModule
navigateToApp
navigateToUrl
|
Update 31/05/2024Since ApplicationStart interface: `src/core/public/application/types.ts`export interface ApplicationStart {
/**
* Gets the read-only capabilities.
*/
capabilities: RecursiveReadonly<Capabilities>;
/**
* Observable emitting the list of currently registered apps and their associated status.
*
* @remarks
* Applications disabled by {@link Capabilities} will not be present in the map. Applications manually disabled from
* the client-side using an {@link AppUpdater | application updater} are present, with their status properly set as `inaccessible`.
*/
applications$: Observable<ReadonlyMap<string, PublicAppInfo>>;
/**
* Navigate to a given app
*
* @param appId
* @param options - navigation options
*/
navigateToApp(appId: string, options?: NavigateToAppOptions): Promise<void>;
/**
* Navigate to given URL in a SPA friendly way when possible (when the URL will redirect to a valid application
* within the current basePath).
*
* The method resolves pathnames the same way browsers do when resolving a `<a href>` value. The provided `url` can be:
* - an absolute URL
* - an absolute path
* - a path relative to the current URL (window.location.href)
*
* If all these criteria are true for the given URL:
* - (only for absolute URLs) The origin of the URL matches the origin of the browser's current location
* - The resolved pathname of the provided URL/path starts with the current basePath (eg. /mybasepath/s/my-space)
* - The pathname segment after the basePath matches any known application route (eg. /app/<id>/ or any application's `appRoute` configuration)
*
* Then a SPA navigation will be performed using `navigateToApp` using the corresponding application and path.
* Otherwise, fallback to a full page reload to navigate to the url using `window.location.assign`
*
* @example
* ```ts
* // current url: `https://opensearch-dashboards:8080/base-path/s/my-space/app/dashboard`
*
* // will call `application.navigateToApp('discover', { path: '/some-path?foo=bar'})`
* application.navigateToUrl('https://opensearch-dashboards:8080/base-path/s/my-space/app/discover/some-path?foo=bar')
* application.navigateToUrl('/base-path/s/my-space/app/discover/some-path?foo=bar')
* application.navigateToUrl('./discover/some-path?foo=bar')
*
* // will perform a full page reload using `window.location.assign`
* application.navigateToUrl('https://elsewhere:8080/base-path/s/my-space/app/discover/some-path') // origin does not match
* application.navigateToUrl('/app/discover/some-path') // does not include the current basePath
* application.navigateToUrl('/base-path/s/my-space/app/unknown-app/some-path') // unknown application
* application.navigateToUrl('../discover') // resolve to `/base-path/s/my-space/discover` which is not a path of a known app.
* application.navigateToUrl('../../other-space/discover') // resolve to `/base-path/s/other-space/discover` which is not within the current basePath.
* ```
*
* @param url - an absolute URL, an absolute path or a relative path, to navigate to.
*/
navigateToUrl(url: string): Promise<void>;
/**
* Returns the absolute path (or URL) to a given app, including the global base path.
*
* By default, it returns the absolute path of the application (e.g `/basePath/app/my-app`).
* Use the `absolute` option to generate an absolute url instead (e.g `http://host:port/basePath/app/my-app`)
*
* Note that when generating absolute urls, the origin (protocol, host and port) are determined from the browser's current location.
*
* @param appId
* @param options.path - optional path inside application to deep link to
* @param options.absolute - if true, will returns an absolute url instead of a relative one
*/
getUrlForApp(appId: string, options?: { path?: string; absolute?: boolean }): string;
/**
* Register a context provider for application mounting. Will only be available to applications that depend on the
* plugin that registered this context. Deprecated, use {@link CoreSetup.getStartServices}.
*
* @deprecated
* @param contextName - The key of {@link AppMountContext} this provider's return value should be attached to.
* @param provider - A {@link IContextProvider} function
*/
registerMountContext<T extends keyof AppMountContext>(
contextName: T,
provider: IContextProvider<AppMountDeprecated, T>
): void;
/**
* An observable that emits the current application id and each subsequent id update.
*/
currentAppId$: Observable<string | undefined>;
} CoreStart interface: `src/core/public/index.ts`export interface CoreStart {
/** {@link ApplicationStart} */
application: ApplicationStart;
/** {@link ChromeStart} */
chrome: ChromeStart;
/** {@link DocLinksStart} */
docLinks: DocLinksStart;
/** {@link HttpStart} */
http: HttpStart;
/** {@link SavedObjectsStart} */
savedObjects: SavedObjectsStart;
/** {@link I18nStart} */
i18n: I18nStart;
/** {@link NotificationsStart} */
notifications: NotificationsStart;
/** {@link OverlayStart} */
overlays: OverlayStart;
/** {@link IUiSettingsClient} */
uiSettings: IUiSettingsClient;
/** {@link FatalErrorsStart} */
fatalErrors: FatalErrorsStart;
/**
* exposed temporarily until https://github.com/elastic/kibana/issues/41990 done
* use *only* to retrieve config values. There is no way to set injected values
* in the new platform.
* @deprecated
* */
injectedMetadata: {
getInjectedVar: (name: string, defaultValue?: any) => unknown;
getBranding: () => Branding;
};
/** {@link WorkspacesStart} */
workspaces: WorkspacesStart;
} classDiagram
class CoreStart {
ApplicationStart application
ChromeStart chrome
DocLinksStart docLinks
HttpStart http
SavedObjectsStart savedObjects
I18nStart i18n
NotificationsStart notifications
OverlayStart overlays
IUiSettingsClient uiSettings
FatalErrorsStart fatalErrors
InjectedMetadata injectedMetadata
WorkspacesStart workspaces
}
class ApplicationStart {
+RecursiveReadonly capabilities
+Observable applications$
+Promise navigateToApp(appId: string, options?: NavigateToAppOptions)
+Promise navigateToUrl(url: string)
+string getUrlForApp(appId: string, options?: unknown)
+void registerMountContext
+Observable currentAppId$
}
CoreStart --> ApplicationStart
navigateToModule static method of AppNavigate: `plugins/main/public/react-services/app-navigate.js`static navigateToModule(e, section, params, navigateMethod = false) {
e.persist(); // needed to access this event asynchronously
if (e.button == 0) {
// left button clicked
if (navigateMethod) {
navigateMethod();
return;
}
}
getIndexPattern().then((indexPattern) => {
const urlParams = {};
if (Object.keys(params).length) {
Object.keys(params).forEach((key) => {
if (key === 'filters') {
urlParams['_w'] = this.buildFilter_w(params[key], indexPattern);
} else {
urlParams[key] = params[key];
}
});
}
const url = Object.entries(urlParams)
.map((e) => e.join('='))
.join('&');
const currentUrl = window.location.href.split('#/')[0];
const newUrl = currentUrl + `#/${section}?` + url;
if (e && (e.which == 2 || e.button == 1)) {
// middlebutton clicked
window.open(newUrl, '_blank', 'noreferrer');
} else if (e.button == 0) {
// left button clicked
if (navigateMethod) {
navigateMethod();
} else {
window.location.href = newUrl;
}
}
});
} Based on the above, the navigateToApp
getUrlForApp
navigateToUrl
NOTE: TODO
|
Update 03/06/2024
TO DO:
|
Update 2024/06/04
|
ProblemWhen using the link group on configuration on Agent > :agent > Configuration causes an error in the view seems ResearchThe bug seems to be related due the AgentView component is unmounted (due to changing of the application), but this is mounted again. It seems the problem is caused because the applications are sharing the same history and when navigating to another Wazuh application, the initial location does not the expected search parameters causing an error on the view. Ensuring each Wazuh application has a different history solves the problem. I am not sure because all the applications are sharing the same history managed by |
Update 04/06/2024
|
Update 05/06/2024
The particular problem with testing NavigationService's goBack, goForward, and go(n) methods is that this is an asynchronous operation, meaning that navigation back/forward/n does not complete immediately. To resolve this, we leverage the listen method to listen for changes in history and ensure that the test waits for the navigation to complete before making expectations.
|
Description
In the last stage of removing the Discover legacy we must refactor the AngularJS routing system into React routing. This will allow us to fully deprecated AngularJS.
This effort involves several tasks and has many breaking changes. So we will work with a feature branch and finally merge it in the 4.9.0 branch once it's done to ensure we don't merge any half-done work and break the app.
Tasks
Replace the routing based on AngularJS to ReactJS
Remove dependencies related to AngularJS
Remove unused code
withRexduxProvider
HOCWzReduxProvider
componentAdapt tests
Nice to have:
/settings?tab=api
to/settings/api
#
from the URL pathDesign
Development considerations:
The application should mount a ReactJS application into the HTML element provided by the parameters received on the
mount
method of the Wazuh dashboard application. The application could be wrapped in the root level with some contexts asI18nProvider
and Redux provider to these are accesibles from any component in the components tree:For routing, we create a history that is managed through the navigation service. This approach lets push new URL changes and access to the current location for services that are not decoupled from the components. The navigation service is the way to manage the navigation instead of using the interface provided by the
react-router-dom
library. It stores a references to the history we create.The
Application
component defines the layout of the application under the router provided byreact-router-dom
. In this step, we port the current routing based on AngularJS to ReactJS.Some views are managed depending on the search parameters (
tab=syscollector
), and the current version ofreact-router-dom
that isv5
is unable to re-render components when the search parameters change. To cover this required navigation based on search paremeter, we use a custom routing components/hooks/HOCs that have a similar interface to the provided by thereact-router-dom
.The text was updated successfully, but these errors were encountered: