[react-aria-menubutton] add typings

This commit is contained in:
Chris Rohlfs
2017-08-25 13:24:18 +12:00
parent 6839a99461
commit d662c2f06a
4 changed files with 297 additions and 0 deletions

151
types/react-aria-menubutton/index.d.ts vendored Normal file
View File

@@ -0,0 +1,151 @@
// Type definitions for react-aria-menubutton 5.0
// Project: https://github.com/davidtheclark/react-aria-menubutton
// Definitions by: Muhammad Fawwaz Orabi <https://github.com/forabi>
// Chris Rohlfs <https://github.com/crohlfs>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.3
import * as React from "react";
export interface WrapperState {
isOpen: boolean;
}
export type WrapperProps<T extends HTMLElement> = React.HTMLAttributes<T> & {
/**
* A callback to run when the user makes a selection
* (i.e. clicks or presses Enter or Space on a `MenuItem`).
* It will be passed the value of the selected `MenuItem` and
* the React `SyntheticEvent`.
*/
onSelection(value: any, event: React.SyntheticEvent<T>): any;
/**
* A callback to run when the menu is opened or closed.
*/
onMenuToggle?(obj: WrapperState): any;
/**
* By default, it does automatically close.
* If false, the menu will not automatically close when a
* selection is made. Default: `true`.
*/
closeOnSelection?: boolean;
isOpen?: boolean;
tag?: T["tagName"];
};
/**
* A simple component to group a `Button`/`Menu`/`MenuItem` set,
* coordinating their interactions. It should wrap your entire menu button
* widget.
* All `Button`, `Menu`, and `MenuItem` components must be nested within a
* `Wrapper` component.
* Each wrapper can contain only one `Button`, only one `Menu`, and
* multiple `MenuItem`s.
*/
export class Wrapper extends React.Component<WrapperProps<HTMLElement>> {}
export type ButtonProps<T extends HTMLElement> = React.HTMLAttributes<T> & {
/**
* If true, the element is disabled
* (aria-disabled='true', not in tab order, clicking has no effect).
*/
disabled?: boolean;
/**
* The HTML tag for this element. Default: 'span'.
*/
tag?: T["tagName"];
};
/**
* A React component to wrap the content of your
* menu-button-pattern's button.
* The `Button` component itself acts as a UI button (with tab-index, role, etc.),
* so you probably do not want to pass an HTML `<button>` element as its child.
* Each `Button` must be wrapped in a Wrapper, and each Wrapper can wrap only
* one `Button`.
*/
export class Button extends React.Component<ButtonProps<HTMLElement>> {}
export type MenuProps<T extends HTMLElement> = React.HTMLAttributes<T> & {
/**
* The HTML tag for this element. Default: 'span'.
*/
tag?: T["tagName"];
};
/**
* A React component to wrap the content of your menu-button-pattern's menu.
*/
export class Menu extends React.Component<MenuProps<HTMLElement>> {}
export type MenuItemProps<T extends HTMLElement> = React.HTMLAttributes<T> & {
/**
* If value has a value, it will be passed to the onSelection handler
* when the `MenuItem` is selected
*/
value?: string | boolean | number;
/**
* If `text` has a value, its first letter will be the letter a user can
* type to navigate to that item.
*/
text?: string;
/**
* The HTML tag for this element. Default: 'span'.
*/
tag?: T["tagName"];
};
/**
* A React component to wrap the content of one of your
* menu-button-pattern's menu items.
* Each `MenuItem` must be wrapped in a `Wrapper`,
* and each Wrapper can wrap multiple `MenuItem`s.
* When a `MenuItem` is selected (by clicking or focusing and hitting Enter
* or Space), it calls the `onSelection` handler you passed ariaMenuButton
* when creating this set of components.
* It passes that handler a value and the event. The value it passes is
* determined as follows:
* * If the `MenuItem` has a `value` prop, that is passed.
* * If the `MenuItem` has no `value` prop, the component's child is passed
* (so it better be simple text!).
*/
export class MenuItem extends React.Component<MenuItemProps<HTMLElement>> {}
/**
* Open a modal programmatically.
* For this to work, you must provide an id prop to the Wrapper of the menu.
* That id should be your first argument to `openMenu()`.
*/
export function openMenu(
wrapperId: string,
openOptions?: {
/**
* If `true`, the menu's first item will receive focus when the
* menu opens. Default: `false`.
*/
focusMenu: boolean;
}
): void;
/**
* Close a modal programmatically.
* For this to work, you must provide an id prop to the Wrapper of the menu.
* That id should be your first argument to `closeMenu()`.
*/
export function closeMenu(
wrapperId: string,
closeOptions?: {
/**
* If `true`, the widget's button will receive focus when the
* menu closes. Default: `false`.
*/
focusMenu: boolean;
}
): void;

