Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Project Household Enrollments table with new designs #989

Merged
merged 29 commits into from
Jan 6, 2025

Conversation

martha
Copy link
Contributor

@martha martha commented Dec 5, 2024

Description

Should be merged into the same release as #998

GH issue: https://github.com/open-path/Green-River/issues/6825
Depends on hmis-warehouse PR: greenriver/hmis-warehouse#5012 This is not a functional dependency, but is needed for spec tests to pass.

This PR

  • Updates the ProjectHouseholdsTable component to render each household member in their own row
  • Defaults to the Clients tab instead of Households tab
  • Updates the columns rendered in both the ProjectHouseholdsTable and the ProjectClientEnrollmentsTable
  • Implements a new TableRowActions that will be more widely adopted with https://github.com/open-path/Green-River/issues/6761
  • Adds a basic storybook story

Type of change

  • Bug fix
  • New feature (adds functionality)
  • Code clean-up / housekeeping
  • Dependency update

Checklist before requesting review

  • I have performed a self-review of my code
  • I have run the code that is being changed under ideal conditions, and it doesn't fail
  • My code includes comments and/or descriptive variable names to help other engineers understand the intent (or not applicable)
  • My code follows the style guidelines of this project (eslint)
  • I have updated the documentation (or not applicable)
  • If it's not obvious how to test this change, I have provided testing instructions in this PR or the related issue

@martha martha marked this pull request as ready for review December 5, 2024 16:15
src/components/elements/CommonMenuButton.tsx Show resolved Hide resolved

// TODO @MARTHA discuss - it's probably a bit silly to redefine ranks here,
// see https://github.com/greenriver/hmis-warehouse/blob/1a170c0e869fd3d69880532f86baa104b800d9ed/drivers/hmis/app/graphql/types/hmis_schema/enums/hud.rb#L723-L733
const hohPriorityMapping: Record<RelationshipToHoH, number> = {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is for sorting the household members by their relationship to HoH. This definition of the ordering is redundant with the backend enum definition, so I wanted to discuss whether there's a better way to pass that info to the frontend.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it's redundant with the backend. The enum lists out the values which are the HUD numeric values (Eg child = 2) whereas here you're specifying a custom order for displaying household members. It seems correct to me that this would live on the frontend.

return (
<>
<TableBody
// TODO @martha - discuss these TableBodies to achieve the `rowgroup` role, is it worth it?
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using the role='rowgroup' turned out to be a bit complicated because of MUI expectations around usage of the TableBody, TableRow and TableCell components. We can't directly have renderRow return a <div role='rowgroup'> which contains all the TableRows within it, because MUI expects that TableRow is a direct child of TableBody. (Here's a screenshot of what happens:)
Screenshot 2024-11-22 at 12 28 25 PM

Rendering each household as its own tbody is the best solution I was able to come up with, but I'm not a huge fan -- both because of the messy markup and because of bloating the already complicated GenericTable api. Let me know if you can think of any better solutions. Or, we could skip using rowgroup. I think the accessibility is still better than before. We could, for example, give the row header cell additional accessible text specifying the household (HoH name or household ID) that would be read when navigating the screen with a screenreader.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

using several tbodys seems like a fine approach. from mdn,

It's permitted to use more than one per table as long as they are all consecutive. This allows to divide the rows ( elements) in large tables into sections, each of which may be separately formatted if so desired.

That seems like basically what we are doing.

overrideTableBody prop maybe isn't the clearest as to what it's doing, since it's really omitting the tbody tag entirely, not overriding it. Maybe another idea is passing TableBodyComponent={React.Fragment} where the default is TableBodyComponent = TableBody, then the passed component can be used directly? Either seems OK.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, thank you for validating the overall approach! I like your api suggestion, took a stab at implementing that here: 3586e74

@@ -0,0 +1,26 @@
import { Card } from '@mui/material';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could add more stories here to test different cases, but I was thinking to leave it as is and create more fleshed-out stories for GenericTable with 6761, or later. Let me know what you think.

@martha martha requested a review from gigxz December 5, 2024 16:22
@martha martha changed the base branch from release-143 to release-144 December 11, 2024 20:57
@martha martha changed the base branch from release-144 to release-146 December 19, 2024 14:06
// This should only be used by tables that take over rendering using renderRow and render a `tbody` within their custom render fn
overrideTableBody?: boolean;
injectBelowRows?: ReactNode; // component to inject below all rendered rows, above footer
getTableRowActions?: (record: T) => TableRowActionsType;
Copy link
Collaborator

@gigxz gigxz Dec 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering about the decision to make this a prop as opposed to just another column definition, can you say more about that? It does add more complexity to the already-complex GenericTable, so just wanting to think that through.

By a column definition, I mean:


const columns = [
         ...othercols,
        {
          key: 'actions',
          render: (enrollment) => (
            <TableRowActions
              record={enrollment}
              primaryAction={viewEnrollmentMenuItemConfig(enrollment}
              secondaryActions={[viewClientMenuItemConfig(enrollment)]}
            />
          ),
        },
]


...
<GenericTableWithData columns={columns} />

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at https://github.com/greenriver/hmis-frontend/pull/998/files, I wonder if this would also give us some more flexibility for use-cases when the action button is more complex. I haven't quite wrapped my head around everything yet, but would something like this simplify the changes in the other PR ?

Definitely an early thought, I'm sure there are things I'm missing about the implementation.

const columns = [
         ...othercols,
        {
          key: 'actions',
          render: (enrollment) => (
            <TableRowActions
              record={enrollment}
              // use this prop if the primary action is a simple navigation
              primaryActionConfig={viewEnrollmentMenuItemConfig(enrollment}
              // use this prop if the primary action is more complicated, or if needs some special treatment
              renderPrimaryAction={<AssignServiceButton .... />}
              // maybe secondary actions are always expected to be navigation
              secondaryActionsConfig={[viewClientMenuItemConfig(enrollment)]}
            />
          ),
        },
]


...
<GenericTableWithData columns={columns} />

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't think thoroughly about it, but it was based on the idea that, since this is a new pattern implemented consistently across many tables in the app, we want the common table component to guide usage and force consistency.

Thinking beyond table row actions, the logical conclusion of that train of thought is that all table controls, like search, filter, toggle, add-record button, etc. live as props on the GenericTable. That doesn't seem desirable - it certainly would lead to a bloated GenericTable. But I guess forcing consistency is the one upside. Currently we rely on GenericTable callers to implement search bars and add-one buttons consistently every time, and there are places in the app where we aren't consistent. (For example, search and toggle on enrollments table vs. search on projects/orgs.)

Anyway, I guess those considerations seem out of scope... I don't feel strongly about it and can take a stab at implementing your suggestion.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

quick example of the consistency issue I'm mentioning, as actually applied to table row actions: https://github.com/greenriver/hmis-frontend/blob/martha/6825-hh-member-row/src/components/elements/table/GenericTable.tsx#L259-L261 By moving this out of GenericTable and into a regular column definition, we leave the column header (or lack thereof / accessibility thereof) up to the caller

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've taken a shot at the refactor, I'd like to look at it together tomorrow: https://github.com/greenriver/hmis-frontend/compare/martha/6761-tables...martha/6761-tables-refactor?expand=1

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we leave the column header (or lack thereof / accessibility thereof) up to the caller

This seems like something that should be addressed globally for all columns. The ColumnDef has the header attribute as optional, so any column could be missing a header and become inaccessible.

Looking at how we implement the Header cells:

{columns.map((def, i) => (
<HeaderCell
key={key(def) || i}
sx={{
...(headerCellSx ? headerCellSx(def) : undefined),
textAlign: def.textAlign,
width: def.width,
}}
>
<strong>{def.header}</strong>
</HeaderCell>
))}

it could be modified so that it sets a hidden header if no visible header is provided:

            <HeaderCell>
              {def.header ? (
                <strong>{def.header}</strong>
              ) : (
                <Box sx={visuallyHidden}>
                  {def.key || def.someOtherFieldThatsRequiredIfHeaderIsMissing}
                </Box>
              )}
            </HeaderCell>

That might address the concern more widely -- we could add additional attributes to ColumnDef if needed, to ensure that columns always have accessible headers.

Copy link
Contributor Author

@martha martha Jan 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good idea, I've left a todo here that I'd like to address in the other PR

render: 'projectName',
},
ENROLLMENT_PERIOD_COL,
{
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pulled this out to here because this is now the only place where it is being used.

@@ -0,0 +1,59 @@
import {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I pulled this change into here, from the other PR, since it's relevant to the Enrollment tables updated in this PR.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice. I never noticed that this was part of the design. It does look like we are missing part of the tooltip contents:

Most accessible text for cells with hover-over interactions should include both cell and tooltip text (2/5/25, 6 days ago’)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this file and RelativeDateDisplay should go in src/components/elements with the rest of the relative date components. (And hopefully we can consolidate later).
The hmis module has general hmis-domain-specific components, like HmisField,HohIndicator, etc. This one isn't domain specific so I think it goes in src.

// This should only be used by tables that take over rendering using renderRow and render a `tbody` within their custom render fn
overrideTableBody?: boolean;
injectBelowRows?: ReactNode; // component to inject below all rendered rows, above footer
getTableRowActions?: (record: T) => TableRowActionsType;
Copy link
Contributor Author

@martha martha Jan 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good idea, I've left a todo here that I'd like to address in the other PR

@gigxz
Copy link
Collaborator

gigxz commented Jan 2, 2025

Is this ready for re-review @martha? And can you re-target to 147? thanks!

@martha martha changed the base branch from release-146 to release-147 January 2, 2025 16:56
@martha
Copy link
Contributor Author

martha commented Jan 2, 2025

@gigxz, yes, I think it's ready for re-review. Thank you!

Copy link
Collaborator

@gigxz gigxz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good!

src/components/elements/table/GenericTable.tsx Outdated Show resolved Hide resolved
src/components/elements/table/tableRowActionUtil.tsx Outdated Show resolved Hide resolved
src/components/elements/table/tableRowActionUtil.tsx Outdated Show resolved Hide resolved
@@ -0,0 +1,59 @@
import {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice. I never noticed that this was part of the design. It does look like we are missing part of the tooltip contents:

Most accessible text for cells with hover-over interactions should include both cell and tooltip text (2/5/25, 6 days ago’)

@@ -0,0 +1,59 @@
import {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this file and RelativeDateDisplay should go in src/components/elements with the rest of the relative date components. (And hopefully we can consolidate later).
The hmis module has general hmis-domain-specific components, like HmisField,HohIndicator, etc. This one isn't domain specific so I think it goes in src.


// TODO @MARTHA discuss - it's probably a bit silly to redefine ranks here,
// see https://github.com/greenriver/hmis-warehouse/blob/1a170c0e869fd3d69880532f86baa104b800d9ed/drivers/hmis/app/graphql/types/hmis_schema/enums/hud.rb#L723-L733
const hohPriorityMapping: Record<RelationshipToHoH, number> = {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it's redundant with the backend. The enum lists out the values which are the HUD numeric values (Eg child = 2) whereas here you're specifying a custom order for displaying household members. It seems correct to me that this would live on the frontend.

src/modules/hmis/hmisUtil.ts Outdated Show resolved Hide resolved
Copy link
Collaborator

@gigxz gigxz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📈 looks good! thanks for your work and iteration!!

@gigxz
Copy link
Collaborator

gigxz commented Jan 3, 2025

@martha merge conflicts might be a pain with this and #998? If you want to hold off on squashing this into main until it contains #998, that would be fine w/ me. (I think that would avoid the conflicts)

Another option is a deploy branch, so this can get plenty of review on QA with design (without being tied to regular releases) - lets revisit Monday!

* Implement Table Actions pattern and default columns

* Add random comment

* Add storybook for table actions and fix cde story

* Implement PR suggestion of moving table row to column def

* add todo

* Finish refactor to use column def instead of getTableAction

* Fix bulk services loading

* Provide a visually hidden header when header isnt provided

* Keep existing behavior of individual/household assessment viewing

* Fix typescript

* Add accessible text for RelativeDateDisplay

* implement one possible approach for client ID typescript

* Remove unnecessary cast
@martha martha changed the base branch from release-147 to deploy/6761-table-actions January 6, 2025 21:55
@martha martha merged commit 4bb7e11 into deploy/6761-table-actions Jan 6, 2025
3 checks passed
@martha martha deleted the martha/6825-hh-member-row branch January 6, 2025 21:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants