Skip to content

Commit

Permalink
add recent items
Browse files Browse the repository at this point in the history
  • Loading branch information
tsullivan committed May 1, 2023
1 parent dd1463a commit 1e0aaf8
Show file tree
Hide file tree
Showing 9 changed files with 72 additions and 20 deletions.
7 changes: 5 additions & 2 deletions packages/shared-ux/chrome/navigation/mocks/src/jest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,21 @@
* Side Public License, v 1.
*/

import { BehaviorSubject } from 'rxjs';
import { NavigationServices, SolutionProperties } from '../../types';

export const getServicesMock = (): NavigationServices => {
const navigateToUrl = jest.fn().mockResolvedValue(undefined);
const basePath = { prepend: jest.fn((path: string) => `/base${path}`) };
const loadingCount = 0;
const loadingCount$ = new BehaviorSubject(0);
const recentlyAccessed$ = new BehaviorSubject([]);

return {
basePath,
loadingCount,
navIsOpen: true,
navigateToUrl,
loadingCount$,
recentlyAccessed$,
};
};

Expand Down
7 changes: 6 additions & 1 deletion packages/shared-ux/chrome/navigation/mocks/src/storybook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@ import { NavigationProps, NavigationServices } from '../../types';
type Arguments = NavigationProps & NavigationServices;
export type Params = Pick<
Arguments,
'activeNavItemId' | 'loadingCount' | 'navIsOpen' | 'platformConfig' | 'solutions'
| 'activeNavItemId'
| 'loadingCount$'
| 'recentlyAccessed$'
| 'navIsOpen'
| 'platformConfig'
| 'solutions'
>;

