Skip to content

Commit

Permalink
feat(ws): Notebooks 2.0 // Frontend // Namespace selector
Browse files Browse the repository at this point in the history
Signed-off-by: yelias <[email protected]>
  • Loading branch information
yelias committed Dec 17, 2024
1 parent 68a0060 commit fee7584
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
const namespaces = ['default', 'kubeflow', 'custom-namespace'];
const mockNamespaces = {
data: [{ name: 'default' }, { name: 'kubeflow' }, { name: 'custom-namespace' }],
};

describe('Namespace Selector Dropdown', () => {
beforeEach(() => {
// Mock the namespaces and selected namespace
cy.intercept('GET', '/api/v1/namespaces', {
body: mockNamespaces,
});
cy.visit('/');
});

it('should open the namespace dropdown and select a namespace', () => {
cy.get('[data-testid="namespace-toggle"]').click();
cy.get('[data-testid="namespace-dropdown"]').should('be.visible');
namespaces.forEach((ns) => {
cy.get(`[data-testid="dropdown-item-${ns}"]`).should('exist').and('contain', ns);
});

cy.get('[data-testid="dropdown-item-kubeflow"]').click();

// Assert the selected namespace is updated
cy.get('[data-testid="namespace-toggle"]').should('contain', 'kubeflow');
});

it('should display the default namespace initially', () => {
cy.get('[data-testid="namespace-toggle"]').should('contain', 'default');
});

it('should navigate to notebook settings and retain the namespace', () => {
cy.get('[data-testid="namespace-toggle"]').click();
cy.get('[data-testid="dropdown-item-custom-namespace"]').click();
cy.get('[data-testid="namespace-toggle"]').should('contain', 'custom-namespace');
// Click on navigation button
cy.get('#Settings').click();
cy.get('[data-testid="nav-link-/notebookSettings"]').click();
cy.get('[data-testid="namespace-toggle"]').should('contain', 'custom-namespace');
});
});
27 changes: 18 additions & 9 deletions workspaces/frontend/src/app/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,20 @@ import {
Title,
} from '@patternfly/react-core';
import { BarsIcon } from '@patternfly/react-icons';
import { NamespaceProvider } from './context/NamespaceContextProvider';
import NamespaceSelector from '../shared/components/NamespaceSelector';

Check warning on line 15 in workspaces/frontend/src/app/App.tsx

View workflow job for this annotation

GitHub Actions / test-and-build

import statements should have an absolute path

Check failure on line 15 in workspaces/frontend/src/app/App.tsx

View workflow job for this annotation

GitHub Actions / test-and-build

`../shared/components/NamespaceSelector` import should occur after import of `./NavSidebar`
import AppRoutes from './AppRoutes';
import NavSidebar from './NavSidebar';