View File

@@ -0,0 +1,126 @@
import * as React from "react";
import * as ReactDOM from "react-dom";
import {
closeMenu,
openMenu,
Wrapper,
Button,
Menu,
MenuItem
} from "react-aria-menubutton";
const menuItemWords = ["foo", "bar", "baz"];
class MyMenuButton extends React.Component {
render() {
const menuItems = menuItemWords.map((word, i) => {
return (
<li key={i}>
<MenuItem className="MyMenuButton-menuItem">
{word}
</MenuItem>
</li>
);
});
return (
<Wrapper
className="MyMenuButton"
onSelection={(value, event) => console.log(value, event)}
>
<Button className="MyMenuButton-button">click me</Button>
<Menu className="MyMenuButton-menu">
<ul>
{menuItems}
</ul>
</Menu>
</Wrapper>
);
}
}
ReactDOM.render(<MyMenuButton />, document.body);
const words = [
"pectinate",
"borborygmus",
"anisodactylous",
"barbar",
"pilcrow",
"destroy"
];
interface DemoOneState {
selected: string;
noMenu: boolean;
}
class DemoOne extends React.Component<{}, DemoOneState> {
constructor(props: any) {
super(props);
this.state = { selected: "", noMenu: false };
}
handleSelection(value: string) {
if (value === "destroy") {
this.setState({ noMenu: true });
} else {
this.setState({ selected: value });
}
}
render() {
const { selected, noMenu } = this.state;
if (noMenu) {
return (
<div>
[You decided to "destroy this menu," so the menu has been destroyed,
according to your wishes. Refresh the page to see it again.]
</div>
);
}
const menuItemElements = words.map((word, i) => {
let itemClass = "AriaMenuButton-menuItem";
if (selected === word) {
itemClass += " is-selected";
}
const display = word === "destroy" ? "destroy this menu" : word;
return (
<li className="AriaMenuButton-menuItemWrapper" key={i}>
<MenuItem className={itemClass} value={word} text={word}>
{display}
</MenuItem>
</li>
);
});
return (
<div>
<Wrapper
className="AriaMenuButton"
onSelection={this.handleSelection.bind(this)}
>
<Button className="AriaMenuButton-trigger">Select a word</Button>
<Menu>
<ul className="AriaMenuButton-menu">
{menuItemElements}
</ul>
</Menu>
</Wrapper>
<span style={{ marginLeft: "1em" }}>
Your last selection was: <strong>{selected}</strong>
</span>
</div>
);
}
}
ReactDOM.render(<DemoOne />, document.getElementById("demo-one"));
closeMenu("");
closeMenu("", { focusMenu: true });
openMenu("");
openMenu("", { focusMenu: true });

View File

@@ -0,0 +1,19 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": ["es6", "dom"],
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"baseUrl": "../",
"typeRoots": ["../"],
"types": [],
"jsx": "react",
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.d.ts",
"react-aria-menubutton-tests.tsx"
]
}

View File

@@ -0,0 +1 @@
{ "extends": "dtslint/dt.json" }