From 9fcb9a289a504c9569a63cf2a60af8fbb7ca9ddc Mon Sep 17 00:00:00 2001 From: Charles Thao Date: Tue, 6 May 2025 10:42:24 -0400 Subject: [PATCH 1/2] feat(ws): Introduce drawer to workspace creation wizard Signed-off-by: Charles Thao --- .../Creation/WorkspaceCreationDrawer.tsx | 55 +++++++++++++++++++ .../image/WorkspaceCreationImageDetails.tsx | 7 +-- .../image/WorkspaceCreationImageSelection.tsx | 42 +++++++++++--- .../kind/WorkspaceCreationKindDetails.tsx | 7 +-- .../kind/WorkspaceCreationKindSelection.tsx | 47 +++++++++++----- .../WorkspaceCreationPodConfigDetails.tsx | 10 +--- .../WorkspaceCreationPodConfigSelection.tsx | 39 ++++++++++--- 7 files changed, 161 insertions(+), 46 deletions(-) create mode 100644 workspaces/frontend/src/app/pages/Workspaces/Creation/WorkspaceCreationDrawer.tsx diff --git a/workspaces/frontend/src/app/pages/Workspaces/Creation/WorkspaceCreationDrawer.tsx b/workspaces/frontend/src/app/pages/Workspaces/Creation/WorkspaceCreationDrawer.tsx new file mode 100644 index 00000000..e9be4c49 --- /dev/null +++ b/workspaces/frontend/src/app/pages/Workspaces/Creation/WorkspaceCreationDrawer.tsx @@ -0,0 +1,55 @@ +import React, { Ref } from 'react'; +import { + Drawer, + DrawerPanelContent, + DrawerContent, + DrawerContentBody, + DrawerHead, + DrawerActions, + DrawerCloseButton, + Title, +} from '@patternfly/react-core'; + +interface WorkspaceCreationDrawerProps { + children: React.ReactNode; + title: string; + info: React.ReactNode; + isExpanded: boolean; + drawerRef?: Ref; + onCloseClick: () => void; + onExpand: () => void; +} + +export const WorkspaceCreationDrawer: React.FC = ({ + children, + isExpanded, + drawerRef, + title, + info, + onCloseClick, + onExpand, +}) => { + const panelContent = ( + + + }> + {title} + + + + + + {info} + + ); + + return ( + <> + + + {children} + + + + ); +}; diff --git a/workspaces/frontend/src/app/pages/Workspaces/Creation/image/WorkspaceCreationImageDetails.tsx b/workspaces/frontend/src/app/pages/Workspaces/Creation/image/WorkspaceCreationImageDetails.tsx index df59bedc..4e00912a 100644 --- a/workspaces/frontend/src/app/pages/Workspaces/Creation/image/WorkspaceCreationImageDetails.tsx +++ b/workspaces/frontend/src/app/pages/Workspaces/Creation/image/WorkspaceCreationImageDetails.tsx @@ -9,12 +9,9 @@ type WorkspaceCreationImageDetailsProps = { export const WorkspaceCreationImageDetails: React.FunctionComponent< WorkspaceCreationImageDetailsProps > = ({ workspaceImage }) => ( - <> - {!workspaceImage &&

Select an image to view its details here.

} - +
{workspaceImage && ( <> - Image {workspaceImage.displayName}
@@ -26,5 +23,5 @@ export const WorkspaceCreationImageDetails: React.FunctionComponent< )} - +
); diff --git a/workspaces/frontend/src/app/pages/Workspaces/Creation/image/WorkspaceCreationImageSelection.tsx b/workspaces/frontend/src/app/pages/Workspaces/Creation/image/WorkspaceCreationImageSelection.tsx index cdfb1e95..2cb6d04f 100644 --- a/workspaces/frontend/src/app/pages/Workspaces/Creation/image/WorkspaceCreationImageSelection.tsx +++ b/workspaces/frontend/src/app/pages/Workspaces/Creation/image/WorkspaceCreationImageSelection.tsx @@ -1,10 +1,10 @@ -import * as React from 'react'; +import React, { useMemo, useState } from 'react'; import { Content, Divider, Split, SplitItem } from '@patternfly/react-core'; -import { useMemo, useState } from 'react'; import { WorkspaceImage } from '~/shared/types'; import { WorkspaceCreationImageDetails } from '~/app/pages/Workspaces/Creation/image/WorkspaceCreationImageDetails'; import { WorkspaceCreationImageList } from '~/app/pages/Workspaces/Creation/image/WorkspaceCreationImageList'; import { FilterByLabels } from '~/app/pages/Workspaces/Creation/labelFilter/FilterByLabels'; +import { WorkspaceCreationDrawer } from '~/app/pages/Workspaces/Creation/WorkspaceCreationDrawer'; interface WorkspaceCreationImageSelectionProps { images: WorkspaceImage[]; @@ -16,6 +16,23 @@ const WorkspaceCreationImageSelection: React.FunctionComponent< WorkspaceCreationImageSelectionProps > = ({ images, selectedImage, onSelect }) => { const [selectedLabels, setSelectedLabels] = useState>>(new Map()); + const [isExpanded, setIsExpanded] = useState(false); + const drawerRef = React.useRef(undefined); + + const onExpand = () => { + if (drawerRef.current) { + drawerRef.current.focus(); + } + }; + + const onClick = (image?: WorkspaceImage) => { + setIsExpanded(true); + onSelect(image); + }; + + const onCloseClick = () => { + setIsExpanded(false); + }; const imageFilterContent = useMemo( () => ( @@ -40,14 +57,21 @@ const WorkspaceCreationImageSelection: React.FunctionComponent< {imageFilterContent} - + + + - {imageDetailsContent} ); diff --git a/workspaces/frontend/src/app/pages/Workspaces/Creation/kind/WorkspaceCreationKindDetails.tsx b/workspaces/frontend/src/app/pages/Workspaces/Creation/kind/WorkspaceCreationKindDetails.tsx index 1fc6088c..7ec63f05 100644 --- a/workspaces/frontend/src/app/pages/Workspaces/Creation/kind/WorkspaceCreationKindDetails.tsx +++ b/workspaces/frontend/src/app/pages/Workspaces/Creation/kind/WorkspaceCreationKindDetails.tsx @@ -9,15 +9,12 @@ type WorkspaceCreationKindDetailsProps = { export const WorkspaceCreationKindDetails: React.FunctionComponent< WorkspaceCreationKindDetailsProps > = ({ workspaceKind }) => ( - <> - {!workspaceKind &&

Select a workspace kind to view its details here.

} - +
{workspaceKind && ( <> - Workspace kind {workspaceKind.name}

{workspaceKind.description}

)} - +
); diff --git a/workspaces/frontend/src/app/pages/Workspaces/Creation/kind/WorkspaceCreationKindSelection.tsx b/workspaces/frontend/src/app/pages/Workspaces/Creation/kind/WorkspaceCreationKindSelection.tsx index ea9e376c..6a7ba1e1 100644 --- a/workspaces/frontend/src/app/pages/Workspaces/Creation/kind/WorkspaceCreationKindSelection.tsx +++ b/workspaces/frontend/src/app/pages/Workspaces/Creation/kind/WorkspaceCreationKindSelection.tsx @@ -1,9 +1,9 @@ -import * as React from 'react'; -import { Content, Divider, Split, SplitItem } from '@patternfly/react-core'; -import { useMemo } from 'react'; +import React, { useState, useRef, useMemo } from 'react'; +import { Content, Divider } from '@patternfly/react-core'; import { WorkspaceKind } from '~/shared/types'; import { WorkspaceCreationKindDetails } from '~/app/pages/Workspaces/Creation/kind/WorkspaceCreationKindDetails'; import { WorkspaceCreationKindList } from '~/app/pages/Workspaces/Creation/kind/WorkspaceCreationKindList'; +import { WorkspaceCreationDrawer } from '~/app/pages/Workspaces/Creation/WorkspaceCreationDrawer'; interface WorkspaceCreationKindSelectionProps { selectedKind: WorkspaceKind | undefined; @@ -13,6 +13,24 @@ interface WorkspaceCreationKindSelectionProps { const WorkspaceCreationKindSelection: React.FunctionComponent< WorkspaceCreationKindSelectionProps > = ({ selectedKind, onSelect }) => { + const [isExpanded, setIsExpanded] = useState(false); + const drawerRef = useRef(undefined); + + const onExpand = () => { + if (drawerRef.current) { + drawerRef.current.focus(); + } + }; + + const onClick = (kind?: WorkspaceKind) => { + setIsExpanded(true); + return onSelect(kind); + }; + + const onCloseClick = () => { + setIsExpanded(false); + }; + /* Replace mocks below for BFF call */ const mockedWorkspaceKind: WorkspaceKind = useMemo( () => ({ @@ -152,16 +170,19 @@ const WorkspaceCreationKindSelection: React.FunctionComponent<

Select a workspace kind to use for the workspace.

- - - - - {kindDetailsContent} - + + +
); }; diff --git a/workspaces/frontend/src/app/pages/Workspaces/Creation/podConfig/WorkspaceCreationPodConfigDetails.tsx b/workspaces/frontend/src/app/pages/Workspaces/Creation/podConfig/WorkspaceCreationPodConfigDetails.tsx index 0d45ca92..962f402b 100644 --- a/workspaces/frontend/src/app/pages/Workspaces/Creation/podConfig/WorkspaceCreationPodConfigDetails.tsx +++ b/workspaces/frontend/src/app/pages/Workspaces/Creation/podConfig/WorkspaceCreationPodConfigDetails.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { List, ListItem, Title } from '@patternfly/react-core'; +import { List, ListItem } from '@patternfly/react-core'; import { WorkspacePodConfig } from '~/shared/types'; type WorkspaceCreationPodConfigDetailsProps = { @@ -10,12 +10,8 @@ export const WorkspaceCreationPodConfigDetails: React.FunctionComponent< WorkspaceCreationPodConfigDetailsProps > = ({ workspacePodConfig }) => ( <> - {!workspacePodConfig &&

Select a pod config to view its details here.

} - {workspacePodConfig && ( - <> - Pod config - {workspacePodConfig.displayName} +

{workspacePodConfig.description}

{Object.keys(workspacePodConfig.labels).map((labelKey) => ( @@ -24,7 +20,7 @@ export const WorkspaceCreationPodConfigDetails: React.FunctionComponent< ))} - +
)} ); diff --git a/workspaces/frontend/src/app/pages/Workspaces/Creation/podConfig/WorkspaceCreationPodConfigSelection.tsx b/workspaces/frontend/src/app/pages/Workspaces/Creation/podConfig/WorkspaceCreationPodConfigSelection.tsx index 6ac201a5..b4e3b683 100644 --- a/workspaces/frontend/src/app/pages/Workspaces/Creation/podConfig/WorkspaceCreationPodConfigSelection.tsx +++ b/workspaces/frontend/src/app/pages/Workspaces/Creation/podConfig/WorkspaceCreationPodConfigSelection.tsx @@ -5,6 +5,7 @@ import { WorkspacePodConfig } from '~/shared/types'; import { WorkspaceCreationPodConfigDetails } from '~/app/pages/Workspaces/Creation/podConfig/WorkspaceCreationPodConfigDetails'; import { WorkspaceCreationPodConfigList } from '~/app/pages/Workspaces/Creation/podConfig/WorkspaceCreationPodConfigList'; import { FilterByLabels } from '~/app/pages/Workspaces/Creation/labelFilter/FilterByLabels'; +import { WorkspaceCreationDrawer } from '~/app/pages/Workspaces/Creation/WorkspaceCreationDrawer'; interface WorkspaceCreationPodConfigSelectionProps { podConfigs: WorkspacePodConfig[]; @@ -16,6 +17,23 @@ const WorkspaceCreationPodConfigSelection: React.FunctionComponent< WorkspaceCreationPodConfigSelectionProps > = ({ podConfigs, selectedPodConfig, onSelect }) => { const [selectedLabels, setSelectedLabels] = useState>>(new Map()); + const [isExpanded, setIsExpanded] = useState(false); + const drawerRef = React.useRef(undefined); + + const onExpand = () => { + if (drawerRef.current) { + drawerRef.current.focus(); + } + }; + + const onClick = (podConfig?: WorkspacePodConfig) => { + setIsExpanded(true); + onSelect(podConfig); + }; + + const onCloseClick = () => { + setIsExpanded(false); + }; const podConfigFilterContent = useMemo( () => ( @@ -40,14 +58,21 @@ const WorkspaceCreationPodConfigSelection: React.FunctionComponent< {podConfigFilterContent} - + + + - {podConfigDetailsContent} ); From c79bcde66396a1c2916bb65e907d5b5a4e5c4830 Mon Sep 17 00:00:00 2001 From: Charles Thao Date: Tue, 6 May 2025 15:43:40 -0400 Subject: [PATCH 2/2] fix(ws): Change label titles in Workspace Creation Signed-off-by: Charles Thao --- .../Creation/kind/WorkspaceCreationKindSelection.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/workspaces/frontend/src/app/pages/Workspaces/Creation/kind/WorkspaceCreationKindSelection.tsx b/workspaces/frontend/src/app/pages/Workspaces/Creation/kind/WorkspaceCreationKindSelection.tsx index 6a7ba1e1..e91b0430 100644 --- a/workspaces/frontend/src/app/pages/Workspaces/Creation/kind/WorkspaceCreationKindSelection.tsx +++ b/workspaces/frontend/src/app/pages/Workspaces/Creation/kind/WorkspaceCreationKindSelection.tsx @@ -59,7 +59,7 @@ const WorkspaceCreationKindSelection: React.FunctionComponent< { id: 'jupyterlab_scipy_180', displayName: 'jupyter-scipy:v1.8.0', - labels: { pythonVersion: '3.11', jupyterlabVersion: '1.8.0' }, + labels: { 'Python Version': '3.11', 'JupyterLab Version': '1.8.0' }, hidden: true, redirect: { to: 'jupyterlab_scipy_190', @@ -72,7 +72,7 @@ const WorkspaceCreationKindSelection: React.FunctionComponent< { id: 'jupyterlab_scipy_190', displayName: 'jupyter-scipy:v1.9.0', - labels: { pythonVersion: '3.12', jupyterlabVersion: '1.9.0' }, + labels: { 'Python Version': '3.12', 'JupyterLab Version': '1.9.0' }, hidden: true, redirect: { to: 'jupyterlab_scipy_200', @@ -85,7 +85,7 @@ const WorkspaceCreationKindSelection: React.FunctionComponent< { id: 'jupyterlab_scipy_200', displayName: 'jupyter-scipy:v2.0.0', - labels: { pythonVersion: '3.12', jupyterlabVersion: '2.0.0' }, + labels: { 'Python Version': '3.12', 'JupyterLab Version': '2.0.0' }, hidden: true, redirect: { to: 'jupyterlab_scipy_210', @@ -98,7 +98,7 @@ const WorkspaceCreationKindSelection: React.FunctionComponent< { id: 'jupyterlab_scipy_210', displayName: 'jupyter-scipy:v2.1.0', - labels: { pythonVersion: '3.13', jupyterlabVersion: '2.1.0' }, + labels: { 'Python Version': '3.13', 'JupyterLab Version': '2.1.0' }, hidden: true, redirect: { to: 'jupyterlab_scipy_220',