Skip to content
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

[Data Explorer] Allow render from View directly, not from Data Explorer #6167

Merged
merged 2 commits into from
Jun 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 1 addition & 23 deletions src/plugins/data_explorer/public/components/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,12 @@
* SPDX-License-Identifier: Apache-2.0
*/

import React, { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
import React from 'react';
import { AppMountParameters } from '../../../../core/public';
import { useView } from '../utils/use';
import { AppContainer } from './app_container';
import { useOpenSearchDashboards } from '../../../opensearch_dashboards_react/public';
import { DataExplorerServices } from '../types';
import { syncQueryStateWithUrl } from '../../../data/public';

export const DataExplorerApp = ({ params }: { params: AppMountParameters }) => {
const { view } = useView();
const {
services: {
data: { query },
osdUrlStateStorage,
},
} = useOpenSearchDashboards<DataExplorerServices>();
const { pathname } = useLocation();

useEffect(() => {
// syncs `_g` portion of url with query services
const { stop } = syncQueryStateWithUrl(query, osdUrlStateStorage);

return () => stop();

// this effect should re-run when pathname is changed to preserve querystring part,
// so the global state is always preserved
}, [query, osdUrlStateStorage, pathname]);

return <AppContainer view={view} params={params} />;
};
93 changes: 51 additions & 42 deletions src/plugins/data_explorer/public/components/app_container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,51 +10,60 @@
import { Sidebar } from './sidebar';
import { NoView } from './no_view';
import { View } from '../services/view_service/view';
import { shallowEqual } from '../utils/use/shallow_equal';
import './app_container.scss';

export const AppContainer = ({ view, params }: { view?: View; params: AppMountParameters }) => {
const isMobile = useIsWithinBreakpoints(['xs', 's', 'm']);
// TODO: Make this more robust.
if (!view) {
return <NoView />;
}
export const AppContainer = React.memo(
({ view, params }: { view?: View; params: AppMountParameters }) => {
const isMobile = useIsWithinBreakpoints(['xs', 's', 'm']);
// TODO: Make this more robust.
if (!view) {
return <NoView />;
}

const { Canvas, Panel, Context } = view;
const { Canvas, Panel, Context } = view;

const MemoizedPanel = memo(Panel);
const MemoizedCanvas = memo(Canvas);
const MemoizedPanel = memo(Panel);
const MemoizedCanvas = memo(Canvas);

// Render the application DOM.
return (
<EuiPage className="deLayout" paddingSize="none">
{/* TODO: improve fallback state */}
<Suspense fallback={<div>Loading...</div>}>
<Context {...params}>
<EuiResizableContainer direction={isMobile ? 'vertical' : 'horizontal'}>
{(EuiResizablePanel, EuiResizableButton) => (
<>
<EuiResizablePanel
initialSize={20}
minSize="260px"
mode={['collapsible', { position: 'top' }]}
paddingSize="none"
>
<Sidebar>
<MemoizedPanel {...params} />
</Sidebar>
</EuiResizablePanel>
<EuiResizableButton />
// Render the application DOM.
return (
<EuiPage className="deLayout" paddingSize="none">
{/* TODO: improve fallback state */}
<Suspense fallback={<div>Loading...</div>}>
<Context {...params}>
<EuiResizableContainer direction={isMobile ? 'vertical' : 'horizontal'}>
{(EuiResizablePanel, EuiResizableButton) => (
<>

Check warning on line 37 in src/plugins/data_explorer/public/components/app_container.tsx

View check run for this annotation

Codecov / codecov/patch

src/plugins/data_explorer/public/components/app_container.tsx#L37

Added line #L37 was not covered by tests
<EuiResizablePanel
initialSize={20}
minSize="260px"
mode={['collapsible', { position: 'top' }]}
paddingSize="none"
>
<Sidebar>
<MemoizedPanel {...params} />
</Sidebar>
</EuiResizablePanel>
<EuiResizableButton />

<EuiResizablePanel initialSize={80} minSize="65%" mode="main" paddingSize="none">
<EuiPageBody className="deLayout__canvas">
<MemoizedCanvas {...params} />
</EuiPageBody>
</EuiResizablePanel>
</>
)}
</EuiResizableContainer>
</Context>
</Suspense>
</EuiPage>
);
};
<EuiResizablePanel initialSize={80} minSize="65%" mode="main" paddingSize="none">
<EuiPageBody className="deLayout__canvas">
<MemoizedCanvas {...params} />
</EuiPageBody>
</EuiResizablePanel>
</>
)}
</EuiResizableContainer>
</Context>
</Suspense>
</EuiPage>
);
},
(prevProps, nextProps) => {
return (

Check warning on line 64 in src/plugins/data_explorer/public/components/app_container.tsx

View check run for this annotation

Codecov / codecov/patch

src/plugins/data_explorer/public/components/app_container.tsx#L64

Added line #L64 was not covered by tests
prevProps.view === nextProps.view &&
shallowEqual(prevProps.params, nextProps.params, ['history'])
);
}
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { shallowEqual } from './shallow_equal';

describe('shallowEqual', () => {
it('should return true for equal objects without ignored keys', () => {
const obj1 = { a: 1, b: 2, c: 3 };
const obj2 = { a: 1, b: 2, c: 3 };
const ignoreKeys = [];

expect(shallowEqual(obj1, obj2, ignoreKeys)).toBe(true);
});

it('should return false for objects with different values', () => {
const obj1 = { a: 1, b: 2, c: 3 };
const obj2 = { a: 1, b: 2, c: 4 };
const ignoreKeys = [];

expect(shallowEqual(obj1, obj2, ignoreKeys)).toBe(false);
});

it('should return false for objects with different keys', () => {
const obj1 = { a: 1, b: 2 };
const obj2 = { a: 1, b: 2, c: 3 };
const ignoreKeys = [];

expect(shallowEqual(obj1, obj2, ignoreKeys)).toBe(false);
});

it('should return true for objects with different values but ignored', () => {
const obj1 = { a: 1, b: 2, c: 3 };
const obj2 = { a: 1, b: 2, c: 4 };
const ignoreKeys = ['c'];

expect(shallowEqual(obj1, obj2, ignoreKeys)).toBe(true);
});

it('should return true for objects with different keys but ignored', () => {
const obj1 = { a: 1, b: 2 };
const obj2 = { a: 1, b: 2, c: 4 };
const ignoreKeys = ['c'];

expect(shallowEqual(obj1, obj2, ignoreKeys)).toBe(true);
});
});
22 changes: 22 additions & 0 deletions src/plugins/data_explorer/public/utils/use/shallow_equal.ts
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: A better home for this is @osd/std.

Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

// A simple shallow equal function that can ignore specified keys
export function shallowEqual(object1: any, object2: any, ignoreKeys: any) {
const keys1 = Object.keys(object1).filter((key) => !ignoreKeys.includes(key));
const keys2 = Object.keys(object2).filter((key) => !ignoreKeys.includes(key));

if (keys1.length !== keys2.length) {
return false;
}

for (const key of keys1) {
if (object1[key] !== object2[key]) {
return false;
}
}

return true;
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { debounceTime } from 'rxjs/operators';
import { i18n } from '@osd/i18n';
import { useEffect } from 'react';
import { cloneDeep } from 'lodash';
import { useLocation } from 'react-router-dom';
import { RequestAdapter } from '../../../../../inspector/public';
import { DiscoverViewServices } from '../../../build_services';
import { search } from '../../../../../data/public';
Expand All @@ -31,6 +32,7 @@ import {
getResponseInspectorStats,
} from '../../../opensearch_dashboards_services';
import { SEARCH_ON_PAGE_LOAD_SETTING } from '../../../../common';
import { syncQueryStateWithUrl } from '../../../../../data/public';

export enum ResultStatus {
UNINITIALIZED = 'uninitialized',
Expand Down Expand Up @@ -68,11 +70,19 @@ export type RefetchSubject = Subject<SearchRefetch>;
* }, [data$]);
*/
export const useSearch = (services: DiscoverViewServices) => {
const { pathname } = useLocation();
const initalSearchComplete = useRef(false);
const [savedSearch, setSavedSearch] = useState<SavedSearch | undefined>(undefined);
const { savedSearch: savedSearchId, sort, interval } = useSelector((state) => state.discover);
const { data, filterManager, getSavedSearchById, core, toastNotifications, chrome } = services;
const indexPattern = useIndexPattern(services);
const {
data,
filterManager,
getSavedSearchById,
core,
toastNotifications,
osdUrlStateStorage,
} = services;
const timefilter = data.query.timefilter.timefilter;
const fetchStateRef = useRef<{
abortController: AbortController | undefined;
Expand Down Expand Up @@ -309,6 +319,16 @@ export const useSearch = (services: DiscoverViewServices) => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [getSavedSearchById, savedSearchId]);

useEffect(() => {
// syncs `_g` portion of url with query services
const { stop } = syncQueryStateWithUrl(data.query, osdUrlStateStorage);

return () => stop();

// this effect should re-run when pathname is changed to preserve querystring part,
// so the global state is always preserved
}, [data.query, osdUrlStateStorage, pathname]);

return {
data$,
refetch$,
Expand Down
Loading