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

[core,select] chore: deprecate .ofType() helper methods #5638

Merged
merged 1 commit into from
Oct 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions packages/core/src/components/tree/tree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,8 @@ export class Tree<T = {}> extends React.Component<TreeProps<T>> {

const nodeItems = treeNodes.map((node, i) => {
const elementPath = currentPath!.concat(i);
const TypedTreeNode = TreeNode.ofType<T>();
return (
<TypedTreeNode
<TreeNode<T>
{...node}
key={node.id}
contentRef={this.handleContentRef}
Expand All @@ -143,7 +142,7 @@ export class Tree<T = {}> extends React.Component<TreeProps<T>> {
path={elementPath}
>
{this.renderNodes(node.childNodes, elementPath)}
</TypedTreeNode>
</TreeNode>
);
});

Expand Down
1 change: 1 addition & 0 deletions packages/core/src/components/tree/treeNode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ export interface ITreeNodeProps<T = {}> extends TreeNodeInfo<T> {
export class TreeNode<T = {}> extends React.Component<ITreeNodeProps<T>> {
public static displayName = `${DISPLAYNAME_PREFIX}.TreeNode`;

/** @deprecated no longer necessary now that the TypeScript parser supports type arguments on JSX element tags */
public static ofType<U>() {
return TreeNode as new (props: ITreeNodeProps<U>) => TreeNode<U>;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,6 @@ export interface TimezoneSelectState {
query: string;
}

const TypedSelect = Select2.ofType<TimezoneWithNames>();

export class TimezoneSelect extends AbstractPureComponent2<TimezoneSelectProps, TimezoneSelectState> {
public static displayName = `${DISPLAYNAME_PREFIX}.TimezoneSelect`;

Expand Down Expand Up @@ -150,7 +148,7 @@ export class TimezoneSelect extends AbstractPureComponent2<TimezoneSelectProps,
const { query } = this.state;

return (
<TypedSelect
<Select2<TimezoneWithNames>
className={classNames(Classes.TIMEZONE_SELECT, className)}
disabled={disabled}
fill={fill}
Expand All @@ -172,7 +170,7 @@ export class TimezoneSelect extends AbstractPureComponent2<TimezoneSelectProps,
resetOnSelect={true}
>
{children ?? this.renderButton()}
</TypedSelect>
</Select2>
);
}

Expand Down
4 changes: 2 additions & 2 deletions packages/datetime2/test/components/timezoneSelectTests.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -169,11 +169,11 @@ describe("<TimezoneSelect>", () => {
}

function findSelect(timezoneSelect: ReactWrapper<TimezoneSelect>) {
return timezoneSelect.find(Select2.ofType<TimezoneWithNames>());
return timezoneSelect.find<Select2<TimezoneWithNames>>(Select2);
}

function findQueryList(timezoneSelect: ReactWrapper<TimezoneSelect>) {
return findSelect(timezoneSelect).find(QueryList.ofType<TimezoneWithNames>());
return findSelect(timezoneSelect).find<QueryList<TimezoneWithNames>>(QueryList);
}

function findPopover(timezoneSelect: ReactWrapper<TimezoneSelect>) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,13 @@ export interface IIconSelectProps {
onChange: (iconName?: IconName) => void;
}

const TypedSelect = Select2.ofType<IconNameOrNone>();

export class IconSelect extends React.PureComponent<IIconSelectProps> {
public render() {
const { disabled, iconName } = this.props;
return (
<label className={classNames(Classes.LABEL, { [Classes.DISABLED]: disabled })}>
Icon
<TypedSelect
<Select2<IconNameOrNone>
disabled={disabled}
items={ICON_NAMES}
itemPredicate={this.filterIconName}
Expand All @@ -57,7 +55,7 @@ export class IconSelect extends React.PureComponent<IIconSelectProps> {
text={iconName || NONE}
rightIcon="caret-down"
/>
</TypedSelect>
</Select2>
</label>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@ import {

import { PropCodeTooltip } from "../../common/propCodeTooltip";

const FilmMultiSelect = MultiSelect2.ofType<Film>();

const INTENTS = [Intent.NONE, Intent.PRIMARY, Intent.SUCCESS, Intent.DANGER, Intent.WARNING];

export interface IMultiSelectExampleState {
Expand Down Expand Up @@ -113,7 +111,7 @@ export class MultiSelectExample extends React.PureComponent<ExampleProps, IMulti

return (
<Example options={this.renderOptions()} {...this.props}>
<FilmMultiSelect
<MultiSelect2<Film>
{...flags}
createNewItemFromQuery={allowCreate ? createFilms : undefined}
createNewItemRenderer={allowCreate ? renderCreateFilmsMenuItem : null}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ import {
TOP_100_FILMS,
} from "@blueprintjs/select/examples";

const FilmOmnibar = Omnibar.ofType<Film>();

export interface IOmnibarExampleState {
allowCreate: boolean;
isOpen: boolean;
Expand Down Expand Up @@ -78,7 +76,7 @@ export class OmnibarExample extends React.PureComponent<ExampleProps, IOmnibarEx
<KeyCombo combo="shift + o" />
</span>

<FilmOmnibar
<Omnibar<Film>
{...this.state}
createNewItemFromQuery={maybeCreateNewItemFromQuery}
createNewItemRenderer={maybeCreateNewItemRenderer}
Expand Down
3 changes: 1 addition & 2 deletions packages/docs-theme/src/components/navigator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ export interface INavigationSection {
title: string;
}

const NavOmnibar = Omnibar.ofType<INavigationSection>();
export class Navigator extends React.PureComponent<INavigatorProps> {
private sections: INavigationSection[];

Expand All @@ -68,7 +67,7 @@ export class Navigator extends React.PureComponent<INavigatorProps> {
return null;
}
return (
<NavOmnibar
<Omnibar<INavigationSection>
className="docs-navigator-menu"
inputProps={{ placeholder: "Search documentation pages and sections..." }}
itemListPredicate={this.filterMatches}
Expand Down
4 changes: 1 addition & 3 deletions packages/select/src/components/multi-select/multiSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,6 @@ export class MultiSelect<T> extends AbstractPureComponent2<MultiSelectProps<T>,
isOpen: (this.props.popoverProps && this.props.popoverProps.isOpen) || false,
};

private TypedQueryList = QueryList.ofType<T>();

public input: HTMLInputElement | null = null;

public queryList: QueryList<T> | null = null;
Expand All @@ -147,7 +145,7 @@ export class MultiSelect<T> extends AbstractPureComponent2<MultiSelectProps<T>,
const { openOnKeyDown, popoverProps, tagInputProps, ...restProps } = this.props;

return (
<this.TypedQueryList
<QueryList<T>
{...restProps}
onItemSelect={this.handleItemSelect}
onQueryChange={this.handleQueryChange}
Expand Down
5 changes: 2 additions & 3 deletions packages/select/src/components/multi-select/multiSelect2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ export class MultiSelect2<T> extends AbstractPureComponent2<MultiSelect2Props<T>
placeholder: "Search...",
};

/** @deprecated no longer necessary now that the TypeScript parser supports type arguments on JSX element tags */
public static ofType<U>() {
return MultiSelect2 as new (props: MultiSelect2Props<U>) => MultiSelect2<U>;
}
Expand All @@ -139,8 +140,6 @@ export class MultiSelect2<T> extends AbstractPureComponent2<MultiSelect2Props<T>
isOpen: (this.props.popoverProps && this.props.popoverProps.isOpen) || false,
};

private TypedQueryList = QueryList.ofType<T>();

public input: HTMLInputElement | null = null;

public queryList: QueryList<T> | null = null;
Expand Down Expand Up @@ -174,7 +173,7 @@ export class MultiSelect2<T> extends AbstractPureComponent2<MultiSelect2Props<T>
const { menuProps, openOnKeyDown, popoverProps, tagInputProps, ...restProps } = this.props;

return (
<this.TypedQueryList
<QueryList<T>
{...restProps}
menuProps={{
"aria-label": "selectable options",
Expand Down
4 changes: 1 addition & 3 deletions packages/select/src/components/omnibar/omnibar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,12 @@ export class Omnibar<T> extends React.PureComponent<OmnibarProps<T>> {
return Omnibar as new (props: OmnibarProps<U>) => Omnibar<U>;
}

private TypedQueryList = QueryList.ofType<T>();

public render() {
// omit props specific to this component, spread the rest.
const { isOpen, inputProps, overlayProps, ...restProps } = this.props;
const initialContent = "initialContent" in this.props ? this.props.initialContent : null;

return <this.TypedQueryList {...restProps} initialContent={initialContent} renderer={this.renderQueryList} />;
return <QueryList<T> {...restProps} initialContent={initialContent} renderer={this.renderQueryList} />;
}

private renderQueryList = (listProps: QueryListRendererProps<T>) => {
Expand Down
2 changes: 1 addition & 1 deletion packages/select/src/components/query-list/query-list.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

`QueryList<T>` is a higher-order component that provides interactions between a query string and a list of items. Specifically, it implements the two predicate props described above and provides keyboard selection. It does not render anything on its own, instead deferring to a `renderer` prop to perform the actual composition of components.

`QueryList<T>` is a generic component where `<T>` represents the type of one item in the array of `items`. The static method `QueryList.ofType<T>()` is available to simplify the TypeScript usage.
`QueryList<T>` is a generic component where `<T>` represents the type of one item in the array of `items`.

If the `Select` interactions are not sufficient for your use case, you can use `QueryList` directly to render your own components while leveraging basic interactions for keyboard selection and filtering. The `Select` source code is a great place to start when implementing a custom `QueryList` `renderer`.

Expand Down
1 change: 1 addition & 0 deletions packages/select/src/components/query-list/queryList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ export class QueryList<T> extends AbstractComponent2<QueryListProps<T>, IQueryLi
resetOnQuery: true,
};

/** @deprecated no longer necessary now that the TypeScript parser supports type arguments on JSX element tags */
public static ofType<U>() {
return QueryList as new (props: QueryListProps<U>) => QueryList<U>;
}
Expand Down
4 changes: 1 addition & 3 deletions packages/select/src/components/select/select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,6 @@ export class Select<T> extends AbstractPureComponent2<SelectProps<T>, ISelectSta

public state: ISelectState = { isOpen: false };

private TypedQueryList = QueryList.ofType<T>();

public inputElement: HTMLInputElement | null = null;

private queryList: QueryList<T> | null = null;
Expand All @@ -135,7 +133,7 @@ export class Select<T> extends AbstractPureComponent2<SelectProps<T>, ISelectSta
const { filterable, inputProps, popoverProps, ...restProps } = this.props;

return (
<this.TypedQueryList
<QueryList<T>
{...restProps}
onItemSelect={this.handleItemSelect}
ref={this.handleQueryListRef}
Expand Down
77 changes: 41 additions & 36 deletions packages/select/src/components/select/select2.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ const FilmSelect: React.FC = () => {
ReactDOM.render(<FilmSelect /> document.querySelector("#root"));
```

In TypeScript, `Select2<T>` is a _generic component_ so you must define a local type that specifies `<T>`, the type of one item in `items`. The props on this local type will now operate on your data type (speak your language) so you can easily define handlers without transformation steps, but most props are required as a result. The static `Select2.ofType<T>()` method is available to streamline this process. (Note that this has no effect on JavaScript usage: the `Select2` export is a perfectly valid React component class.)
In TypeScript, `Select2<T>` is a _generic component_ so you must define a local type that specifies `<T>`, the type of one item in `items`. The props on this local type will now operate on your data type so you can easily define handlers without transformation steps, but most props are required as a result.

@## Querying

Expand All @@ -112,13 +112,11 @@ Since Select2 accepts arbitrary children, disabling a Select2 componet requires
_and also_ disabling its children. For example:

```tsx
const FilmSelect = Select2.ofType<Films.Film>();

// many props omitted here for brevity
return (
<FilmSelect disabled={true}>
const FilmSelect: React.FC = () => (
// many props omitted here for brevity
<Select2<Film> disabled={true}>
<Button disabled={true}>
</FilmSelect>
</Select2>
);
```

Expand All @@ -137,17 +135,19 @@ The input value can be controlled with the `query` and `onQueryChange` props. _D
The focused item (for keyboard interactions) can be controlled with the `activeItem` and `onActiveItemChange` props.

```tsx
<FilmSelect
items={myFilter(ALL_ITEMS, this.state.myQuery)}
itemRenderer={...}
onItemSelect={...}
// controlled active item
activeItem={this.state.myActiveItem}
onActiveItemChange={this.handleActiveItemChange}
// controlled query
query={this.state.myQuery}
onQueryChange={this.handleQueryChange}
/>
const FilmSelect: React.FC = () => (
<Select2<Film>
items={myFilter(ALL_ITEMS, this.state.myQuery)}
itemRenderer={...}
onItemSelect={...}
// controlled active item
activeItem={this.state.myActiveItem}
onActiveItemChange={this.handleActiveItemChange}
// controlled query
query={this.state.myQuery}
onQueryChange={this.handleQueryChange}
/>
);
```

This controlled usage allows you to implement all sorts of advanced behavior on
Expand Down Expand Up @@ -207,17 +207,16 @@ function renderCreateFilmOption(
)
}

ReactDOM.render(
<FilmSelect
const FilmSelect: React.FC = () => (
<Select2<Film>
createNewItemFromQuery={createFilm}
createNewItemRenderer={renderCreateFilmOption}
items={Films.items}
itemPredicate={Films.itemPredicate}
itemRenderer={Films.itemRenderer}
noResults={<MenuItem disabled={true} text="No results." roleStructure="listoption" />}
onItemSelect={...}
/>,
document.querySelector("#root")
/>
);
```

Expand Down Expand Up @@ -261,15 +260,14 @@ function getActiveItem() {
return isCreateNewItemActive ? getCreateNewItem() : currentActiveItem;
}

ReactDOM.render(
<FilmSelect
const FilmSelect: React.FC = () => (
<Select2<Film>
{...} // Other required props (see previous examples).
activeItem={getActiveItem()}
createNewItemFromQuery={...}
createNewItemRenderer={...}
onActiveItemChange={handleActiveItemChange}
/>,
document.querySelector("#root")
/>
);
```

Expand All @@ -288,8 +286,6 @@ import { Classes } from "@blueprintjs/core";
import { MenuItem } from "@blueprintjs/popover2";
import { ItemRenderer, ItemPredicate, Select2 } from "@blueprintjs/select";

const FilmSelect = Select2.ofType<Film>();

const filterFilm: ItemPredicate<Film> = (query, film) => {
return film.title.toLowerCase().indexOf(query.toLowerCase()) >= 0;
};
Expand All @@ -311,7 +307,14 @@ const renderFilm: ItemRenderer<Film> = (film, { handleClick, handleFocus, modifi
);
};

<FilmSelect itemPredicate={filterFilm} itemRenderer={renderFilm} items={...} onItemSelect={...} />
const FilmSelect: React.FC = () => (
<Select2<Film>
itemPredicate={filterFilm}
itemRenderer={renderFilm}
items={...}
onItemSelect={...}
/>
);
```

@interface ItemRendererProps
Expand All @@ -337,13 +340,15 @@ const renderMenu: ItemListRenderer<Film> = ({ items, itemsParentRef, query, rend
);
};

<FilmSelect
itemListRenderer={renderMenu}
itemPredicate={filterFilm}
itemRenderer={renderFilm}
items={...}
onItemSelect={...}
/>
const FilmSelect: React.FC = () => (
<Select2<Film>
itemListRenderer={renderMenu}
itemPredicate={filterFilm}
itemRenderer={renderFilm}
items={...}
onItemSelect={...}
/>
);
```

@interface ItemListRendererProps
Loading