From 3f415a5cd51e9911672b0a542f89dbb9ae44dce7 Mon Sep 17 00:00:00 2001 From: Christian Vogt Date: Wed, 8 Nov 2023 10:33:19 -0500 Subject: [PATCH] improve section divider rendering --- .../pages/projects/ProjectDetails.spec.ts | 21 +++++++++++++------ .../screens/detail/DetailsSection.tsx | 10 ++++++++- .../detail/ProjectDetailsComponents.scss | 5 +++++ .../detail/ProjectDetailsComponents.tsx | 3 +++ .../data-connections/DataConnectionsList.tsx | 3 +-- .../screens/detail/notebooks/NotebookList.tsx | 3 +-- .../detail/pipelines/PipelinesSection.tsx | 11 ++++------ .../screens/detail/storage/StorageList.tsx | 3 +-- 8 files changed, 39 insertions(+), 20 deletions(-) create mode 100644 frontend/src/pages/projects/screens/detail/ProjectDetailsComponents.scss diff --git a/frontend/src/__tests__/integration/pages/projects/ProjectDetails.spec.ts b/frontend/src/__tests__/integration/pages/projects/ProjectDetails.spec.ts index 2f374c7b1f..1e7091da44 100644 --- a/frontend/src/__tests__/integration/pages/projects/ProjectDetails.spec.ts +++ b/frontend/src/__tests__/integration/pages/projects/ProjectDetails.spec.ts @@ -7,11 +7,20 @@ test('Empty project', async ({ page }) => { // wait for page to load await page.waitForSelector('text=Models and model servers'); - // the dividers number should always 1 less than the section number - const sections = await page.locator('[data-id="details-page-section"]').all(); - const dividers = await page.locator('[data-id="details-page-section-divider"]').all(); - - expect(dividers).toHaveLength(sections.length - 1); + // all empty sections should be divided + const sectionsLocator = page.locator('[data-id="details-page-section"]'); + const sections = await sectionsLocator.all(); + const dividers = await page.locator('.odh-details-section--divide').all(); + + expect(dividers).toHaveLength(sections.length); + + // all but the last section should include the bottom border divider + const sectionsWithDivider = await sectionsLocator.evaluateAll((elements) => + elements + .map((element) => window.getComputedStyle(element).getPropertyValue('border-bottom-style')) + .filter((x) => x !== 'none'), + ); + expect(sectionsWithDivider).toHaveLength(sections.length - 1); }); test('Non-empty project', async ({ page }) => { @@ -21,7 +30,7 @@ test('Non-empty project', async ({ page }) => { await page.waitForSelector('text=Test Notebook'); // we fill in the page with data, so there should be no dividers on the page - expect(await page.locator('[data-id="details-page-section-divider"]').all()).toHaveLength(0); + expect(await page.locator('.odh-details-section--divide').all()).toHaveLength(0); // check the x-small size shown correctly expect(page.getByText('Small')).toBeTruthy(); diff --git a/frontend/src/pages/projects/screens/detail/DetailsSection.tsx b/frontend/src/pages/projects/screens/detail/DetailsSection.tsx index a2dd71c4d3..7119d18320 100644 --- a/frontend/src/pages/projects/screens/detail/DetailsSection.tsx +++ b/frontend/src/pages/projects/screens/detail/DetailsSection.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import classNames from 'classnames'; import { Alert, Bullseye, @@ -21,6 +22,7 @@ type DetailsSectionProps = { emptyState?: React.ReactNode; children: React.ReactNode; labels?: React.ReactNode[]; + showDivider?: boolean; }; const DetailsSection: React.FC = ({ @@ -33,6 +35,7 @@ const DetailsSection: React.FC = ({ loadError, title, labels, + showDivider, }) => { const renderContent = () => { if (loadError) { @@ -59,7 +62,12 @@ const DetailsSection: React.FC = ({ }; return ( - + diff --git a/frontend/src/pages/projects/screens/detail/ProjectDetailsComponents.scss b/frontend/src/pages/projects/screens/detail/ProjectDetailsComponents.scss new file mode 100644 index 0000000000..3d70a11d72 --- /dev/null +++ b/frontend/src/pages/projects/screens/detail/ProjectDetailsComponents.scss @@ -0,0 +1,5 @@ +.odh-project-details-components__item:has(> .odh-details-section--divide):has( + + .odh-project-details-components__item + ) { + border-bottom: 1px solid var(--pf-global--BorderColor--100); +} diff --git a/frontend/src/pages/projects/screens/detail/ProjectDetailsComponents.tsx b/frontend/src/pages/projects/screens/detail/ProjectDetailsComponents.tsx index 497c3a5af9..f9df3533a9 100644 --- a/frontend/src/pages/projects/screens/detail/ProjectDetailsComponents.tsx +++ b/frontend/src/pages/projects/screens/detail/ProjectDetailsComponents.tsx @@ -12,6 +12,8 @@ import StorageList from './storage/StorageList'; import { ProjectSectionTitles } from './const'; import DataConnectionsList from './data-connections/DataConnectionsList'; +import './ProjectDetailsComponents.scss'; + type SectionType = { id: ProjectSectionID; component: React.ReactNode; @@ -74,6 +76,7 @@ const ProjectDetailsComponents: React.FC = () => { id={id} aria-label={ProjectSectionTitles[id]} data-id="details-page-section" + className="odh-project-details-components__item" > {component} diff --git a/frontend/src/pages/projects/screens/detail/data-connections/DataConnectionsList.tsx b/frontend/src/pages/projects/screens/detail/data-connections/DataConnectionsList.tsx index 92a686590e..c13c7af21a 100644 --- a/frontend/src/pages/projects/screens/detail/data-connections/DataConnectionsList.tsx +++ b/frontend/src/pages/projects/screens/detail/data-connections/DataConnectionsList.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { Button, Divider } from '@patternfly/react-core'; +import { Button } from '@patternfly/react-core'; import EmptyDetailsList from '~/pages/projects/screens/detail/EmptyDetailsList'; import { ProjectSectionID } from '~/pages/projects/screens/detail/types'; import DetailsSection from '~/pages/projects/screens/detail/DetailsSection'; @@ -43,7 +43,6 @@ const DataConnectionsList: React.FC = () => { > - {isDataConnectionsEmpty && } { diff --git a/frontend/src/pages/projects/screens/detail/notebooks/NotebookList.tsx b/frontend/src/pages/projects/screens/detail/notebooks/NotebookList.tsx index 5759aea454..17dcade087 100644 --- a/frontend/src/pages/projects/screens/detail/notebooks/NotebookList.tsx +++ b/frontend/src/pages/projects/screens/detail/notebooks/NotebookList.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { Button, Divider } from '@patternfly/react-core'; +import { Button } from '@patternfly/react-core'; import { useNavigate } from 'react-router-dom'; import EmptyDetailsList from '~/pages/projects/screens/detail/EmptyDetailsList'; import { ProjectSectionID } from '~/pages/projects/screens/detail/types'; @@ -53,7 +53,6 @@ const NotebookList: React.FC = () => { > - {isNotebooksEmpty && } ); }; diff --git a/frontend/src/pages/projects/screens/detail/pipelines/PipelinesSection.tsx b/frontend/src/pages/projects/screens/detail/pipelines/PipelinesSection.tsx index b3e865c791..a1e096cb1d 100644 --- a/frontend/src/pages/projects/screens/detail/pipelines/PipelinesSection.tsx +++ b/frontend/src/pages/projects/screens/detail/pipelines/PipelinesSection.tsx @@ -1,5 +1,4 @@ import * as React from 'react'; -import { Divider } from '@patternfly/react-core'; import { ProjectSectionID } from '~/pages/projects/screens/detail/types'; import { ProjectSectionTitles } from '~/pages/projects/screens/detail/const'; import DetailsSection from '~/pages/projects/screens/detail/DetailsSection'; @@ -7,11 +6,11 @@ import { PipelineServerTimedOut, usePipelinesAPI } from '~/concepts/pipelines/co import NoPipelineServer from '~/concepts/pipelines/NoPipelineServer'; import ImportPipelineButton from '~/concepts/pipelines/content/import/ImportPipelineButton'; import PipelinesList from '~/pages/projects/screens/detail/pipelines/PipelinesList'; -import EnsureAPIAvailability from '~/concepts/pipelines/EnsureAPIAvailability'; import PipelineServerActions from '~/concepts/pipelines/content/pipelinesDetails/pipeline/PipelineServerActions'; const PipelinesSection: React.FC = () => { const { + apiAvailable, pipelinesServer: { initializing, installed, timedOut }, } = usePipelinesAPI(); @@ -34,19 +33,17 @@ const PipelinesSection: React.FC = () => { variant="kebab" />, ]} - isLoading={initializing} + isLoading={(!apiAvailable && installed) || initializing} isEmpty={!installed} emptyState={} + showDivider={isPipelinesEmpty} > {timedOut ? ( ) : ( - - - + )} - {(isPipelinesEmpty || !installed) && } ); }; diff --git a/frontend/src/pages/projects/screens/detail/storage/StorageList.tsx b/frontend/src/pages/projects/screens/detail/storage/StorageList.tsx index 3ebdd5c4f8..1bf524d29e 100644 --- a/frontend/src/pages/projects/screens/detail/storage/StorageList.tsx +++ b/frontend/src/pages/projects/screens/detail/storage/StorageList.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { Button, Divider } from '@patternfly/react-core'; +import { Button } from '@patternfly/react-core'; import EmptyDetailsList from '~/pages/projects/screens/detail/EmptyDetailsList'; import DetailsSection from '~/pages/projects/screens/detail/DetailsSection'; import { ProjectSectionID } from '~/pages/projects/screens/detail/types'; @@ -43,7 +43,6 @@ const StorageList: React.FC = () => { > setOpen(true)} /> - {isPvcsEmpty && } {