const App: React.FC = () => {
const masthead = (
<Masthead>
<MastheadToggle>
<PageToggleButton id="page-nav-toggle" variant="plain" aria-label="Dashboard navigation">
<PageToggleButton

Check failure on line 23 in workspaces/frontend/src/app/App.tsx

View workflow job for this annotation

GitHub Actions / test-and-build

Replace `⏎··········id="page-nav-toggle"⏎··········variant="plain"⏎··········aria-label="Dashboard·navigation"⏎········` with `·id="page-nav-toggle"·variant="plain"·aria-label="Dashboard·navigation"`
id="page-nav-toggle"
variant="plain"
aria-label="Dashboard navigation"
>
<BarsIcon />
</PageToggleButton>
</MastheadToggle>
Expand All @@ -28,20 +34,23 @@ const App: React.FC = () => {
<Title headingLevel="h2" size="3xl">
Kubeflow Notebooks 2.0
</Title>
<NamespaceSelector />
</Flex>
</MastheadContent>
</Masthead>
);

return (
<Page
mainContainerId="primary-app-container"
masthead={masthead}
isManagedSidebar
sidebar={<NavSidebar />}
>
<AppRoutes />
</Page>
<NamespaceProvider>
<Page
mainContainerId="primary-app-container"
masthead={masthead}
isManagedSidebar
sidebar={<NavSidebar />}
>
<AppRoutes />
</Page>
</NamespaceProvider>
);
};

Expand Down
5 changes: 3 additions & 2 deletions workspaces/frontend/src/app/NavSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ import { useNavData, isNavDataGroup, NavDataHref, NavDataGroup } from './AppRout

const NavHref: React.FC<{ item: NavDataHref }> = ({ item }) => (
<NavItem key={item.label} data-id={item.label} itemId={item.label}>
<NavLink to={item.path}>{item.label}</NavLink>
<NavLink to={item.path} data-testid={`nav-link-${item.path}`}>
{item.label}
</NavLink>
</NavItem>
);

Expand Down Expand Up @@ -40,7 +42,6 @@ const NavGroup: React.FC<{ item: NavDataGroup }> = ({ item }) => {

const NavSidebar: React.FC = () => {
const navData = useNavData();

return (
<PageSidebar>
<PageSidebarBody>
Expand Down
59 changes: 59 additions & 0 deletions workspaces/frontend/src/app/context/NamespaceContextProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import React, {

Check failure on line 1 in workspaces/frontend/src/app/context/NamespaceContextProvider.tsx

View workflow job for this annotation

GitHub Actions / test-and-build

Replace `⏎··useState,⏎··useContext,⏎··ReactNode,⏎··useMemo,⏎··useCallback,⏎` with `·useState,·useContext,·ReactNode,·useMemo,·useCallback·`
useState,
useContext,
ReactNode,
useMemo,
useCallback,
} from 'react';
import useMount from '../hooks/useMount';

Check warning on line 8 in workspaces/frontend/src/app/context/NamespaceContextProvider.tsx

View workflow job for this annotation

GitHub Actions / test-and-build

import statements should have an absolute path

interface NamespaceContextState {
namespaces: string[];
selectedNamespace: string;
setSelectedNamespace: (namespace: string) => void;
}

const NamespaceContext = React.createContext<NamespaceContextState | undefined>(

Check failure on line 16 in workspaces/frontend/src/app/context/NamespaceContextProvider.tsx

View workflow job for this annotation

GitHub Actions / test-and-build

Replace `⏎··undefined⏎` with `undefined`
undefined
);

export const useNamespaceContext = () => {

Check failure on line 20 in workspaces/frontend/src/app/context/NamespaceContextProvider.tsx

View workflow job for this annotation

GitHub Actions / test-and-build

Missing return type on function
const context = useContext(NamespaceContext);
if (!context) {
throw new Error(

Check failure on line 23 in workspaces/frontend/src/app/context/NamespaceContextProvider.tsx

View workflow job for this annotation

GitHub Actions / test-and-build

Replace `⏎······"useNamespaceContext·must·be·used·within·a·NamespaceProvider"⏎····` with `'useNamespaceContext·must·be·used·within·a·NamespaceProvider'`
"useNamespaceContext must be used within a NamespaceProvider"
);
}
return context;
};

interface NamespaceProviderProps {
children: ReactNode;
}

export const NamespaceProvider: React.FC<NamespaceProviderProps> = ({

Check failure on line 34 in workspaces/frontend/src/app/context/NamespaceContextProvider.tsx

View workflow job for this annotation

GitHub Actions / test-and-build

Replace `⏎··children,⏎` with `·children·`
children,
}) => {
const [namespaces, setNamespaces] = useState<string[]>([]);
const [selectedNamespace, setSelectedNamespace] = useState<string>("");

Check failure on line 38 in workspaces/frontend/src/app/context/NamespaceContextProvider.tsx

View workflow job for this annotation

GitHub Actions / test-and-build

Replace `""` with `''`

// Todo: Need to replace with actual API call
const fetchNamespaces = useCallback(async () => {
const mockNamespaces = {
data: [{ name: 'default' }, { name: 'kubeflow' }, { name: 'custom-namespace' }],
};
const namespaceNames = mockNamespaces.data.map((ns) => ns.name);
setNamespaces(namespaceNames);
setSelectedNamespace(namespaceNames.length > 0 ? namespaceNames[0] : "");

Check failure on line 47 in workspaces/frontend/src/app/context/NamespaceContextProvider.tsx

View workflow job for this annotation

GitHub Actions / test-and-build

Replace `""` with `''`
}, []);
useMount(fetchNamespaces);
const namespacesContextValues = useMemo(
() => ({ namespaces, selectedNamespace, setSelectedNamespace }),
[namespaces, selectedNamespace]

Check failure on line 52 in workspaces/frontend/src/app/context/NamespaceContextProvider.tsx

View workflow job for this annotation

GitHub Actions / test-and-build

Insert `,`
);
return (
<NamespaceContext.Provider value={namespacesContextValues}>
{children}
</NamespaceContext.Provider>
);
};
9 changes: 9 additions & 0 deletions workspaces/frontend/src/app/hooks/useMount.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { useEffect } from "react"

const useMount = (callback:()=>void): void => {
useEffect(() => {
callback();
}, []);
}

export default useMount;
57 changes: 57 additions & 0 deletions workspaces/frontend/src/shared/components/NamespaceSelector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import React, { FC, useMemo, useState } from 'react';
import {
Dropdown,
DropdownItem,
MenuToggle,
DropdownList,
DropdownProps,
} from '@patternfly/react-core';
import { useNamespaceContext } from '../../app/context/NamespaceContextProvider';

Check warning on line 9 in workspaces/frontend/src/shared/components/NamespaceSelector.tsx

View workflow job for this annotation

GitHub Actions / test-and-build

import statements should have an absolute path

const NamespaceSelector: FC = () => {
const { namespaces, selectedNamespace, setSelectedNamespace } = useNamespaceContext();
const [isOpen, setIsOpen] = useState<boolean>(false);

const onSelect: DropdownProps['onSelect'] = (_event, value) => {
setSelectedNamespace(value as string);
setIsOpen(false);
};

const dropdownItems = useMemo(
() =>
namespaces.map((ns) => (
<DropdownItem
key={ns}
itemId={ns}
className="namespace-list-items"
data-testid={`dropdown-item-${ns}`}
>
{ns}
</DropdownItem>
)),
[namespaces],
);

return (
<Dropdown
onSelect={onSelect}
toggle={(toggleRef) => (
<MenuToggle
ref={toggleRef}
onClick={() => setIsOpen(!isOpen)}
isExpanded={isOpen}
className="namespace-select-toggle"
data-testid="namespace-toggle"
>
{selectedNamespace}
</MenuToggle>
)}
isOpen={isOpen}
data-testid="namespace-dropdown"
>
<DropdownList>{dropdownItems}</DropdownList>
</Dropdown>
);
};

export default NamespaceSelector;

0 comments on commit fee7584

Please sign in to comment.