export class StorybookMock extends AbstractStorybookMock<NavigationProps, NavigationServices> {
Expand Down
1 change: 0 additions & 1 deletion packages/shared-ux/chrome/navigation/src/model/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ export interface NavigationModelDeps {
* @public
*/
export enum Platform {
Recents = 'recents',
Analytics = 'analytics',
MachineLearning = 'ml',
DevTools = 'devTools',
Expand Down
8 changes: 3 additions & 5 deletions packages/shared-ux/chrome/navigation/src/services.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
*/

import React, { FC, useContext } from 'react';
import useObservable from 'react-use/lib/useObservable';
import { NavigationKibanaDependencies, NavigationServices } from '../types';

const Context = React.createContext<NavigationServices | null>(null);
Expand All @@ -27,17 +26,16 @@ export const NavigationKibanaProvider: FC<NavigationKibanaDependencies> = ({
...dependencies
}) => {
const { core } = dependencies;
const { http } = core;
const { chrome, http } = core;
const { basePath } = http;
const { navigateToUrl } = core.application;

const loadingCount = useObservable(http.getLoadingCount$(), 0);

const value: NavigationServices = {
basePath,
loadingCount,
navigateToUrl,
navIsOpen: true,
loadingCount$: http.getLoadingCount$(),
recentlyAccessed$: chrome.recentlyAccessed.get$(),
};

return (
Expand Down
6 changes: 6 additions & 0 deletions packages/shared-ux/chrome/navigation/src/ui/i18n_strings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,10 @@ export const getI18nStrings = () => ({
defaultMessage: 'My deployments',
}
),
recentlyAccessed: i18n.translate(
'sharedUXPackages.chrome.sideNavigation.recentlyAccessed.title',
{
defaultMessage: 'Recent',
}
),
});
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ import {
EuiPopover,
EuiThemeProvider,
} from '@elastic/eui';
import { css } from '@emotion/react';
import { ComponentMeta, ComponentStory } from '@storybook/react';
import React, { useCallback, useState } from 'react';
import { css } from '@emotion/react';
import { BehaviorSubject } from 'rxjs';
import { getSolutionPropertiesMock, NavigationStorybookMock } from '../../mocks';
import mdx from '../../README.mdx';
import { NavigationProps, NavigationServices } from '../../types';
Expand Down Expand Up @@ -132,7 +133,7 @@ ReducedPlatformLinks.argTypes = storybookMock.getArgumentTypes();
export const WithRequestsLoading: ComponentStory<typeof Template> = Template.bind({});
WithRequestsLoading.args = {
activeNavItemId: 'example_project.root.get_started',
loadingCount: 1,
loadingCount$: new BehaviorSubject(1),
solutions: [getSolutionPropertiesMock()],
};
WithRequestsLoading.argTypes = storybookMock.getArgumentTypes();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import { render } from '@testing-library/react';
import React from 'react';
import { BehaviorSubject } from 'rxjs';
import { getServicesMock } from '../../mocks/src/jest';
import { PlatformConfigSet, SolutionProperties } from '../../types';
import { Platform } from '../model';
Expand Down Expand Up @@ -114,7 +115,7 @@ describe('<Navigation />', () => {
});

test('shows loading state', async () => {
services.loadingCount = 5;
services.loadingCount$ = new BehaviorSubject(5);

const { findByTestId } = render(
<NavigationProvider {...services} navIsOpen={true}>
Expand Down
52 changes: 45 additions & 7 deletions packages/shared-ux/chrome/navigation/src/ui/navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@
* Side Public License, v 1.
*/

import useObservable from 'react-use/lib/useObservable';
import {
EuiCollapsibleNavGroup,
EuiFlexGroup,
EuiFlexItem,
EuiHeaderLogo,
EuiLink,
EuiLoadingSpinner,
EuiSideNav,
EuiSideNavItemType,
EuiSpacer,
useEuiTheme,
} from '@elastic/eui';
Expand All @@ -21,28 +24,35 @@ import { getI18nStrings } from './i18n_strings';
import { NavigationBucketProps, NavigationProps } from '../../types';
import { NavigationModel } from '../model';
import { useNavigation } from '../services';
import { navigationStyles as styles } from '../styles';
import { ElasticMark } from './elastic_mark';
import './header_logo.scss';
import { NavigationBucket } from './navigation_bucket';

export const Navigation = (props: NavigationProps) => {
const { loadingCount, activeNavItemId, ...services } = useNavigation();
const { activeNavItemId, basePath, navIsOpen, navigateToUrl, ...observables } = useNavigation();
const { euiTheme } = useEuiTheme();

const activeNav = activeNavItemId ?? props.activeNavItemId;

const nav = new NavigationModel(services, props.platformConfig, props.solutions, activeNav);
const nav = new NavigationModel(
{ basePath, navigateToUrl },
props.platformConfig,
props.solutions,
activeNav
);

const solutions = nav.getSolutions();
const { analytics, ml, devTools, management } = nav.getPlatform();

const strings = getI18nStrings();

const NavHeader = () => {
const homeUrl = services.basePath.prepend(props.homeHref);
const loadingCount = useObservable(observables.loadingCount$, 0);
const homeUrl = basePath.prepend(props.homeHref);
const navigateHome = (event: React.MouseEvent) => {
event.preventDefault();
services.navigateToUrl(homeUrl);
navigateToUrl(homeUrl);
};
const logo =
loadingCount === 0 ? (
Expand All @@ -66,9 +76,7 @@ export const Navigation = (props: NavigationProps) => {
return (
<>
{logo}
{services.navIsOpen ? (
<ElasticMark className="chrHeaderLogo__mark" aria-hidden={true} />
) : null}
{navIsOpen ? <ElasticMark className="chrHeaderLogo__mark" aria-hidden={true} /> : null}
</>
);
};
Expand Down Expand Up @@ -100,6 +108,34 @@ export const Navigation = (props: NavigationProps) => {
}
};

const RecentlyAccessed = () => {
const recentlyAccessed = useObservable(observables.recentlyAccessed$, []);

const navItems: Array<EuiSideNavItemType<unknown>> = [
{
name: '',
id: 'recents_root',
items: recentlyAccessed.map((item) => ({
...item,
name: item.label,
href: item.link,
})),
},
];

return (
<EuiCollapsibleNavGroup
title={strings.recentlyAccessed}
iconType="clock"
isCollapsible={true}
initialIsOpen={recentlyAccessed.length > 0}
data-test-subj={`nav-bucket-recentlyAccessed`}
>
<EuiSideNav items={navItems} css={styles.euiSideNavItems} />
</EuiCollapsibleNavGroup>
);
};

// higher-order-component to keep the common props DRY
const NavigationBucketHoc = (outerProps: Omit<NavigationBucketProps, 'activeNavItemId'>) => (
<NavigationBucket {...outerProps} activeNavItemId={activeNav} />
Expand All @@ -114,6 +150,8 @@ export const Navigation = (props: NavigationProps) => {

<LinkToCloud />

<RecentlyAccessed />

{solutions.map((solutionBucket, idx) => {
return <NavigationBucketHoc {...solutionBucket} key={`solution${idx}`} />;
})}
Expand Down
3 changes: 2 additions & 1 deletion packages/shared-ux/chrome/navigation/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ import { BasePathService, NavigateToUrlFn, RecentItem } from './internal';
export interface NavigationServices {
activeNavItemId?: string;
basePath: BasePathService;
loadingCount: number;
loadingCount$: Observable<number>;
recentlyAccessed$: Observable<RecentItem[]>;
navIsOpen: boolean;
navigateToUrl: NavigateToUrlFn;
}
Expand Down

0 comments on commit 1e0aaf8

Please sign in to comment.