Skip to content

Table multiselect filters #823

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

Merged
merged 9 commits into from
May 7, 2021
Merged

Table multiselect filters #823

merged 9 commits into from
May 7, 2021

Conversation

jake-bassett
Copy link
Contributor

Description

This change is to make all of the table control dropdowns multiselect instead of single select.

It also cleans up some messiness around the table control filter types. Using multi-select is actually easier to configure because it no-longer requires an Unset option in the dropdown. This simplifies the API for the select dropdowns and reduces its possible filter types to just TableControlOptionType.Filter. Now only checkbox table controls use TableControlOptionType.Unset and TableControlOptionType.Property types.

Screen.Recording.2021-05-05.at.1.52.04.PM.mov

@jake-bassett jake-bassett requested a review from a team as a code owner May 5, 2021 20:57
@codecov
Copy link

codecov bot commented May 5, 2021

Codecov Report

Merging #823 (d352aa3) into main (73bb82f) will decrease coverage by 0.01%.
The diff coverage is 25.80%.

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #823      +/-   ##
==========================================
- Coverage   85.44%   85.42%   -0.02%     
==========================================
  Files         792      793       +1     
  Lines       16222    16230       +8     
  Branches     1930     1930              
==========================================
+ Hits        13861    13865       +4     
- Misses       2329     2333       +4     
  Partials       32       32              
Impacted Files Coverage Δ
...rc/pages/apis/endpoints/endpoint-list.dashboard.ts 100.00% <ø> (ø)
...d/widgets/table/table-widget-renderer.component.ts 49.27% <7.69%> (+4.00%) ⬆️
...hboard/widgets/table/table-widget-control.model.ts 40.90% <11.11%> (ø)
...omponents/src/table/controls/table-controls-api.ts 16.66% <16.66%> (ø)
.../table/table-widget-control-select-option.model.ts 38.46% <27.27%> (-61.54%) ⬇️
...able/table-widget-control-checkbox-option.model.ts 37.50% <28.57%> (-0.97%) ⬇️
...te/entities-attribute-options-data-source.model.ts 57.14% <33.33%> (+1.58%) ⬆️
projects/common/src/utilities/lang/lang-utils.ts 89.47% <50.00%> (-4.65%) ⬇️
...nts/src/table/controls/table-controls.component.ts 76.11% <50.00%> (ø)
...graphql-table-control-options-data-source.model.ts 100.00% <100.00%> (ø)
... and 1 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 73bb82f...d352aa3. Read the comment docs.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.


export const toInFilter = (tableFilters: TableFilter[]): TableFilter =>
tableFilters.reduce((previousValue, currentValue) => ({
field: previousValue.field,
Copy link
Contributor

@anandtiwary anandtiwary May 5, 2021

Choose a reason for hiding this comment

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

what if the fields in the tableFilter are different?. In this reduce, we are just considering the last field as field for the final In-filter.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, this is my issue with this too.

We could validate here or I can break up the args to ensure we only are passed one field and operator, but I felt it was overkill and traded a little bit of confusion (and a potential for a bug if used incorrectly) for ease of use.

I can add some validation easy enough, so I will. I can revisit other options if we feel strongly.


interface TableControlOptionBase<T> {
value?: T;
export interface SelectControl {
Copy link
Contributor

Choose a reason for hiding this comment

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

This should be prefixed with 'Table'

options: SelectOption<TableControlOption>[];
export const enum TableControlOptionType {
Filter = 'filter',
Property = 'property',
Copy link
Contributor

Choose a reason for hiding this comment

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

What is a property control option? any visual example?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Please see this comment below.

export interface TableFilterControlOption<T = unknown> extends TableControlOptionBase<T> {
type: TableControlOptionType.Filter;
metaValue: TableFilter;
export type TableSelectControlOption = TableFilterControlOption;
Copy link
Contributor

Choose a reason for hiding this comment

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

do we really need so many different types? Like below where you specify metaValue type

export interface TableControlOption<T> {
  type: TableControlOptionType;
  label: string;
  metaValue: T;
}
}

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 add them just for fun! 😂

But valid question. I explain a little more in this comment.

export interface TableUnsetControlOption {
type: TableControlOptionType.Unset;
label: string;
metaValue: string;
Copy link
Contributor

Choose a reason for hiding this comment

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

what is meta value supposed to do? How is it different than 'value' ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Please see this comment below.

private isValidCheckboxControlOptions(options: TableControlOption[]): options is TableCheckboxOptions {
return (
options.length === 2 &&
options.every(option => hasOwnProperty(option, 'value') && typeof option.value === 'boolean')
Copy link
Contributor

Choose a reason for hiding this comment

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

I am yet to understand the inheritance in the table control options. value property seems to be defined at only one place. We can also use in here, i think

Copy link
Contributor Author

@jake-bassett jake-bassett May 6, 2021

Choose a reason for hiding this comment

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

Yeah, they are confusing. Fortunately it got a little bit cleaner with this PR.

Correct, that value is only for checkbox control now, since it has to hold a true or false while the select control doesn't. Checkbox controls are actually the oddball because they have to hold more ways of modifying a query.

Both select and checkbox controls have a metaValue. This is what contains the information about what to use in the query. It is most typically a Filter (see the metaValue for TableFilterControlOption), containing key, operator, and value. This is the metaValue that all select controls use now.

There are two other cases with query info, but they are now only used for checkbox controls. In addition to the Filter metaValue, there is an Unset and a Property metaValue (see the metaValue for TableUnsetControlOption and TablePropertyControlOption). These are also used to modify the query, but in a different way.

The Unset metaValue is used to hold a string that identifies which filter to remove from our filter list. The Property metaValue is a Dictionary used to add request properties to the query. Currently, our only example of this is includeInactive: true/false that we add to the request for APIs.

So, the reason that checkbox has a value and not the select dropdown is that checkbox has this additional user specified true/false in addition to the value that is buried in the metaValue for all controls.

Not sure if that explains it or makes it more confusing, but happy to expand on anything.

assertUnreachable(changed.value);
if (changed.values.length === 0) {
// tslint:disable-next-line: no-void-expression
return this.selectFilterSubject.next(this.removeFilters(changed.select.options[0].metaValue.field));
Copy link
Contributor

Choose a reason for hiding this comment

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

we don't need the return here right?

Copy link
Contributor Author

@jake-bassett jake-bassett May 6, 2021

Choose a reason for hiding this comment

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

Correct! Don't need to, but returning makes the code a little cleaner because we don't need an else block or a separate return; statement within the if block. Downside is I need the tslint disable. I could eliminate the return below, I suppose with the same results. It just looked odd.

Copy link
Contributor

Choose a reason for hiding this comment

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

I honestly find this more confusing. If we can use a separate return i think that's more clear.

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 the strangest thing to hold up a PR for.

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'll change it in my local.

Copy link
Contributor

Choose a reason for hiding this comment

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

It is a strong nit from my end

Copy link
Contributor

Choose a reason for hiding this comment

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

I was really curious what this was about when seeing the notifications. At first I was on team jake, as I prefer unwrapping an else if possible, but then I realized that we were returning a void expression and anand won the day. No strong preference between if/else and a separate return though.

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'm going to change it. Still silly to hold up a PR over this.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@jake-bassett jake-bassett requested review from anandtiwary and a team May 6, 2021 20:07
@github-actions

This comment has been minimized.

@jake-bassett jake-bassett merged commit f4b3436 into main May 7, 2021
@jake-bassett jake-bassett deleted the table-multiselect-filters branch May 7, 2021 14:34
@github-actions
Copy link

github-actions bot commented May 7, 2021

Unit Test Results

    4 files  ±0  250 suites  ±0   14m 10s ⏱️ -57s
899 tests ±0  899 ✔️ ±0  0 💤 ±0  0 ❌ ±0 
905 runs  ±0  905 ✔️ ±0  0 💤 ±0  0 ❌ ±0 

Results for commit f4b3436. ± Comparison against base commit 73bb82f.

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.

3 participants