Skip to content

Commit

Permalink
🌱 Refactor ComposableTableWithControls to use render-props pattern …
Browse files Browse the repository at this point in the history
…for more flexibility, apply changes to Migration Waves and Jira Trackers tables (#792)

In preparation for the rest of the dynamic reports work, this PR takes
the table abstraction introduced in
#598 for migration waves and
refactors it to be even more composable. The goal is that all JSX used
to render the Tr and Td elements is passed in from the consumer, with
functions made available via the render-props pattern for abstracting
repetitive props out into the component. This way the consumer can
benefit from a simplified way to render tables without losing the
flexibility to pass any props necessary on any row/cell.

There are some remaining questions to be answered about this
abstraction, to be addressed in followup PRs. Some of these are covered
in TODO comments here, in particular how to handle composable access to
the props of the Thead/Tr elements without too much repetition, and how
to handle non-compound-expandable rows and sorting controls. These will
be addressed as part of adding the Issues table for dynamic reports.

---------

Signed-off-by: Mike Turley <mike.turley@alum.cs.umass.edu>
Co-authored-by: Ian Bolton <ibolton@redhat.com>
  • Loading branch information
mturley and ibolton336 authored Apr 7, 2023
1 parent 504e898 commit 63dceb5
Show file tree
Hide file tree
Showing 5 changed files with 391 additions and 421 deletions.
45 changes: 20 additions & 25 deletions client/src/app/pages/jira-trackers/jira-trackers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ import {
FilterType,
} from "@app/shared/components/FilterToolbar";
import { useFilterState } from "@app/shared/hooks/useFilterState";
import { IComposableRow } from "@app/shared/components/composable-table-with-controls/composable-table-with-controls";
import { useFetchJiraTrackers } from "@app/queries/jiratrackers";
import { Tbody, Tr, Td } from "@patternfly/react-table";

export const JiraTrackers: React.FC = () => {
const { t } = useTranslation();
Expand Down Expand Up @@ -68,27 +68,8 @@ export const JiraTrackers: React.FC = () => {

const columnNames = {
name: "Name",
url: "Start date",
type: "Type",
actions: "",
};

const rows: IComposableRow[] = [];
currentPageItems?.forEach((item, index) => {
rows.push({
name: item.name,
cells: [
{
title: item.name,
width: 25,
},
{
title: "url",
width: 10,
},
],
});
});
url: "URL",
} as const;

return (
<>
Expand All @@ -103,7 +84,7 @@ export const JiraTrackers: React.FC = () => {
then={<AppPlaceholder />}
>
<ComposableTableWithControls
isSelectable={true}
isSelectable
toolbarActions={
<>
<ToolbarGroup variant="button-group">
Expand Down Expand Up @@ -135,10 +116,10 @@ export const JiraTrackers: React.FC = () => {
</ToolbarGroup>
</>
}
rowItems={rows}
columnNames={columnNames}
isLoading={isFetching}
fetchError={fetchError}
isNoData={jiraTrackers.length === 0}
paginationProps={paginationProps}
toolbarToggle={
<FilterToolbar<JiraTracker>
Expand All @@ -148,7 +129,21 @@ export const JiraTrackers: React.FC = () => {
/>
}
toolbarClearAllFilters={handleOnClearAllFilters}
></ComposableTableWithControls>
renderTableBody={() => (
<Tbody>
{currentPageItems?.map((jiraTracker) => (
<Tr key={jiraTracker.name}>
<Td width={25} dataLabel={columnNames.name}>
{jiraTracker.name}
</Td>
<Td width={10} dataLabel={columnNames.url}>
TODO: URL
</Td>
</Tr>
))}
</Tbody>
)}
/>
</ConditionalRender>
</PageSection>
</>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
import React from "react";
import { Application } from "@app/api/models";
import { Application, Wave } from "@app/api/models";
import { useTranslation } from "react-i18next";
import { usePaginationState } from "@app/shared/hooks/usePaginationState";
import { useSortState } from "@app/shared/hooks/useSortState";
import { ComposableTableWithControls } from "@app/shared/components/composable-table-with-controls";
import { IComposableRow } from "@app/shared/components/composable-table-with-controls/composable-table-with-controls";
import TrashIcon from "@patternfly/react-icons/dist/esm/icons/trash-icon";
import { Button } from "@patternfly/react-core";
import { Tbody, Td, Tr } from "@patternfly/react-table";
import alignment from "@patternfly/react-styles/css/utilities/Alignment/alignment";

export interface IWaveApplicationsTableProps {
wave: Wave;
applications: Application[];
}

export const WaveApplicationsTable: React.FC<IWaveApplicationsTableProps> = ({
wave,
applications,
}) => {
const { t } = useTranslation();
Expand All @@ -21,8 +24,7 @@ export const WaveApplicationsTable: React.FC<IWaveApplicationsTableProps> = ({
description: "Description",
businessService: "Business service",
owner: "Owner",
actions: "",
};
} as const;

const getSortValues = (item: Application) => [
item?.name || "",
Expand All @@ -33,6 +35,7 @@ export const WaveApplicationsTable: React.FC<IWaveApplicationsTableProps> = ({
"", // Action column
];

// TODO add sort stuff to ComposableTableWithControls
const { sortBy, onSort, sortedItems } = useSortState(
applications,
getSortValues
Expand All @@ -41,51 +44,40 @@ export const WaveApplicationsTable: React.FC<IWaveApplicationsTableProps> = ({
const { currentPageItems, setPageNumber, paginationProps } =
usePaginationState(sortedItems, 10);

const rows: IComposableRow[] = [];
currentPageItems?.forEach((item, index) => {
rows.push({
id: item.name,
cells: [
{
id: "appName",
title: item.name,
width: 25,
},
{
id: "description",
title: item.description,
width: 10,
},
{
id: "businessServiceName",
title: item?.businessService?.name,
width: 10,
},
{
id: "owner",
title: "TODO: Owner",
width: 10,
},
{
children: (
<Button type="button" variant="plain" onClick={() => {}}>
<TrashIcon />
</Button>
),
width: 10,
style: { textAlign: "right" },
},
],
});
});

return (
<ComposableTableWithControls
variant="compact"
rowItems={rows}
aria-label={`Applications table for wave ${wave.name}`}
columnNames={columnNames}
hasActionsColumn
paginationProps={paginationProps}
withoutBottomPagination={true}
></ComposableTableWithControls>
isNoData={applications.length === 0}
renderTableBody={() => (
<Tbody>
{currentPageItems?.map((app) => (
<Tr key={app.name}>
<Td width={25} dataLabel={columnNames.appName}>
{app.name}
</Td>
<Td width={10} dataLabel={columnNames.description}>
{app.description}
</Td>
<Td width={10} dataLabel={columnNames.businessService}>
{app?.businessService?.name}
</Td>
<Td width={10} dataLabel={columnNames.owner}>
TODO: Owner
</Td>
<Td width={10} className={alignment.textAlignRight}>
<Button type="button" variant="plain" onClick={() => {}}>
<TrashIcon />
</Button>
</Td>
</Tr>
))}
</Tbody>
)}
/>
);
};
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
import React from "react";
import { Application, Stakeholder, Wave } from "@app/api/models";
import { Stakeholder, Wave } from "@app/api/models";
import { useTranslation } from "react-i18next";
import { usePaginationState } from "@app/shared/hooks/usePaginationState";
import { useSortState } from "@app/shared/hooks/useSortState";
import { ComposableTableWithControls } from "@app/shared/components/composable-table-with-controls";
import { IComposableRow } from "@app/shared/components/composable-table-with-controls/composable-table-with-controls";
import TrashIcon from "@patternfly/react-icons/dist/esm/icons/trash-icon";
import { Button } from "@patternfly/react-core";
import { Td, Tr } from "@patternfly/react-table";
import alignment from "@patternfly/react-styles/css/utilities/Alignment/alignment";

export interface IWaveStakeholdersTableProps {
wave: Wave;
stakeholders: Stakeholder[];
}

export const WaveStakeholdersTable: React.FC<IWaveStakeholdersTableProps> = ({
wave,
stakeholders,
}) => {
const { t } = useTranslation();
Expand All @@ -22,8 +25,9 @@ export const WaveStakeholdersTable: React.FC<IWaveStakeholdersTableProps> = ({
const columnNames = {
name: "Name",
jobFunction: "Job Function",
role: "role",
owner: "Stakeholders",
role: "Role",
email: "Email",
groups: "Stakeholder groups",
actions: "",
};

Expand All @@ -36,6 +40,7 @@ export const WaveStakeholdersTable: React.FC<IWaveStakeholdersTableProps> = ({
"", // Action column
];

// TODO add sort stuff to ComposableTableWithControls
const { sortBy, onSort, sortedItems } = useSortState(
stakeholders,
getSortValues
Expand All @@ -44,56 +49,43 @@ export const WaveStakeholdersTable: React.FC<IWaveStakeholdersTableProps> = ({
const { currentPageItems, setPageNumber, paginationProps } =
usePaginationState(sortedItems, 10);

const rows: IComposableRow[] = [];
currentPageItems?.forEach((item, index) => {
rows.push({
id: item.name,
cells: [
{
id: "name",
title: item.name,
width: 25,
},
{
id: "jobFunction",
title: item.jobFunction?.name,
width: 10,
},
{
id: "role",
title: "TODO: Role",
width: 10,
},
{
id: "email",
title: item.email,
width: 10,
},
{
id: "groups",
title: item?.stakeholderGroups?.length?.toString(),
width: 10,
},
{
children: (
<Button type="button" variant="plain" onClick={() => {}}>
<TrashIcon />
</Button>
),
width: 10,
style: { textAlign: "right" },
},
],
});
});

return (
<ComposableTableWithControls
variant="compact"
rowItems={rows}
aria-label={`Stakeholders table for wave ${wave.name}`}
columnNames={columnNames}
hasActionsColumn
paginationProps={paginationProps}
withoutBottomPagination={true}
></ComposableTableWithControls>
isNoData={stakeholders.length === 0}
renderTableBody={() => (
<>
{currentPageItems?.map((stakeholder) => (
<Tr key={stakeholder.name}>
<Td width={25} dataLabel={columnNames.name}>
{stakeholder.name}
</Td>
<Td width={10} dataLabel={columnNames.jobFunction}>
{stakeholder.jobFunction?.name}
</Td>
<Td width={10} dataLabel={columnNames.role}>
TODO: Role
</Td>
<Td width={10} dataLabel={columnNames.email}>
{stakeholder.email}
</Td>
<Td width={10} dataLabel={columnNames.groups}>
{stakeholder?.stakeholderGroups?.length?.toString()}
</Td>
<Td width={10} className={alignment.textAlignRight}>
<Button type="button" variant="plain" onClick={() => {}}>
<TrashIcon />
</Button>
</Td>
</Tr>
))}
</>
)}
/>
);
};
Loading

0 comments on commit 63dceb5

Please sign in to comment.