Skip to content

Commit

Permalink
Add accelerator table
Browse files Browse the repository at this point in the history
  • Loading branch information
dpanshug committed Nov 6, 2023
1 parent d15f8aa commit 976d5a5
Show file tree
Hide file tree
Showing 10 changed files with 203 additions and 17 deletions.
22 changes: 16 additions & 6 deletions frontend/src/__mocks__/mockAcceleratorProfile.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,29 @@
import { AcceleratorKind } from '~/k8sTypes';

export const mockAcceleratorProfile = (): AcceleratorKind => ({
type MockAcceleratorProfileType = {
name?: string;
enabled?: boolean;
identifier?: string;
};

export const mockAcceleratorProfile = ({
name = 'Test Accelerator',
enabled = true,
identifier = 'nvidia.com/gpu',
}: MockAcceleratorProfileType): AcceleratorKind => ({
apiVersion: 'dashboard.opendatahub.io/v1',
kind: 'AcceleratorProfile',
metadata: {
name: 'test-accelerator',
name: name,
},
spec: {
displayName: 'test-accelerator',
enabled: true,
identifier: 'nvidia.com/gpu',
displayName: name,
enabled: enabled,
identifier: identifier,
description: 'Test description',
tolerations: [
{
key: 'nvidia.com/gpu',
key: identifier,
operator: 'Exists',
effect: 'NoSchedule',
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,16 @@ test('Empty State no Accelerator profile', async ({ page }) => {
// Test that the button is enabled
await expect(page.getByRole('button', { name: 'Add new accelerator profile' })).toBeEnabled();
});

test('List Accelerator profiles', async ({ page }) => {
await page.goto(
navigateToStory('pages-acceleratorprofiles-acceleratorprofiles', 'list-accelerator-profiles'),
);

// wait for page to load
await page.waitForSelector('text=Accelerator profiles');

expect(page.getByText('TensorRT')).toBeTruthy();
expect(page.getByText('Test Accelerator')).toBeTruthy();
expect(page.getByText('nvidia.com/gpu ')).toBeTruthy();
});
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import React from 'react';
import { StoryFn, Meta, StoryObj } from '@storybook/react';
import { within } from '@storybook/testing-library';
import { rest } from 'msw';
import { mockAcceleratorProfile } from '~/__mocks__/mockAcceleratorProfile';
import { mockProjectK8sResource } from '~/__mocks__/mockProjectK8sResource';
import { mockK8sResourceList } from '~/__mocks__/mockK8sResourceList';
import useDetectUser from '~/utilities/useDetectUser';
import { mockStatus } from '~/__mocks__/mockStatus';

import AcceleratorProfiles from '~/pages/acceleratorProfiles/AcceleratorProfiles';
import AcceleratorProfiles from '~/pages/acceleratorProfiles/screens/list/AcceleratorProfiles';

export default {
component: AcceleratorProfiles,
Expand All @@ -22,7 +23,18 @@ export default {
],
accelerators: rest.get(
'/api/k8s/apis/dashboard.opendatahub.io/v1/namespaces/opendatahub/acceleratorprofiles',
(req, res, ctx) => res(ctx.json(mockK8sResourceList([mockAcceleratorProfile()]))),
(req, res, ctx) =>
res(
ctx.json(
mockK8sResourceList([
mockAcceleratorProfile({}),
mockAcceleratorProfile({
name: 'TensorRT',
enabled: false,
}),
]),
),
),
),
},
},
Expand All @@ -48,3 +60,14 @@ export const EmptyStateNoAcceleratorProfile: StoryObj = {
},
},
};

export const ListAcceleratorProfiles: StoryObj = {
render: Template,

play: async ({ canvasElement }) => {
// load page and wait until settled
const canvas = within(canvasElement);
await canvas.findByText('Test accelerator', undefined, { timeout: 5000 });
await canvas.findByText('TensorRT', undefined, { timeout: 5000 });
},
};
2 changes: 1 addition & 1 deletion frontend/src/app/AppRoutes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const DependencyMissingPage = React.lazy(
);

const AcceleratorProfiles = React.lazy(
() => import('../pages/acceleratorProfiles/AcceleratorProfiles'),
() => import('../pages/acceleratorProfiles/screens/list/AcceleratorProfiles'),
);

const AppRoutes: React.FC = () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import * as React from 'react';
import { Switch } from '@patternfly/react-core';

type AcceleratorProfileEnableToggleProps = {
enabled: boolean;
name: string;
};

const AcceleratorProfileEnableToggle: React.FC<AcceleratorProfileEnableToggleProps> = ({
enabled,
name,
}) => {
const label = enabled ? 'enabled' : 'stopped';

return (
<Switch
aria-label={label}
id={`${name}-enable-switch`}
isChecked={enabled}
onClick={() => undefined}
/>
);
};

export default AcceleratorProfileEnableToggle;
Empty file.
41 changes: 41 additions & 0 deletions frontend/src/pages/acceleratorProfiles/components/TableData.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { SortableData } from '~/components/table';
import { AcceleratorKind } from '~/k8sTypes';

export const columns: SortableData<AcceleratorKind>[] = [
{
field: 'name',
label: 'Name',
sortable: (a, b) => a.spec.displayName.localeCompare(b.spec.displayName),
width: 30,
},
{
field: 'identifier',
label: 'Identifier',
sortable: (a, b) => a.spec.identifier.localeCompare(b.spec.identifier),
info: {
tooltip: 'The resource identifier of the accelerator device.',
},
},
{
field: 'enablement',
label: 'Enable',
sortable: (a, b) => Number(a.spec.enabled ?? false) - Number(b.spec.enabled ?? false),
info: {
tooltip: 'Indicates whether the accelerator profile is available for new resources.',
},
},
{
field: 'last_modified',
label: 'Last modified',
sortable: (a, b): number => {
const first = a.metadata.annotations?.['opendatahub.io/modified-date'];
const second = b.metadata.annotations?.['opendatahub.io/modified-date'];
return new Date(first ?? 0).getTime() - new Date(second ?? 0).getTime();
},
},
{
field: 'kebab',
label: '',
sortable: false,
},
];
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,18 @@ import * as React from 'react';
import {
Button,
ButtonVariant,
Flex,
FlexItem,
EmptyState,
EmptyStateIcon,
EmptyStateVariant,
EmptyStateBody,
PageSection,
PageSectionVariants,
Title,
} from '@patternfly/react-core';
import { PlusCircleIcon } from '@patternfly/react-icons';
import ApplicationsPage from '~/pages/ApplicationsPage';
import useAccelerators from '~/pages/notebookController/screens/server/useAccelerators';
import { useDashboardNamespace } from '~/redux/selectors';
import AcceleratorProfilesTable from '~/pages/acceleratorProfiles/screens/list/AcceleratorProfilesTable';

const description = `Manage accelerator profile settings for users in your organization`;

Expand Down Expand Up @@ -57,12 +55,9 @@ const AcceleratorProfiles: React.FC = () => {
loadError={loadError}
errorMessage="Unable to load accelerator profiles."
emptyStatePage={noAcceleratorProfilePageSection}
provideChildrenPadding
>
<PageSection variant={PageSectionVariants.light} padding={{ default: 'noPadding' }}>
<Flex direction={{ default: 'column' }}>
<FlexItem>{/* Todo: Create accelerator table */}</FlexItem>
</Flex>
</PageSection>
<AcceleratorProfilesTable accelerators={accelerators} />
</ApplicationsPage>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import * as React from 'react';
import { Table } from '~/components/table';
import { columns } from '~/pages/acceleratorProfiles/components/TableData';
import AcceleratorProfilesTableRow from '~/pages/acceleratorProfiles/screens/list/AcceleratorProfilesTableRow';
import { AcceleratorKind } from '~/k8sTypes';

type AcceleratorProfilesTableProps = {
accelerators: AcceleratorKind[];
};

const AcceleratorProfilesTable: React.FC<AcceleratorProfilesTableProps> = ({ accelerators }) => (
<Table
enablePagination
data={accelerators}
columns={columns}
emptyTableView={'No projects match your filters.'}
rowRenderer={(accelerator) => (
<AcceleratorProfilesTableRow key={accelerator.metadata.uid} accelerator={accelerator} />
)}
/>
);

export default AcceleratorProfilesTable;
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import * as React from 'react';
import { Text, TextContent, TextVariants, Timestamp, Truncate } from '@patternfly/react-core';
import { ActionsColumn, Td, Tr } from '@patternfly/react-table';
import { AcceleratorKind } from '~/k8sTypes';
import AcceleratorProfileEnableToggle from '~/pages/acceleratorProfiles/components/AcceleratorProfileEnableToggle';

type AcceleratorProfilesTableRow = {
accelerator: AcceleratorKind;
};

const AcceleratorProfilesTableRow: React.FC<AcceleratorProfilesTableRow> = ({ accelerator }) => (
<Tr>
<Td dataLabel="Name">
<TextContent>
<Text>{accelerator.spec.displayName}</Text>
{accelerator.spec.description && (
<Text component={TextVariants.small}>
<Truncate content={accelerator.spec.description} />
</Text>
)}
</TextContent>
</Td>
<Td dataLabel="Identifier">{accelerator.spec.identifier}</Td>
<Td dataLabel="Enable">
<AcceleratorProfileEnableToggle
enabled={accelerator.spec.enabled}
name={accelerator.spec.displayName}
/>
</Td>
<Td dataLabel="Last modified">
{accelerator.metadata.annotations?.['opendatahub.io/modified-date'] ? (
<Timestamp
date={new Date(accelerator.metadata.annotations['opendatahub.io/modified-date'])}
/>
) : (
'--'
)}
</Td>
<Td isActionCell>
<ActionsColumn
items={[
{
title: 'Edit',
onClick: () => undefined,
},
{
title: 'Delete',
onClick: () => undefined,
},
]}
/>
</Td>
</Tr>
);

export default AcceleratorProfilesTableRow;

0 comments on commit 976d5a5

Please sign in to comment.