Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,31 +1,17 @@
import React, { useMemo, useState } from 'react';
import { Alert, Button, debounce, Flex, SearchInput, Truncate } from '@patternfly/react-core';
import React, { ReactNode, useMemo, useState } from 'react';
import { Alert, Button, debounce, Flex, SearchInput } from '@patternfly/react-core';

import BacklogListSelector from 'Components/PatternFly/BacklogListSelector';
import { CollectionResponse } from 'services/CollectionsService';
import useEmbeddedCollections from './hooks/useEmbeddedCollections';

const selectorListCells = [
{
name: 'Name',
render: ({ name }) => (
<Button variant="link" className="pf-u-pl-0" isInline>
{name}
</Button>
),
},
{
name: 'Description',
render: ({ description }) => <Truncate content={description} />,
},
];

export type CollectionAttacherProps = {
// A collection ID that should not be visible in the collection attacher component. This is
// used when editing a collection to prevent reference cycles.
excludedCollectionId: string | null;
initialEmbeddedCollections: CollectionResponse[];
onSelectionChange: (collections: CollectionResponse[]) => void;
collectionTableCells: { name: string; render: (collection: CollectionResponse) => ReactNode }[];
};

function compareNameLowercase(search: string): (item: { name: string }) => boolean {
Expand All @@ -36,6 +22,7 @@ function CollectionAttacher({
excludedCollectionId,
initialEmbeddedCollections,
onSelectionChange,
collectionTableCells,
}: CollectionAttacherProps) {
const [search, setSearch] = useState('');
const embedded = useEmbeddedCollections(excludedCollectionId, initialEmbeddedCollections);
Expand Down Expand Up @@ -69,7 +56,7 @@ function CollectionAttacher({
onDeselectItem={({ id }) => detach(id)}
onSelectionChange={onSelectionChange}
rowKey={({ id }) => id}
cells={selectorListCells}
cells={collectionTableCells}
selectedLabel="Attached collections"
deselectedLabel="Detached collections"
selectButtonText="Attach"
Expand Down
40 changes: 23 additions & 17 deletions ui/apps/platform/src/Containers/Collections/CollectionForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {
Label,
TextInput,
Title,
Truncate,
} from '@patternfly/react-core';
import { CubesIcon } from '@patternfly/react-icons';
import { TableComposable, TableVariant, Tbody, Tr, Td } from '@patternfly/react-table';
Expand All @@ -24,23 +23,26 @@ import { CollectionResponse } from 'services/CollectionsService';
import { getIsValidLabelKey } from 'utils/labels';
import { CollectionPageAction } from './collections.utils';
import RuleSelector from './RuleSelector';
import CollectionAttacher from './CollectionAttacher';
import CollectionAttacher, { CollectionAttacherProps } from './CollectionAttacher';
import { Collection, ScopedResourceSelector, SelectorEntityType } from './types';

function AttachedCollectionTable({ collections }: { collections: CollectionResponse[] }) {
function AttachedCollectionTable({
collections,
collectionTableCells,
}: {
collections: CollectionResponse[];
collectionTableCells: CollectionAttacherProps['collectionTableCells'];
}) {
return collections.length > 0 ? (
<TableComposable aria-label="Attached collections" variant={TableVariant.compact}>
<Tbody>
{collections.map(({ name, description }) => (
<Tr key={name}>
<Td dataLabel="Name">
<Button variant="link" className="pf-u-pl-0" isInline>
{name}
</Button>
</Td>
<Td dataLabel="Description">
<Truncate content={description} />
</Td>
{collections.map((collection) => (
<Tr key={collection.name}>
{collectionTableCells.map(({ name, render }) => (
<Td key={name} dataLabel={name}>
{render(collection)}
</Td>
))}
</Tr>
))}
</Tbody>
Expand All @@ -62,9 +64,8 @@ export type CollectionFormProps = {
/* collection responses for the embedded collections of `initialData` */
initialEmbeddedCollections: CollectionResponse[];
onSubmit: (collection: Collection) => Promise<void>;
/* Callback used when clicking on a collection name in the CollectionAttacher section. If
left undefined, collection names will not be linked. */
appendTableLinkAction?: (collectionId: string) => void;
/* Table cells to render for each collection in the CollectionAttacher component */
collectionTableCells: CollectionAttacherProps['collectionTableCells'];
/* content to render before the main form */
headerContent?: ReactElement;
};
Expand Down Expand Up @@ -103,6 +104,7 @@ function CollectionForm({
initialData,
initialEmbeddedCollections,
onSubmit,
collectionTableCells,
}: CollectionFormProps) {
const history = useHistory();

Expand Down Expand Up @@ -253,7 +255,10 @@ function CollectionForm({
Attached collections
</Title>
{isReadOnly ? (
<AttachedCollectionTable collections={initialEmbeddedCollections} />
<AttachedCollectionTable
collections={initialEmbeddedCollections}
collectionTableCells={collectionTableCells}
/>
) : (
<>
<p>Extend this collection by attaching other sets.</p>
Expand All @@ -263,6 +268,7 @@ function CollectionForm({
}
initialEmbeddedCollections={initialEmbeddedCollections}
onSelectionChange={onEmbeddedCollectionsChange}
collectionTableCells={collectionTableCells}
/>
</>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { CollectionPageAction } from './collections.utils';
import CollectionResults from './CollectionResults';
import { Collection } from './types';
import { parseCollection } from './converter';
import CollectionForm from './CollectionForm';
import CollectionForm, { CollectionFormProps } from './CollectionForm';

export type CollectionFormDrawerProps = {
hasWriteAccessForCollections: boolean;
Expand All @@ -34,9 +34,7 @@ export type CollectionFormDrawerProps = {
toggleDrawer: (isOpen: boolean) => void;
headerContent?: ReactElement;
onSubmit: (collection: Collection) => Promise<void>;
/* Callback used when clicking on a collection name in the CollectionAttacher section. If
left undefined, collection names will not be linked. */
appendTableLinkAction?: (collectionId: string) => void;
collectionTableCells: CollectionFormProps['collectionTableCells'];
};

function CollectionFormDrawer({
Expand All @@ -48,6 +46,7 @@ function CollectionFormDrawer({
isDrawerOpen,
toggleDrawer,
onSubmit,
collectionTableCells,
}: CollectionFormDrawerProps) {
const initialData = parseCollection(collectionData.collection);
const initialEmbeddedCollections = collectionData.embeddedCollections;
Expand All @@ -69,9 +68,11 @@ function CollectionFormDrawer({
<DrawerHead>
<Title headingLevel="h2">Collection results</Title>
<Text>See a preview of current matches.</Text>
<DrawerActions>
<DrawerCloseButton onClick={() => toggleDrawer(false)} />
</DrawerActions>
{!isInlineDrawer && (
<DrawerActions>
<DrawerCloseButton onClick={() => toggleDrawer(false)} />
</DrawerActions>
)}
</DrawerHead>
<DrawerPanelBody className="pf-u-h-100" style={{ overflow: 'auto' }}>
<CollectionResults />
Expand All @@ -93,6 +94,7 @@ function CollectionFormDrawer({
initialData={initialData}
initialEmbeddedCollections={initialEmbeddedCollections}
onSubmit={onSubmit}
collectionTableCells={collectionTableCells}
/>
)}
</DrawerContentBody>
Expand Down
128 changes: 128 additions & 0 deletions ui/apps/platform/src/Containers/Collections/CollectionFormModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import React, { ReactElement } from 'react';
import { Button, Divider, Flex, FlexItem, Modal, Title, Truncate } from '@patternfly/react-core';
import { ExternalLinkAltIcon } from '@patternfly/react-icons';
import { useMediaQuery } from 'react-responsive';

import useSelectToggle from 'hooks/patternfly/useSelectToggle';
import { collectionsBasePath } from 'routePaths';
import { CollectionResponse } from 'services/CollectionsService';
import useCollection from './hooks/useCollection';
import CollectionFormDrawer from './CollectionFormDrawer';

export type CollectionsFormModalProps = {
hasWriteAccessForCollections: boolean;
collectionId: string;
onClose: () => void;
};
const collectionTableCells = [
{
name: 'Name',
render: ({ id, name }: CollectionResponse) => (
<Button
variant="link"
component="a"
isInline
href={`${collectionsBasePath}/${id}?action=edit`}
target="_blank"
rel="noopener noreferrer"
icon={<ExternalLinkAltIcon />}
>
{name}
</Button>
),
},
{
name: 'Description',
render: ({ description }) => <Truncate content={description} />,
},
];

function CollectionsFormModal({
hasWriteAccessForCollections,
collectionId,
onClose,
}: CollectionsFormModalProps) {
const isLargeScreen = useMediaQuery({ query: '(min-width: 992px)' }); // --pf-global--breakpoint--lg
const {
isOpen: isDrawerOpen,
toggleSelect: toggleDrawer,
closeSelect: closeDrawer,
openSelect: openDrawer,
} = useSelectToggle(isLargeScreen);

const { data, loading, error } = useCollection(collectionId);

let content: ReactElement | null = null;

if (error) {
content = (
<>
{error.message}
{/* TODO - Handle UI for network errors */}
</>
);
} else if (loading) {
content = <>{/* TODO - Handle UI for loading state */}</>;
} else if (data) {
content = (
<Modal
isOpen
onClose={onClose}
aria-label={`View ${data.collection.name}`}
width="90vw"
hasNoBodyWrapper
header={
<Flex className="pf-u-pb-md" alignItems={{ default: 'alignItemsCenter' }}>
<FlexItem grow={{ default: 'grow' }}>
<Title headingLevel="h2">{data.collection.name}</Title>
</FlexItem>
{hasWriteAccessForCollections && (
<Button
variant="link"
component="a"
href={`${collectionsBasePath}/${collectionId}?action=edit`}
target="_blank"
rel="noopener noreferrer"
icon={<ExternalLinkAltIcon />}
iconPosition="right"
>
Edit collection
</Button>
)}
{isDrawerOpen ? (
<Button variant="secondary" onClick={closeDrawer}>
Hide results
</Button>
) : (
<Button variant="secondary" onClick={openDrawer}>
Preview results
</Button>
)}
<Divider orientation={{ default: 'vertical' }} component="div" />
</Flex>
}
>
<Divider component="div" />
<CollectionFormDrawer
// We do not want to present the user with options to change the collection when in this modal
hasWriteAccessForCollections={false}
action={{
type: 'view',
collectionId,
}}
collectionData={data}
isInlineDrawer={isLargeScreen}
isDrawerOpen={isDrawerOpen}
toggleDrawer={toggleDrawer}
// Since the form cannot be submitted, stub this out with an empty promise
onSubmit={() => Promise.resolve()}
collectionTableCells={collectionTableCells}
/>
</Modal>
);
}

return content;
}

export default CollectionsFormModal;
Loading