-
Notifications
You must be signed in to change notification settings - Fork 204
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 user filtering to the Notebook #2835
Conversation
src/sidebar/components/hooks/test/use-user-filter-options-test.js
Outdated
Show resolved
Hide resolved
1580fca
to
4b6c298
Compare
@robertknight I've removed one commit from this PR—the "fix" to the |
Codecov Report
@@ Coverage Diff @@
## master #2835 +/- ##
==========================================
+ Coverage 97.72% 97.74% +0.01%
==========================================
Files 202 206 +4
Lines 7615 7670 +55
Branches 1683 1694 +11
==========================================
+ Hits 7442 7497 +55
Misses 173 173
Continue to review full report at Codecov.
|
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.
Overall this looks good and I was able to follow it quite easily. I had some requests for changes on various minor details and I also suggested slight name changes to some of the components to better fit the pattern of other components.
src/sidebar/components/hooks/test/use-user-filter-options-test.js
Outdated
Show resolved
Hide resolved
src/sidebar/components/hooks/test/use-user-filter-options-test.js
Outdated
Show resolved
Hide resolved
src/sidebar/components/hooks/test/use-user-filter-options-test.js
Outdated
Show resolved
Hide resolved
src/sidebar/components/hooks/test/use-user-filter-options-test.js
Outdated
Show resolved
Hide resolved
// If there is a filter conflict with focusFilters, deactivate focus | ||
// mode to prevent unintended collisions and let the new filter value | ||
// take precedence. | ||
if (getState().filters.focusFilters?.[filterName]) { |
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.
Since this logic only depends on state from the current module, it could potentially be handled in the reducer as well. In other words, the SET_FILTER
reducer would combine the results of the SET_FOCUS_MODE
reducer with whatever other changes it was going to make.
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.
For some reason I'm having trouble wrapping my head around what you're envisioning, would you mind elaborating a little bit?
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.
If you want one action to incorporate the effects of another, then one option is to use a thunk with dispatch
as you have done here. Another is to call one reducer from within another:
diff --git a/src/sidebar/store/modules/filters.js b/src/sidebar/store/modules/filters.js
index e36e7eff5..c47b6f5a8 100644
--- a/src/sidebar/store/modules/filters.js
+++ b/src/sidebar/store/modules/filters.js
@@ -119,6 +119,15 @@ const update = {
},
SET_FILTER: function (state, action) {
+ // If there is a filter conflict with focusFilters, deactivate focus
+ // mode to prevent unintended collisions and let the new filter value
+ // take precedence.
+ let newState = /** @type {typeof state} */ ({});
+ if (state.focusFilters?.[action.filterName]) {
+ // eslint-disable-next-line new-cap
+ newState = update.SET_FOCUS_MODE(state, { active: false });
+ }
+
const updatedFilters = {
...state.filters,
[action.filterName]: action.filterOption,
@@ -127,7 +136,9 @@ const update = {
if (action.filterOption?.value === '') {
delete updatedFilters[action.filterName];
}
- return { filters: updatedFilters };
+ newState.filters = updatedFilters;
+
+ return newState;
},
SET_FILTER_QUERY: function (state, action) {
@@ -170,22 +181,7 @@ function changeFocusModeUser(user) {
* @param {FilterOption} filterOption
*/
function setFilter(filterName, filterOption) {
- return (dispatch, getState) => {
- // If there is a filter conflict with focusFilters, deactivate focus
- // mode to prevent unintended collisions and let the new filter value
- // take precedence.
- if (getState().filters.focusFilters?.[filterName]) {
- dispatch({
- type: actions.SET_FOCUS_MODE,
- active: false,
- });
- }
- dispatch({
- type: actions.SET_FILTER,
- filterName,
- filterOption,
- });
- };
+ return { type: actions.SET_FILTER, filterName, filterOption };
}
/** Set the query used to filter displayed annotations. */
The above pattern has the advantage that the reducers get passed the local state so they don't have to extract it from the root state. I guess the downside is that it's just not a pattern that we currently use a lot.
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.
Admittedly did not know this was even possible, so this has value just as educational content :).
4b6c298
to
8f1f8ef
Compare
@robertknight Due to the number of modules that got renamed as a result of integrating feedback, etc, I ended up rewriting commit history entirely. I hope this doesn't slow you down. I addressed all of your feedback items except for one that I left unresolved that I had a confusion over. Thanks! |
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 almost ready to go. A few minor issues I highlighted:
- I suggest making the
value
property of user filter options always be usernames, to avoid needing to deal with a mix of account IDs and usernames. - There is a mismatch between CSS class names, .scss file name and Preact component name for one component
Once the above are addressed this is good to merge.
<span className="notebook-result-count"> | ||
{!hasResults && <h2>No results</h2>} | ||
{hasResults && hasAppliedFilter && ( | ||
<span> |
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.
If the <span>
element is not styled in any way then perhaps a Fragment
could be used instead.
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.
Can you help me understand the tradeoffs of using a <Fragment>
versus using a containing element?
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.
The main benefit is that it avoids adding clutter to the DOM tree that doesn't serve any purpose for semantic or styling purposes. It's only a small benefit in many cases though.
8f1f8ef
to
2aaef59
Compare
This is a
draftPR of all the bits and pieces needed to add rudimentary user filtering to the Notebook. What I'd like to ascertain with thisdraftPR is:A Few Screenshots
On launch (not in user-focus mode), Notebook looks like this:
When filtering by user, the count of matches changes to use a "results" unit:
"Force-expanding" a non-matching thread will update the results count similar to in the sidebar:
When user-focus mode is active (e.g. in grading mode), opening the Notebook will filter by the currently-focused user initially:
(but the user can change which user to filter by, or set it to "Everybody").
A List
You should expect these changes to accomplish the following:
client_display_names
feature flag is enabled, the user’s display names will display, and will be alphabetizedX threads (y annotations)
toX results
(or1 result
if singular)X results (and y more)
similarly to how the sidebar handles this stateWhat's not included
Literally everything else, including things like URL hash/history management, handling really long lists of users, handling empty result sets more gracefully, anything but the most elementary visual styling, etc., etc.
Edited: Moving out of draft now
Part of #2731