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

feat: add load more pager to filters #3760

Merged
merged 11 commits into from
Jul 14, 2023
Merged

feat: add load more pager to filters #3760

merged 11 commits into from
Jul 14, 2023

Conversation

GZolla
Copy link
Contributor

@GZolla GZolla commented Jun 20, 2023

Adding load more pager to filters. Added the pager-has-more property to d2l-filter-dimension-set that when set will render a d2l-pager-load-more button (rendering done in d2l-filter).
The handling of pagination and adding/removing the property from the dimension is still the responsibility of the consumer (see the added demo as an example).

Rally: US153373

@github-actions
Copy link
Contributor

Thanks for the PR! 🎉

We've deployed an automatic preview for this PR - you can see your changes here:

URL https://pr-3760-brightspace-ui-core.d2l.dev/

Note
The build needs to finish before your changes are deployed.
Changes to the PR will automatically update the instance.

@GZolla GZolla marked this pull request as ready for review June 20, 2023 16:52
@GZolla GZolla requested a review from a team as a code owner June 20, 2023 16:52
const dimensionKey = e.target.parentNode.id.slice(SET_DIMENSION_ID_PREFIX.length);

/** @ignore */
this.dispatchEvent(new CustomEvent('d2l-filter-dimension-load-more', {
Copy link
Contributor

@dbatiste dbatiste Jun 21, 2023

Choose a reason for hiding this comment

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

Would a consumer ever try to do this?

<d2l-filter-dimension-set @d2l-filter-dimension-load-more=${...}>...</d2l-filter-dimension-set>

Are we sure the <filter> is the correct element to dispatch the event?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Consumers would need to check for the key in the event. I replicated the behavior for the d2l-filter-dimension-empty-state-action, since rendering happens on filter it is also responsible for dispatching events.

Copy link
Contributor

Choose a reason for hiding this comment

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

Why are we /** @ignore */-ing the event?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah having the filter dispatch all the events is a side-effect of our choice to have filter render everything, which we recently met about and decided not to change, even with the limitations. I think we could technically dispatch the event off the corresponding data-only dimension component, but like @GZolla mentioned the other dimension-specific events currently don't work this way. I'm also not sure if the analyzer for the Daylight site would understand where to put the event in that case?


return html`<d2l-filter-dimension-set key="${dimension.key}" text="${dimension.text}" ?pager-has-more="${loadedItems.length < items.length}">
${repeat(loadedItems, item => item.key, item => html`
<d2l-filter-dimension-set-value key="${item.key}" text="${item.text}"></d2l-filter-dimension-set-value>
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like loading more items unselects everything, which I suspect is just be a demo problem. I think you'd need to handle tracking and setting the selected state like this demo does. Which I think is worth it, to give people a realistic 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.

The last commit modifies the demo to handle this

components/filter/filter.js Show resolved Hide resolved
const dimensionKey = e.target.parentNode.id.slice(SET_DIMENSION_ID_PREFIX.length);

/** @ignore */
this.dispatchEvent(new CustomEvent('d2l-filter-dimension-load-more', {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why are we /** @ignore */-ing the event?

Copy link
Contributor

Choose a reason for hiding this comment

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

So this is probably a @geurts question, but one thing this doesn't handle at all is selected items that haven't been loaded. If the filter is pre-populated with some selected things (like in Quick Eval where filters are re-applied when hitting the back button), and those items aren't included in the initial load, all the counts we show will be wrong because we expect to have all options.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The demo now uses the same logic we use for search: we add all items but hide them and then show only those that are "loaded". This means the count will be correct even when some values are hidden by the search or the load more.

Copy link
Contributor

Choose a reason for hiding this comment

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

Also, if a consumer is using load more they'll have to be using manual search, we don't have all the values to search for them. Should we enforce that somehow, like ignore the search value set and pass manual if has-more is set? Maybe another @geurts question

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 not sure what the best approach is here, but looking at other components we could just leave it as is. If manual search is not set the load more will work but the search would just filter the added items which sort of breaks its use. To me this is similar to failing to add keys to list items, we don't generate a key but just let the component break, meaning that the consumer is solely responsible for properly setting up the component.

components/filter/filter.js Show resolved Hide resolved
@github-actions
Copy link
Contributor

Visual diff tests failed - pull request #3788 has been opened with the updated goldens.

github-actions bot and others added 3 commits June 27, 2023 16:09
- Remove /** @ignore */ from load more event and add documentation
- Fix demo resetting selection
- Modify the complete callback to take the keys to be loaded
* Wether the dimension has more values to load
* @type {boolean}
*/
pagerHasMore: { type: Boolean, attribute: 'pager-has-more', reflect: true },
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
pagerHasMore: { type: Boolean, attribute: 'pager-has-more', reflect: true },
hasMore: { type: Boolean, attribute: 'has-more', reflect: true },

I wonder if just has-more would be better here, since a consumer doesn't really know we're using pager under the hood

Copy link
Contributor

Choose a reason for hiding this comment

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

I still like this being named has-more to avoid revealing implementation details.

{ key: 'chemistry', selected:false, text: 'Chemistry' },
{ key: 'drama', selected:false, text: 'Drama' },
{ key: 'english', selected:false, text: 'English' },
{ key: 'how-t selected:false,o', text: 'How To Write a How To Article With a Flashy Title' },
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
{ key: 'how-t selected:false,o', text: 'How To Write a How To Article With a Flashy Title' },
{ key: 'how-to', selected:false, text: 'How To Write a How To Article With a Flashy Title' },

components/filter/filter.js Outdated Show resolved Hide resolved
components/filter/demo/filter-load-more-demo.js Outdated Show resolved Hide resolved
* Wether the dimension has more values to load
* @type {boolean}
*/
pagerHasMore: { type: Boolean, attribute: 'pager-has-more', reflect: true },
Copy link
Contributor

Choose a reason for hiding this comment

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

I still like this being named has-more to avoid revealing implementation details.

components/filter/filter.js Show resolved Hide resolved
components/filter/filter.js Outdated Show resolved Hide resolved
components/filter/filter.js Outdated Show resolved Hide resolved
@@ -45,7 +50,7 @@ class FilterDimensionSet extends LitElement {
* Whether to render the selected items at the top of the filter
* @type {boolean}
*/
selectedFirst: { type: Boolean, attribute: 'selected-first' },
selectedFirst: { type: Boolean, attribute: 'selected-first', reflect : true },
Copy link
Contributor

Choose a reason for hiding this comment

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

Is the reflect needed to work, or is it just to tell the consumer we changed the value on them?

Comment on lines +141 to +149
willUpdate(changedProperties) {
if (changedProperties.has('hasMore') && this.hasMore) {
if (this.searchType !== 'manual') {
console.warn('Paging requires search type set to manual.');
this.hasMore = false;
}
else this.selectedFirst = true;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Interested in some other opinions here. Currently, if you've set has-more but not search-type="manual", we turn off has-more and don't render the pager. We can't just turn on manual search for them, because they won't have events setup to handle it. Then, if they have correctly configured search, we override their setting of selected-first because we also require that to be on (and we can force that on for them, there's nothing they need to do).

This fails pretty gracefully, but just wondering if we should actually be more destructive to make it obvious things are misconfiguring? Should we log a console error in one or both scenarios, and/or be less graceful and more obviously broken?

I'm good with this, but wanted to check since we've used a few different strategies across core before

Comment on lines 630 to 632
if (dimension.selectedFirst) {
this._updateDimensionShouldBubble(dimension);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Are we now running this twice for the search scenario (once here, and then again above)?

If so, could we move it into _performDimensionSearch? Seems like that hits all paths - automatic search, manual search, and search after load more?

this.dispatchEvent(new CustomEvent('d2l-filter-dimension-load-more', {
detail: {
key: dimensionKey,
value: dimension.searchValue,
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe searchValue to make it clear, or currentlyAppliedSearch?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My thought process was to have the same attribute names between this and the manual search event. Since we also decided to document those together, I think it makes it more intuitive to the consumer.

Copy link
Contributor

Choose a reason for hiding this comment

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

Do you know why this visual diff is here? I wonder if merging main in would remove this, doesn't seem related to your changes

Copy link
Contributor

@svanherk svanherk left a comment

Choose a reason for hiding this comment

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

Just realized we should probably add a visual-diff test that shows the load-more pager, and has a couple values selected-first but selected-first isn't applied. But I'm fine with that being a follow-up PR!

@@ -31,6 +31,11 @@ class FilterDimensionSet extends LitElement {
* @type {boolean}
*/
loading: { type: Boolean },
/**
* Wether the dimension has more values to load
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
* Wether the dimension has more values to load
* Whether the dimension has more values to load. Must be used with selected-first and search-type="manual"

Could also be done as part of the documentation story if you're trying to get this merged before branching!

@svanherk
Copy link
Contributor

svanherk commented Jul 14, 2023

Also, just realized the focus is being lost after hitting "Load More". @dbatiste, is this something the list is supposed to handle automatically, or the consumer has to?

@dbatiste
Copy link
Contributor

Also, just realized the focus is being lost after hitting "Load More". @dbatiste, is this something the list is supposed to handle automatically, or the consumer has to?

I don't think focus should be lost. There were some previous changes that would disable the button while loading, and that did cause focus to be lost, but that was removed a long time ago. Focus should stay on the button while loading, and when the consumer calls e.detail.complete() we focus the first newly loaded item (based on index). We do ignore subsequent clicks while loading.

@GZolla GZolla merged commit efc6310 into main Jul 14, 2023
@GZolla GZolla deleted the gzolla/filter-load-more branch July 14, 2023 20:04
@ghost
Copy link

ghost commented Jul 14, 2023

🎉 This PR is included in version 2.134.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

@ghost ghost added the released label Jul 14, 2023
@svanherk
Copy link
Contributor

Also, just realized the focus is being lost after hitting "Load More". @dbatiste, is this something the list is supposed to handle automatically, or the consumer has to?

I don't think focus should be lost. There were some previous changes that would disable the button while loading, and that did cause focus to be lost, but that was removed a long time ago. Focus should stay on the button while loading, and when the consumer calls e.detail.complete() we focus the first newly loaded item (based on index). We do ignore subsequent clicks while loading.

@GZolla Is there a defect or something to track this? Just don't want to forget about it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants