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

QueryList itemRenderer prop & renderItem method #1877

Merged
merged 18 commits into from
Jan 25, 2018

Conversation

giladgray
Copy link
Contributor

@giladgray giladgray commented Dec 9, 2017

Please read the new docs in select-component.md (including diffs in example code)!

Looking for feedback on these changes. I think this is mostly an improvement but I need validation. 😬
API breaks ahead and no new features (yet) but this refactor greatly reduces duplication and enables desirable improvements like disabled support (through modifiers).

Notable Changes

🤞 - denotes future / wishlist / proposals; cool stuff this feature enables down the road.

  • new ItemRenderer<T> type uses two arguments so the Props bag doesn't need to be generic. also allows for null return values. (eventually, 🤞 props bag can be stored in state and intelligently diffed, rather than reconstructed in every render.)

    export type ItemRenderer<T> = (item: T, itemProps: IItemRendererProps) => JSX.Element | null;
  • QueryList IItemModifiers for data about how to render. this sets us up for disabled support [Labs] MultiSelect needs a disabled prop #1834, and maybe even 🤞 user-defined modifiers a la react-day-picker or Popper.js!

    export interface IItemModifiers {
        /** Whether this is the "active" (focused) item, meaning keyboard interactions will act upon it. */
        active: boolean;
    
        /** Whether this item is disabled and should ignore interactions. */
        disabled: boolean;
    
        /** Whether this item matches the predicate. A typical renderer could hide `false` values. */
        matchesPredicate: boolean;
    }
  • move itemRenderer prop definition to QueryList itself (because every component redefined it exactly).

  • add renderItem method in IQueryListRendererProps that retrieves modifiers and delegates to itemRenderer prop. makes for much simpler item rendering!

  • refactor examples to share a common renderer and predicate implementation, and rename the data file to films.

@blueprint-bot
Copy link

fix the tests

Preview: documentation

@blueprint-bot
Copy link

Merge branch 'master' of github.com:palantir/blueprint into gg/querylist-modifiers

Preview: documentation

Copy link
Contributor

@adidahiya adidahiya left a comment

Choose a reason for hiding this comment

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

overall API breaks lgtm

) : (
undefined
);

return (
<FilmMultiSelect
{...Films}
Copy link
Contributor

Choose a reason for hiding this comment

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

this is pretty confusing / too clever. please refactor to export something like multiSelectProps from the "./films" module

Copy link
Contributor

Choose a reason for hiding this comment

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

then you also wouldn't have awkward looking code like type Film = Films.Film;

@@ -108,4 +113,27 @@ export const TOP_100_FILMS = [
{ title: "Monty Python and the Holy Grail", year: 1975 },
].map((m, index) => ({ ...m, rank: index + 1 }));

export type Film = typeof TOP_100_FILMS[0];
export type Film = typeof items[0];
Copy link
Contributor

Choose a reason for hiding this comment

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

would rather just make this an interface so its usage is more obvious in the other files.

interface IFilm {
    ...
}


// TODO: not happy with this name
/** Whether this item matches the predicate. A typical renderer could hide `false` values. */
filtered: boolean;
Copy link
Contributor

Choose a reason for hiding this comment

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

how about matchesFilter: boolean

Copy link
Contributor Author

@giladgray giladgray Dec 15, 2017

Choose a reason for hiding this comment

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

sure, but matchesPredicate would be more accurate (itemPredicate etc), and now we're getting verbose.

Copy link
Contributor

Choose a reason for hiding this comment

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

sure, yeah, makes sense

Copy link
Contributor

@hellochar hellochar left a comment

Choose a reason for hiding this comment

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

I like item totally separate from props in general

* An object describing how to render a particular item.
* An `itemRenderer` receives the item as its first argument, and this object as its second argument.
*/
export interface IItemRendererProps {
Copy link
Contributor

Choose a reason for hiding this comment

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

What's the benefit of splitting the props/modifier? Why is it no longer generic?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

it was only generic for the item: T field -- everything else has well-defined types.
also item is the only user-supplied field; the others are derived from other props and state. so now the derived fields are in a separate place and can be reused across renders, rather than being forced to create a new object just to change item.


// TODO: not happy with this name
/** Whether this item matches the predicate. A typical renderer could hide `false` values. */
filtered: boolean;
Copy link
Contributor

Choose a reason for hiding this comment

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

Specifically having a "filtered" prop is a little confusing; why is filtering special vs any other thing that users might want to do

Copy link
Contributor Author

Choose a reason for hiding this comment

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

filtering is one of QueryList's existential features: itemPredicate / itemListPredicate.
this API sets us up to support arbitrary user-defined modifiers.

@cmslewis cmslewis changed the title [Proposal] QueryList itemRenderer prop & renderItem method QueryList itemRenderer prop & renderItem method Dec 15, 2017
Copy link
Contributor

@themadcreator themadcreator left a comment

Choose a reason for hiding this comment

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

Naming comments

/** Top 100 films as rated by IMDb users. http://www.imdb.com/chart/top */
export const TOP_100_FILMS = [
export const items = [
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: why not UPPER_CASE?

item,
}),
);
private renderItems({ items, renderItem }: IQueryListRendererProps<T>) {
Copy link
Contributor

Choose a reason for hiding this comment

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

not sure where IQueryListRendererProps is defined, but i think this should be itemRenderer

/** Whether this item is disabled and should ignore interactions. */
disabled: boolean;

// TODO: not happy with this name
Copy link
Contributor

Choose a reason for hiding this comment

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

rename or not. remove comment.

* `QueryList` will retrieve modifiers for the item and delegate to `itemRenderer` prop for the actual rendering.
* The second parameter `index` is optional here; if provided, it will be passed through `itemRenderer` props.
*/
renderItem: (item: T, index?: number) => JSX.Element;
Copy link
Contributor

Choose a reason for hiding this comment

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

itemRenderer

Copy link
Contributor Author

Choose a reason for hiding this comment

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

nope, this is a method given to you, not a prop you define.

@@ -107,6 +108,13 @@ export interface IQueryListRendererProps<T> extends IProps {
*/
handleKeyUp: React.KeyboardEventHandler<HTMLElement>;
Copy link
Contributor

Choose a reason for hiding this comment

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

shouldn't this be onKeyUp for the prop? handle[Event] is for method names, right?

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 a method passed to the renderer. hard to tell without context above.

@adidahiya adidahiya changed the base branch from master to develop January 15, 2018 17:15
@blueprint-bot
Copy link

modifiers.filtered => modifiers.matchesPredicate

Preview: documentation | landing | table

@giladgray
Copy link
Contributor Author

@adidahiya this should be ready to go as well!

@@ -11,18 +11,16 @@ import * as React from "react";
import * as sinon from "sinon";

// this is an awkward import across the monorepo, but we'd rather not introduce a cyclical dependency or create another package
Copy link
Contributor

Choose a reason for hiding this comment

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

in a future PR, we should move this to packages/select/src/__fixtures__/. then you'd do the import from "@blueprintjs/select/src/__fixtures__/films"

btw we'd want to .npmignore __tests__ and __fixtures__

Copy link
Contributor Author

Choose a reason for hiding this comment

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

cool yes i like that

@giladgray giladgray merged commit 59830d5 into develop Jan 25, 2018
@giladgray giladgray deleted the gg/querylist-modifiers branch January 25, 2018 19:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants