-
Notifications
You must be signed in to change notification settings - Fork 2
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
feat: add filters to Team page #4176
base: ian/team-page-rebuild
Are you sure you want to change the base?
Conversation
return mappedArrayFilters; | ||
}; | ||
|
||
export const addDisplayNamesToFilters = <T extends object>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function deals with something similar to the development of SortControl
component in this PR:#4137
We have to create a number of ways to name each filter and sometimes they are not very useful for searchParams, UI display, and as keys for objects. A way round this is to slugify the display name when adding the searchParams.
That was it is a nice, followable name in the url that you can connect with the UI and it means for a dev adding it to a new list, you don't need to maintain 3 different types of similar data
const route = useCurrentRoute(); | ||
|
||
useEffect(() => { | ||
setOriginalRecords(records); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This maintains a record on mount, I can use for resetting it. I was thinking about how this might change with Search being added, and may have to refine this approach when we bring the two things together. It ultimately though, works well as a component.
fb24317
to
024214c
Compare
b30086d
to
3034f09
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a solid start - working largely as expected which is great to see. The generic approach is working well and I can see us easily using this elsewhere which is exactly what I want to be seeing. I think there's some complexity here around state management that we can probably address, but more on that below.
There's a few small comments on code, mostly around types I think.
I'm sure it's on your radar already, but there's currently zero test coverage for the new filter and search functionality - something to follow up on shortly.
I've got a few UI bits of feedback here also -
-
A focussed checkbox has a transparent background. Very likely not introduced here, just the first time we're using a checkbox on a non-white background I'd guess.
Screen.Recording.2025-01-27.at.11.49.22.mov
- We need to handle 0 results - maybe explain this to the user.
Biggest bit of feedback is around state management and accessibility - this component is without doubt a form!
It accepts user input, has a submit button, meets all the criteria. As such we should treat it as one both in terms of markup (using <form>
, <fieldset>
elements etc for accessibility) and in terms of state management.
Have a think about this or a crack at it - if we consider this a form, and maybe use formik to be a single source of truth in terms of the form values, how would this simplify our code?
Potentially we could make these changes -
addToSearchParams()
clearSearchParams()
- Make these reactive (
useEffect()
?) based onformik.values
- Make these reactive (
clearAllFilters()
- This just becomes
formik.resetForm()
- This just becomes
removeFilter()
removeSelectedFilter()
updateFilterState()
- These would just be a
formik.setFieldValue()
I guess
- These would just be a
All of these functions as you currently have them are manually controlling, validating, and setting our state and we could centralise this in one place.
I would see this as a win in terms of a11y, simplicity, and maintainability. Very happy to talk this change through if you'd like to 👍
</Typography> | ||
</FiltersToggle> | ||
<Box sx={{ display: "flex", gap: 1 }}> | ||
{selectedFilters && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's some UI inconsistency here (cc @ianjon3s)
- This list displays the filters currently applied to my list (it's only populated when I hit "Appy...") ✅
- It does not update as I check and uncheck filters ✅
- When I remove a tag, a filter becomes unchecked in the form ❌
If this list is meant to describe the filters currently applied, removing a tag should not update the form (but it should update the list - as it currently does).
Open to be challenged here - this felt pretty weird to me, will see if I can cross-reference with a similar component.
Screen.Recording.2025-01-27.at.11.58.49.mov
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure Ian will have better input, but just so I can follow, what is your expected behaviour here? To remove a chip but have the checkbox checked?
Or should you be prompted to apply the filter after clicking delete on a chip?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also would love Ian's thoughts here 😅
The chips are intended to work when the filter accordion is closed, so these should apply immediately when removed - this is expected.
The bit that I found unexpected was that there's just a one-way relationship between the form and chips. I'm not sure how this is best handled when we have one action (remove chip) which is immediate, and another (check/uncheck, and then hit apply) which is not immediate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree the inconsistency isn't ideal. The chips are indeed intended to function when the accordion is closed, so will need to action without the use of a submit button.
Potentially a bit of a refactor, but could we make the filter checkboxes submit on check/uncheck, rather than having a submit button? This would be consistent with the other form fields that will be visible on the page (search input, reorder selects) in that they take immediate action. In terms of accessibility, having an aria-live
property on the flows subtitle (Showing 6 services
etc) would be necessary for screen readers.
export enum FlowTagType { | ||
Status = "status", | ||
ApplicationType = "applicationType", | ||
ServiceType = "serviceType", | ||
} | ||
|
||
export enum StatusVariant { | ||
Online = "online", | ||
Offline = "offline", | ||
} | ||
|
||
const BG_ONLINE = "#D6FFD7"; | ||
const BG_OFFLINE = "#EAEAEA"; | ||
const BG_APPLICATION_TYPE = "#D6EFFF"; | ||
const BG_SERVICE_TYPE = "#FFEABE"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this needs to be rebased to main
as these changes already exist in editor.planx.uk/src/ui/editor/FlowTag/types.ts
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rebased to main, but nothing came up for this. Something to resolve in the Epic PR?
f9bcf42
to
d6bd70c
Compare
91a3328
to
6dab004
Compare
77aa1eb
to
d43f596
Compare
Co-authored-by: Ian Jones <[email protected]>
separate styles switch lodash filter to array.filter remove filter lodash import swap out lodash map switch out lodash map feat: Add tags to flowCard (#4205) switch to filter on checkbox and add comments run prettier on Filter/ remove search and parseUrl dependency array place filterOptions in static state run prettier on changed files status tag shouldAddTag always true
d43f596
to
1e38068
Compare
6dab004
to
291500b
Compare
}: FiltersProps<T>) => { | ||
const [expanded, setExpanded] = useState<boolean>(false); | ||
const [filters, setFilters] = useState<Filters<T> | null>(null); | ||
const [optionsToFilter] = useState(filterOptions); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had to memo-ise this or there was an issue with re-rendering from the face it was an array of options, react didn't like it as a dependency. Decided to put it here as a static state variable so I didn't need to memo-ise it in the Team.tsx
and I run into the same re-rendering issue if I memo-ise it within Filters.tsx
because I still need to add filterOptions
as a dependency.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could something similar work for keys
in the useSearch
hook?
} | ||
}, [filters, setFilteredRecords, records, optionsToFilter]); | ||
|
||
useEffect(() => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a big change since we don't have a button now to update the search params, I have added it to a useEffect()
here. I wanted to keep the functions separate so I could conditionally call each one and not have entangle one function with the conditions. It felt longer, but more readable? Happy to take feedback here though
What does this PR do?
This is tied into the larger epic here: https://trello.com/c/7av8lsVr/3160-epic-as-an-editor-i-want-to-have-a-way-to-organise-my-flows-so-that-i-can-easily-find-what-im-looking-for
This work introduces a
Filters
component which takes in a governing type and records, and looks to filter this record based on givenfilterOptions
. It was built with further use cases in mind, so I tried to decouple the logic into what is Filter option agnostic, and what should be defined when the filter is used.By this, I have added the
validationFn
into thefilterOptions
defined in the Parent component of<Filters>
so you can have control over how we validate when something is true or false. Especially ran into this whenstatus
would either beoffline | online
whereas forserviceType
it was defined byhasSendComponent === true | false
.As I was building the component I found it becoming quite long with functions and code, so I separated things out in an iterable
FilterColumns
component and a selection of helper functions inhelpers.ts
. It did dawn on me thathelpers
may be language for testing and it should actually beutils
, but I wasn't sure.Ways to test
Testing of the logic needs to happen in a few places:
Data for
applicationType
is still to be added and the PR is here: #4169Warning
Struggling with the way search params are being parsed in my
parseStateFromURL()
function - they seem to filter but keep resetting the url