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

Add filtering by undefined and fix searching by name #2714

Merged
merged 21 commits into from
Sep 12, 2022
Merged

Conversation

joshri
Copy link
Contributor

@joshri joshri commented Sep 6, 2022

Closes #2711
Closes #2712

  • Fixed a bug where filter categories would be disabled if their first value was undefined
  • Fixed display of undefined values in data across the app (in all tables, filter dialogs, and chips). Empty data is now represented by -
  • Fixed a bug in the filterText function that didn't allow searching by name with the new object structure (name can be a function instead of a string). The function now finds the row's value as other filtering functions do - with field.value(row) or row[field.value], which supports both strings and functions.
  • Fixed a styling bug that adjusted content when chips appeared. ChipGroup is now set at 40px
  • Changes in tests allow for us to change what the filter separator is without breaking everything. I also added a test in ChipGroup to test if changing chips that end with the filter separator add a - to properly display undefined.

Quick vid showing filtering and searching on the notifications page:

FilterUndefined.mov

@joshri joshri added the area/ui Issues that require front-end work label Sep 6, 2022
@joshri joshri requested review from ozamosi and opudrovs September 6, 2022 21:40
return f.sortValue(row) === value;
}
});
fields.forEach((field) => {
Copy link
Contributor

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 simplified further. Let's say that both name and namespace (in that order) is text searchable. If you search for 1 text filter that matches the name, then I think the namespace would set matches back to false. So I think the matches filter needs to be more carefully passed from "layer to layer", instead of just building up one value as we loop.

The text filters seem like they should be ANDed based on the tests, so I'm going to assume that's correct.

To make this work properly, I think we need to:

  • First (inner-most) OR each field together - any field that matches a single text filter means that the whole row matches that filter.
  • Then, AND the filters together.
  • And only then do we decide if the row as a whole matches the filters as a whole.

We're currently merging the filters before we're merging the columns, which I think doesn't work properly with multiple textSearchable columns - if again name and namespace are searchable, you wouldn't get a hit if one filter matches name and one filter matches namespace, only if all filters matches either name or namespace.

So I would change this whole loop to:

  return rows.filter((row) => {
    return textFilters.every((filter) => {
      return fields.some((field) => {
        if (!field.textSearchable) return false;

        let value;
        if (field.sortValue) {
          value = field.sortValue(row);
        } else if (typeof field.value === "function") {
          value = field.value(row);
        } else {
          value = row[field.value];
        }

        return value.includes(filter);
      });
    });
  });

Perhaps we could even turn that value extraction into some helper function.

I like chaining together every and some because they help separate "checking the condition" from "combining the outputs" so they take care of the matches malarkey for us - we only return a boolean, and the right thing comes out, and they even handles short-circuiting for us.

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 don't think it was ever intended to text search any column other than name - I took a look through and no tables have more than one column searchable. I'm down to add this because why not - but to me it'd be weird to have something be filterable AND text searchable.

Copy link
Contributor

Choose a reason for hiding this comment

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

Text search let you do substring matches, the filter UI doesn't - so you can't filter for "all namespaces with denim in the name" without going through them all.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should I add textSearchable on all Namespace and Cluster columns? Any other columns that might be useful?

<Chip label={chip} onDelete={() => onChipRemove([chip])} />
<Chip
label={
chip.slice(-filterSeparator.length) === `${filterSeparator}`
Copy link
Contributor

Choose a reason for hiding this comment

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

Either do something like chip.indexOf(filterSeparator) == chip.length - filterSeparator.length (I probably have an off-by-one in there, I haven't tried it) so it 100% only manipulates empty chip values, or just drop this.

Copy link
Contributor

Choose a reason for hiding this comment

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

@joshri why are you using a template literal

`${filterSeparator}`

here instead of just filterSeparator? Was the comparison not working?

@opudrovs
Copy link
Contributor

opudrovs commented Sep 8, 2022

How can the user determine which fields are searchable? I see the textSearchable prop, but only as a developer, is there a visual indication for the fields you can search? The first thing I tried searching was by channel and type, it didn't work. Then I read that search is enabled only for names.

@opudrovs
Copy link
Contributor

opudrovs commented Sep 8, 2022

If using the Chip component imported from Material-UI causes such problems, can we just create our own Chip component which has two props, label and value (or maybe not label to avoid confusion with the original Chip component), and combines them as the label of the original Chip component it renders with a filterSeparator const, like return <OriginalChipComponent label={${OurChipComponentLabelProp}${filterSeparator}${OurChipComponentValueProp}}>? So, we can always know that we are not messing up a real value.

@joshri
Copy link
Contributor Author

joshri commented Sep 12, 2022

Created some follow-up issues - for now I've removed the hack in ChipGroup and left the filterByText function as is

@opudrovs
Copy link
Contributor

opudrovs commented Sep 12, 2022

The ChipGroup tests are failing, including locally for me.

if (field.sortValue) {
value = field.sortValue(row);
} else {
typeof field.value === "function"
Copy link
Contributor

@opudrovs opudrovs Sep 12, 2022

Choose a reason for hiding this comment

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

Can we re-write this ternary as:

value = typeof field.value === "function"
    ? field.value(row)
    : row[field.value];

It's a standard form of an assignment with ternary instead of having multiple assignments.

for (const filterString of textFilters) {
if (_.includes(value, filterString)) {
for (let i = 0; i < textFilters.length; i++) {
if (value.includes(textFilters[i])) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This can be refactored as

matches = value.includes(textFilters[i]);

        if (!matches) {
          break;
        }

It would be easier to read.

@opudrovs
Copy link
Contributor

opudrovs commented Sep 12, 2022

Is Not Ready text in the status column supposed to wrap like this? Or do we need to make the status column wider?

Screenshot 2022-09-12 at 20 22 09

@opudrovs
Copy link
Contributor

Is it expected behavior that an empty filter dialog is displayed for the events table? I don't think it's specific to this PR.

I remember fixing sorting in the events table but don't remember working with filters in it. Is the events table supposed to have filters or should we add hiding the filter dialog button for it?

Screenshot 2022-09-12 at 20 36 38

Copy link
Contributor

@opudrovs opudrovs left a comment

Choose a reason for hiding this comment

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

Tested it, have not found a way to break anything! 🎉 Except for the failing test, LGTM.

@joshri
Copy link
Contributor Author

joshri commented Sep 12, 2022

@opudrovs

"Not Ready" wrapping - I don't believe it's intentional that it's wrapping, but I also don't see any limits on the width of the status column

Events Table empty filters - we aren't providing a filter state to the table so it makes sense that it's empty. We should probably disable search and filtering on this table with a boolean prop on DataTable

@joshri
Copy link
Contributor Author

joshri commented Sep 12, 2022

We decided to make the filter dialog optional on the data table since the events table has never had any filtering functionality

@joshri joshri merged commit 7b7ceff into main Sep 12, 2022
@joshri joshri deleted the filter-undefined branch September 12, 2022 20:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/ui Issues that require front-end work
Projects
None yet
3 participants