diff --git a/packages/react-core/src/components/Menu/examples/Menu.md b/packages/react-core/src/components/Menu/examples/Menu.md
index 31b87730634..de09eeb021e 100644
--- a/packages/react-core/src/components/Menu/examples/Menu.md
+++ b/packages/react-core/src/components/Menu/examples/Menu.md
@@ -20,112 +20,12 @@ import AngleLeftIcon from '@patternfly/react-icons/dist/esm/icons/angle-left-ico
### Basic
-```js
-import React from 'react';
-import { Menu, MenuContent, MenuList, MenuItem, Checkbox } from '@patternfly/react-core';
-
-class MenuBasicList extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- activeItem: 0,
- isPlain: false
- };
- this.onSelect = (event, itemId) => {
- console.log(`clicked ${itemId}`);
- this.setState({
- activeItem: itemId
- });
- };
- this.togglePlain = checked => {
- this.setState({
- isPlain: checked
- });
- };
- }
-
- render() {
- const { activeItem, isPlain } = this.state;
- return (
-
-
-
-
-
-
- );
- }
-}
+```ts file="MenuBasic.tsx"
```
### With icons
-```js
-import React from 'react';
-import { Menu, MenuContent, MenuList, MenuItem } from '@patternfly/react-core';
-import CodeBranchIcon from '@patternfly/react-icons/dist/esm/icons/code-branch-icon';
-import LayerGroupIcon from '@patternfly/react-icons/dist/esm/icons/layer-group-icon';
-import CubeIcon from '@patternfly/react-icons/dist/esm/icons/cube-icon';
-
-class MenuIconsList extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- activeItem: 0
- };
- this.onSelect = (event, itemId) => {
- this.setState({
- activeItem: itemId
- });
- };
- }
-
- render() {
- const { activeItem } = this.state;
- return (
-
- );
- }
-}
+```ts file="MenuWithIcons.tsx"
```
### With checkbox
@@ -135,1258 +35,87 @@ class MenuIconsList extends React.Component {
### Filtering with text input
-```js
-import React from 'react';
-import { Menu, MenuList, MenuItem, MenuContent, MenuInput, TextInput, Divider } from '@patternfly/react-core';
-
-class MenuWithFiltering extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- activeItem: 0,
- input: ''
- };
-
- this.onSelect = (event, itemId) => {
- this.setState({
- activeItem: itemId
- });
- };
-
- this.handleTextInputChange = (value, field) => {
- this.setState({ [field]: value });
- };
- }
-
- render() {
- const { activeItem, input } = this.state;
- const menuListItemsText = ['Action 1', 'Action 2', 'Action 3'];
-
- const menuListItems = menuListItemsText
- .filter(item => !input || item.toLowerCase().includes(input.toString().toLowerCase()))
- .map((currentValue, index) => (
-
- ));
- if (input && menuListItems.length === 0) {
- menuListItems.push(
-
- );
- }
-
- return (
-
- );
- }
-}
+```ts file="MenuFilteringWithTextInput.tsx"
```
### With links
-```js
-import React from 'react';
-import { Menu, MenuContent, MenuList, MenuItem } from '@patternfly/react-core';
-
-class MenuWithLinks extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- activeItem: 0
- };
- this.onSelect = (event, itemId) => {
- this.setState({
- activeItem: itemId
- });
- };
- }
-
- render() {
- const { activeItem } = this.state;
- return (
-
- );
- }
-}
+```ts file="MenuWithLinks.tsx"
```
### With separator(s)
-```js
-import React from 'react';
-import { Divider, Menu, MenuContent, MenuList, MenuItem } from '@patternfly/react-core';
-
-class MenuWithSeparators extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- activeItem: 0
- };
- this.onSelect = (event, itemId) => {
- this.setState({
- activeItem: itemId
- });
- };
- }
-
- render() {
- const { activeItem } = this.state;
- return (
-
- );
- }
-}
+```ts file="MenuWithSeparators.tsx"
```
### With titled groups
-```js
-import React from 'react';
-import { Menu, MenuContent, MenuGroup, MenuList, MenuItem, Divider } from '@patternfly/react-core';
-
-class MenuWithTitledGroups extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- activeItem: 0
- };
- this.onSelect = (event, itemId) => {
- this.setState({
- activeItem: itemId
- });
- };
- }
-
- render() {
- const { activeItem } = this.state;
- return (
-
- );
- }
-}
+```ts file="MenuWithTitledGroups.tsx"
```
### With description
-```js
-import React from 'react';
-import { Menu, MenuContent, MenuList, MenuItem } from '@patternfly/react-core';
-import CodeBranchIcon from '@patternfly/react-icons/dist/esm/icons/code-branch-icon';
-
-class MenuWithDescription extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- activeItem: 0
- };
- this.onSelect = (event, itemId) => {
- this.setState({
- activeItem: itemId
- });
- };
- }
-
- render() {
- const { activeItem } = this.state;
- return (
-
- );
- }
-}
+```ts file="MenuWithDescription.tsx"
```
### With actions
-```js
-import React from 'react';
-import { Menu, MenuContent, MenuGroup, MenuList, MenuItem, MenuItemAction } from '@patternfly/react-core';
-import BarsIcon from '@patternfly/react-icons/dist/esm/icons/bars-icon';
-import ClipboardIcon from '@patternfly/react-icons/dist/esm/icons/clipboard-icon';
-import CodeBranchIcon from '@patternfly/react-icons/dist/esm/icons/code-branch-icon';
-import BellIcon from '@patternfly/react-icons/dist/esm/icons/bell-icon';
-
-class MenuWithActions extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- activeItem: 0,
- isActionMenuOpen: false,
- selectedItems: [0, 2, 3]
- };
-
- this.onSelect = (event, itemId) => {
- if (this.state.selectedItems.indexOf(itemId) !== -1) {
- this.setState({
- selectedItems: this.state.selectedItems.filter(id => id !== itemId)
- });
- } else {
- this.setState({
- selectedItems: [...this.state.selectedItems, itemId]
- });
- }
- };
- }
-
- render() {
- const { activeItem, selectedItems } = this.state;
-
- return (
-
- );
- }
-}
+```ts file="MenuWithActions.tsx"
```
### With favorites
-```js
-import React from 'react';
-import { Menu, MenuContent, MenuItem, MenuItemAction, MenuGroup, MenuList, Divider } from '@patternfly/react-core';
-import BarsIcon from '@patternfly/react-icons/dist/esm/icons/bars-icon';
-import ClipboardIcon from '@patternfly/react-icons/dist/esm/icons/clipboard-icon';
-import BellIcon from '@patternfly/react-icons/dist/esm/icons/bell-icon';
-
-class MenuWithFavorites extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- activeItem: 0,
- favorites: []
- };
-
- this.onSelect = (event, itemId) => {
- this.setState({
- activeItem: itemId
- });
- };
- this.onFavorite = (event, itemId, actionId) => {
- console.log(`clicked ${itemId} - ${actionId}`);
- if (actionId === 'fav') {
- const isFavorite = this.state.favorites.includes(itemId);
- if (isFavorite) {
- this.setState({
- favorites: this.state.favorites.filter(fav => fav !== itemId)
- });
- } else {
- this.setState({
- favorites: [...this.state.favorites, itemId]
- });
- }
- }
- };
- }
-
- render() {
- const { activeItem, favorites } = this.state;
-
- const items = [
- {
- text: 'Item 1',
- description: 'Description 1',
- itemId: 'item-1',
- action: ,
- actionId: 'bars'
- },
- {
- text: 'Item 2',
- description: 'Description 2',
- itemId: 'item-2',
- action: ,
- actionId: 'clipboard'
- },
- {
- text: 'Item 3',
- description: 'Description 3',
- itemId: 'item-3',
- action: ,
- actionId: 'bell'
- }
- ];
-
- return (
-
- );
- }
-}
+```ts file="MenuWithFavorites.tsx"
```
### Option single select
-```js
-import React from 'react';
-import { Menu, MenuContent, MenuList, MenuItem } from '@patternfly/react-core';
-import TableIcon from '@patternfly/react-icons/dist/esm/icons/table-icon';
-
-class MenuOptionSingleSelect extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- activeItem: 0,
- selectedItem: 0
- };
-
- this.onSelect = (event, itemId) => {
- this.setState({
- activeItem: itemId,
- selectedItem: itemId
- });
- };
- }
-
- render() {
- const { activeItem, selectedItem } = this.state;
- return (
-
- );
- }
-}
+```ts file="MenuOptionSingleSelect.tsx"
```
### Option multi select
-```js
-import React from 'react';
-import { Menu, MenuContent, MenuList, MenuItem } from '@patternfly/react-core';
-import TableIcon from '@patternfly/react-icons/dist/esm/icons/table-icon';
-
-class MenuOptionMultiSelect extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- activeItem: 0,
- selectedItems: []
- };
- this.onSelect = (event, itemId) => {
- if (this.state.selectedItems.indexOf(itemId) !== -1) {
- this.setState({
- selectedItems: this.state.selectedItems.filter(id => id !== itemId)
- });
- } else {
- this.setState({
- selectedItems: [...this.state.selectedItems, itemId]
- });
- }
- };
- }
-
- render() {
- const { activeItem, selectedItems } = this.state;
- return (
-
- );
- }
-}
+```ts file="MenuOptionMultiSelect.tsx"
```
### With drilldown
-```ts file="./MenuDrilldown.tsx" isBeta
+```ts file="./MenuWithDrilldown.tsx" isBeta
```
### With drilldown - initial drill in state
To render an initially drilled in menu, the `menuDrilledIn`, `drilldownPath`, and `activeMenu` states must be set to an initial state. The `menuHeights` state must also be set, defining the height of the root menu. The `setHeight` function passed into the `onGetMenuHeight` property must also account for updating heights, other than the root menu, as menus drill in and out of view.
-```ts file="./MenuDrilldownInitialState.tsx" isBeta
+```ts file="./MenuWithDrilldownInitialState.tsx" isBeta
```
### With drilldown - submenu functions
-```ts file="./MenuDrilldownSubmenuFunctions.tsx" isBeta
+```ts file="./MenuWithDrilldownSubmenuFunctions.tsx" isBeta
```
### With drilldown breadcrumbs
-```js isBeta
-import React from 'react';
-import {
- Menu,
- MenuContent,
- MenuList,
- MenuItem,
- Divider,
- DrilldownMenu,
- Breadcrumb,
- BreadcrumbItem,
- BreadcrumbHeading,
- MenuBreadcrumb,
- Dropdown,
- DropdownItem,
- BadgeToggle,
- Checkbox
-} from '@patternfly/react-core';
-import StorageDomainIcon from '@patternfly/react-icons/dist/esm/icons/storage-domain-icon';
-import CodeBranchIcon from '@patternfly/react-icons/dist/esm/icons/code-branch-icon';
-import LayerGroupIcon from '@patternfly/react-icons/dist/esm/icons/layer-group-icon';
-import AngleLeftIcon from '@patternfly/react-icons/dist/esm/icons/angle-left-icon';
-import CubeIcon from '@patternfly/react-icons/dist/esm/icons/cube-icon';
-
-class MenuWithDrilldownBreadcrumbs extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- menuDrilledIn: [],
- drilldownPath: [],
- menuHeights: {},
- activeMenu: 'breadcrumbs-rootMenu',
- breadcrumb: undefined,
- withMaxMenuHeight: false
- };
-
- this.onToggle = (isOpen, key) => {
- switch (key) {
- case 'app':
- this.setState({
- breadcrumb: this.appGroupingBreadcrumb(isOpen)
- });
- break;
- case 'label':
- this.setState({
- breadcrumb: this.labelsBreadcrumb(isOpen)
- });
- break;
- default:
- break;
- }
- };
-
- this.onToggleMaxMenuHeight = checked => {
- this.setState({
- withMaxMenuHeight: checked
- });
- };
-
- this.drillOut = (toMenuId, fromPathId, breadcrumb) => {
- const indexOfMenuId = this.state.menuDrilledIn.indexOf(toMenuId);
- const menuDrilledInSansLast = this.state.menuDrilledIn.slice(0, indexOfMenuId);
- const indexOfMenuIdPath = this.state.drilldownPath.indexOf(fromPathId);
- const pathSansLast = this.state.drilldownPath.slice(0, indexOfMenuIdPath);
- this.setState({
- menuDrilledIn: menuDrilledInSansLast,
- drilldownPath: pathSansLast,
- activeMenu: toMenuId,
- breadcrumb
- });
- };
- this.setHeight = (menuId, height) => {
- if (!this.state.menuHeights[menuId]) {
- this.setState({
- menuHeights: {
- ...this.state.menuHeights,
- [menuId]: height
- }
- });
- }
- };
- this.drillIn = (fromMenuId, toMenuId, pathId) => {
- this.setState({
- menuDrilledIn: [...this.state.menuDrilledIn, fromMenuId],
- drilldownPath: [...this.state.drilldownPath, pathId],
- activeMenu: toMenuId
- });
- };
-
- this.startRolloutBreadcrumb = (
-
- this.drillOut('breadcrumbs-rootMenu', 'group:start_rollout', null)}>
- Root
-
- Start rollout
-
- );
-
- this.appGroupingBreadcrumb = isOpen => {
- return (
-
- this.drillOut('breadcrumbs-rootMenu', 'group:start_rollout', null)}>
- Root
-
-
- this.onToggle(open, 'app')}>
- 1
-
- }
- isOpen={isOpen}
- dropdownItems={[
- }
- onClick={() =>
- this.drillOut('breadcrumbs-drilldownMenuStart', 'group:app_grouping_start', this.startRolloutBreadcrumb)
- }
- >
- Start rollout
-
- ]}
- />
-
- Application Grouping
-
- );
- };
-
- this.labelsBreadcrumb = isOpen => (
-
- this.drillOut('breadcrumbs-rootMenu', 'group:start_rollout', null)}>
- Root
-
-
- this.onToggle(open, 'label')}>
- 1
-
- }
- isOpen={isOpen}
- dropdownItems={[
- }
- onClick={() => this.drillOut('breadcrumbs-drilldownMenuStart', 'group:labels_start', this.startRolloutBreadcrumb)}
- >
- Start rollout
-
- ]}
- />
-
- Labels
-
- );
-
- this.pauseRolloutsBreadcrumb = (
-
- this.drillOut('breadcrumbs-rootMenu', 'group:pause_rollout', null)}>
- Root
-
- Pause rollouts
-
- );
-
- this.pauseRolloutsAppGrpBreadcrumb = (
-
- this.drillOut('breadcrumbs-rootMenu', 'group:pause_rollout', null)}>
- Root
-
- this.drillOut('breadcrumbs-drilldownMenuPause', 'group:app_grouping', this.pauseRolloutsBreadcrumb)}
- >
- Pause rollouts
-
- Application Grouping
-
- );
-
- this.pauseRolloutsLabelsBreadcrumb = (
-
- this.drillOut('breadcrumbs-rootMenu', 'group:pause_rollout', null)}>
- Root
-
- this.drillOut('breadcrumbs-drilldownMenuPause', 'group:labels', this.pauseRolloutsBreadcrumb)}
- >
- Pause rollouts
-
- Labels
-
- );
-
- this.addStorageBreadcrumb = (
-
- this.drillOut('breadcrumbs-rootMenu', 'group:storage', null)}>
- Root
-
- Add storage
-
- );
- }
-
- render() {
- const { menuDrilledIn, drilldownPath, activeMenu, menuHeights, breadcrumb, withMaxMenuHeight } = this.state;
-
- return (
- <>
-
-
-
- >
- );
- }
-}
+```ts file="MenuWithDrilldownBreadcrumbs.tsx" isBeta
```
### Scrollable
-```js
-import React from 'react';
-import { Menu, MenuContent, MenuList, MenuItem } from '@patternfly/react-core';
-
-class MenuBasicList extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- activeItem: 0
- };
- this.onSelect = (event, itemId) => {
- console.log(`clicked ${itemId}`);
- this.setState({
- activeItem: itemId
- });
- };
- }
-
- render() {
- const { activeItem } = this.state;
- return (
-
- );
- }
-}
+```ts file="MenuScrollable.tsx"
```
### Scrollable with custom menu height
-```js
-import React from 'react';
-import { Menu, MenuContent, MenuList, MenuItem } from '@patternfly/react-core';
-
-class MenuBasicList extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- activeItem: 0
- };
- this.onSelect = (event, itemId) => {
- console.log(`clicked ${itemId}`);
- this.setState({
- activeItem: itemId
- });
- };
- }
-
- render() {
- const { activeItem } = this.state;
- return (
-
- );
- }
-}
+```ts file="MenuScrollableCustomMenuHeight.tsx"
```
### With footer
-```js
-import React from 'react';
-import { Menu, MenuList, MenuItem, MenuContent, MenuFooter, Button } from '@patternfly/react-core';
-
-class FooterMenu extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- activeItem: 0
- };
- this.onSelect = (event, itemId) => {
- console.log(`clicked ${itemId}`);
- this.setState({
- activeItem: itemId
- });
- };
- }
-
- render() {
- const { activeItem } = this.state;
- return (
-
- );
- }
-}
+```ts file="MenuWithFooter.tsx"
```
### With view more
-```js
-import React from 'react';
-import { Menu, MenuList, MenuItem, MenuContent, MenuFooter, Spinner } from '@patternfly/react-core';
-
-class ViewMoreMenu extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- activeItem: 0,
- isLoading: false,
- numOptions: 3
- };
- this.activeItemRef = React.createRef();
- this.viewMoreRef = React.createRef();
-
- this.menuOptions = [
- ,
- ,
- ,
- ,
- ,
- ,
- ,
- ,
-
- ];
-
- this.onSelect = (event, itemId) => {
- console.log(`clicked ${itemId}`);
- this.setState({
- activeItem: itemId
- });
- };
-
- this.simulateNetworkCall = networkCallback => {
- setTimeout(networkCallback, 2000);
- };
-
- this.getNextValidItem = (startingIndex, maxLength) => {
- let validItem;
- for (let i = startingIndex; i < maxLength; i++) {
- if (this.menuOptions[i].props.isDisabled) {
- continue;
- } else {
- validItem = this.menuOptions[i];
- break;
- }
- }
- return validItem;
- };
-
- this.loadMoreOptions = stateCallback => {
- const newLength =
- this.state.numOptions + 3 <= this.menuOptions.length ? this.state.numOptions + 3 : this.menuOptions.length;
- const prevPosition = this.state.numOptions;
- const nextValidItem = this.getNextValidItem(prevPosition, newLength);
-
- this.setState({ numOptions: newLength, isLoading: false, activeItem: nextValidItem.props.itemId }, stateCallback);
- };
-
- this.onViewMoreClick = event => {
- this.setState({ isLoading: true });
- this.simulateNetworkCall(() => {
- this.loadMoreOptions(() => {
- this.activeItemRef.current.focus();
- });
- });
- };
-
- this.onViewMoreKeyDown = event => {
- if (!(event.key === ' ' || event.key === 'Enter')) {
- return;
- }
- event.stopPropagation();
- event.preventDefault();
-
- this.setState({ isLoading: true });
- this.simulateNetworkCall(() => {
- this.loadMoreOptions(() => {
- if (this.viewMoreRef.current) {
- this.viewMoreRef.current.tabIndex = -1;
- }
- const firstMenuItem = this.activeItemRef.current.closest('ul').firstChild;
- firstMenuItem.querySelector('button, a').tabIndex = -1;
- this.activeItemRef.current.tabIndex = 0;
- this.activeItemRef.current.focus();
- });
- });
- };
- }
-
- render() {
- const { activeItem, isLoading, numOptions } = this.state;
- return (
-
- );
- }
-}
+```ts file="MenuWithViewMore.tsx"
```
diff --git a/packages/react-core/src/components/Menu/examples/MenuBasic.tsx b/packages/react-core/src/components/Menu/examples/MenuBasic.tsx
new file mode 100644
index 00000000000..98280db25ab
--- /dev/null
+++ b/packages/react-core/src/components/Menu/examples/MenuBasic.tsx
@@ -0,0 +1,51 @@
+import React from 'react';
+import { Menu, MenuContent, MenuList, MenuItem, Checkbox } from '@patternfly/react-core';
+
+export const MenuBasic: React.FunctionComponent = () => {
+ const [activeItem, setActiveItem] = React.useState(0);
+ const [isPlain, setIsPlain] = React.useState(false);
+
+ const onSelect = (_event: React.MouseEvent | undefined, itemId: number | string | undefined) => {
+ const item = itemId as number; // eslint-disable-next-line no-console
+ console.log(`clicked ${itemId}`);
+ setActiveItem(item);
+ };
+
+ const togglePlain = (checked: boolean) => {
+ setIsPlain(checked);
+ };
+
+ return (
+
+
+
+
+
+
+ );
+};
diff --git a/packages/react-core/src/components/Menu/examples/MenuFilteringWithTextInput.tsx b/packages/react-core/src/components/Menu/examples/MenuFilteringWithTextInput.tsx
new file mode 100644
index 00000000000..4e8f32e2453
--- /dev/null
+++ b/packages/react-core/src/components/Menu/examples/MenuFilteringWithTextInput.tsx
@@ -0,0 +1,52 @@
+import React from 'react';
+import { Menu, MenuList, MenuItem, MenuContent, MenuInput, TextInput, Divider } from '@patternfly/react-core';
+
+export const MenuFilteringWithTextInput: React.FunctionComponent = () => {
+ const [activeItem, setActiveItem] = React.useState(0);
+ const [input, setInput] = React.useState('');
+
+ const onSelect = (_event: React.MouseEvent | undefined, itemId: number | string | undefined) => {
+ const item = itemId as number; // eslint-disable-next-line no-console
+ console.log(`clicked ${itemId}`);
+ setActiveItem(item);
+ };
+
+ const handleTextInputChange = (value: string) => {
+ setInput(value);
+ };
+
+ const menuListItemsText = ['Action 1', 'Action 2', 'Action 3'];
+
+ const menuListItems = menuListItemsText
+ .filter(item => !input || item.toLowerCase().includes(input.toString().toLowerCase()))
+ .map((currentValue, index) => (
+
+ ));
+ if (input && menuListItems.length === 0) {
+ menuListItems.push(
+
+ );
+ }
+
+ return (
+
+ );
+};
diff --git a/packages/react-core/src/components/Menu/examples/MenuOptionMultiSelect.tsx b/packages/react-core/src/components/Menu/examples/MenuOptionMultiSelect.tsx
new file mode 100644
index 00000000000..d8c333699e6
--- /dev/null
+++ b/packages/react-core/src/components/Menu/examples/MenuOptionMultiSelect.tsx
@@ -0,0 +1,30 @@
+import React from 'react';
+import { Menu, MenuContent, MenuList, MenuItem } from '@patternfly/react-core';
+import TableIcon from '@patternfly/react-icons/dist/esm/icons/table-icon';
+
+export const MenuOptionMultiSelect: React.FunctionComponent = () => {
+ const [selectedItems, setSelectedItems] = React.useState([]);
+
+ const onSelect = (_event: React.MouseEvent | undefined, itemId: number | string | undefined) => {
+ const item = itemId as number;
+ if (selectedItems.indexOf(item) !== -1) {
+ setSelectedItems(selectedItems.filter(id => id !== item));
+ } else {
+ setSelectedItems([...selectedItems, item]);
+ }
+ };
+
+ return (
+
+ );
+};
diff --git a/packages/react-core/src/components/Menu/examples/MenuOptionSingleSelect.tsx b/packages/react-core/src/components/Menu/examples/MenuOptionSingleSelect.tsx
new file mode 100644
index 00000000000..57e21bcbd99
--- /dev/null
+++ b/packages/react-core/src/components/Menu/examples/MenuOptionSingleSelect.tsx
@@ -0,0 +1,27 @@
+import React from 'react';
+import { Menu, MenuContent, MenuList, MenuItem } from '@patternfly/react-core';
+import TableIcon from '@patternfly/react-icons/dist/esm/icons/table-icon';
+
+export const MenuOptionSingleSelect: React.FunctionComponent = () => {
+ const [activeItem, setActiveItem] = React.useState(0);
+ const [selectedItem, setSelectedItem] = React.useState(0);
+
+ const onSelect = (_event, itemId) => {
+ setActiveItem(itemId);
+ setSelectedItem(itemId);
+ };
+
+ return (
+
+ );
+};
diff --git a/packages/react-core/src/components/Menu/examples/MenuScrollable.tsx b/packages/react-core/src/components/Menu/examples/MenuScrollable.tsx
new file mode 100644
index 00000000000..e4909ec1dfc
--- /dev/null
+++ b/packages/react-core/src/components/Menu/examples/MenuScrollable.tsx
@@ -0,0 +1,48 @@
+import React from 'react';
+import { Menu, MenuContent, MenuList, MenuItem } from '@patternfly/react-core';
+
+export const MenuScrollable: React.FunctionComponent = () => {
+ const [activeItem, setActiveItem] = React.useState(0);
+
+ const onSelect = (_event, itemId) => {
+ // eslint-disable-next-line no-console
+ console.log(`clicked ${itemId}`);
+ setActiveItem(itemId);
+ };
+
+ return (
+
+ );
+};
diff --git a/packages/react-core/src/components/Menu/examples/MenuScrollableCustomMenuHeight.tsx b/packages/react-core/src/components/Menu/examples/MenuScrollableCustomMenuHeight.tsx
new file mode 100644
index 00000000000..f1891efae60
--- /dev/null
+++ b/packages/react-core/src/components/Menu/examples/MenuScrollableCustomMenuHeight.tsx
@@ -0,0 +1,48 @@
+import React from 'react';
+import { Menu, MenuContent, MenuList, MenuItem } from '@patternfly/react-core';
+
+export const MenuScrollableCustomMenuHeight: React.FunctionComponent = () => {
+ const [activeItem, setActiveItem] = React.useState(0);
+
+ const onSelect = (_event, itemId) => {
+ // eslint-disable-next-line no-console
+ console.log(`clicked ${itemId}`);
+ setActiveItem(itemId);
+ };
+
+ return (
+
+ );
+};
diff --git a/packages/react-core/src/components/Menu/examples/MenuWithActions.tsx b/packages/react-core/src/components/Menu/examples/MenuWithActions.tsx
new file mode 100644
index 00000000000..13ff5017510
--- /dev/null
+++ b/packages/react-core/src/components/Menu/examples/MenuWithActions.tsx
@@ -0,0 +1,75 @@
+import React from 'react';
+import { Menu, MenuContent, MenuGroup, MenuList, MenuItem, MenuItemAction } from '@patternfly/react-core';
+import BarsIcon from '@patternfly/react-icons/dist/esm/icons/bars-icon';
+import ClipboardIcon from '@patternfly/react-icons/dist/esm/icons/clipboard-icon';
+import CodeBranchIcon from '@patternfly/react-icons/dist/esm/icons/code-branch-icon';
+import BellIcon from '@patternfly/react-icons/dist/esm/icons/bell-icon';
+
+export const MenuWithActions: React.FunctionComponent = () => {
+ const [selectedItems, setSelectedItems] = React.useState([0, 2, 3]);
+
+ const onSelect = (_event: React.MouseEvent | undefined, itemId: number | string | undefined) => {
+ const item = itemId as number;
+ if (selectedItems.indexOf(item) !== -1) {
+ setSelectedItems(selectedItems.filter(id => id !== item));
+ } else {
+ setSelectedItems([...selectedItems, item]);
+ }
+ };
+
+ return (
+
+ );
+};
diff --git a/packages/react-core/src/components/Menu/examples/MenuWithCheckbox.tsx b/packages/react-core/src/components/Menu/examples/MenuWithCheckbox.tsx
index 2640d18fb7c..0980d539388 100644
--- a/packages/react-core/src/components/Menu/examples/MenuWithCheckbox.tsx
+++ b/packages/react-core/src/components/Menu/examples/MenuWithCheckbox.tsx
@@ -1,11 +1,11 @@
import React from 'react';
import { Menu, MenuContent, MenuList, MenuItem } from '@patternfly/react-core';
-export const MenuCheckboxList: React.FunctionComponent = () => {
+export const MenuWithCheckbox: React.FunctionComponent = () => {
const [selectedItems, setSelectedItems] = React.useState([]);
/* eslint no-unused-vars: ["error", {"args": "after-used"}] */
- const onSelect = (event: React.MouseEvent, itemId: number | string) => {
+ const onSelect = (_event: React.MouseEvent | undefined, itemId: number | string | undefined) => {
const item = itemId as number;
if (selectedItems.includes(item)) {
setSelectedItems(selectedItems.filter(id => id !== item));
diff --git a/packages/react-core/src/components/Menu/examples/MenuWithDescription.tsx b/packages/react-core/src/components/Menu/examples/MenuWithDescription.tsx
new file mode 100644
index 00000000000..f6b3cc77ea5
--- /dev/null
+++ b/packages/react-core/src/components/Menu/examples/MenuWithDescription.tsx
@@ -0,0 +1,35 @@
+import React from 'react';
+import { Menu, MenuContent, MenuList, MenuItem } from '@patternfly/react-core';
+import CodeBranchIcon from '@patternfly/react-icons/dist/esm/icons/code-branch-icon';
+
+export const MenuWithDescription: React.FunctionComponent = () => {
+ const [activeItem, setActiveItem] = React.useState(0);
+
+ const onSelect = (_event: React.MouseEvent | undefined, itemId: number | string | undefined) => {
+ const item = itemId as number; // eslint-disable-next-line no-console
+ console.log(`clicked ${item}`);
+ setActiveItem(item);
+ };
+
+ return (
+
+ );
+};
diff --git a/packages/react-core/src/components/Menu/examples/MenuDrilldown.tsx b/packages/react-core/src/components/Menu/examples/MenuWithDrilldown.tsx
similarity index 99%
rename from packages/react-core/src/components/Menu/examples/MenuDrilldown.tsx
rename to packages/react-core/src/components/Menu/examples/MenuWithDrilldown.tsx
index 6798b35f8c9..0918400a12a 100644
--- a/packages/react-core/src/components/Menu/examples/MenuDrilldown.tsx
+++ b/packages/react-core/src/components/Menu/examples/MenuWithDrilldown.tsx
@@ -5,7 +5,7 @@ import CodeBranchIcon from '@patternfly/react-icons/dist/esm/icons/code-branch-i
import LayerGroupIcon from '@patternfly/react-icons/dist/esm/icons/layer-group-icon';
import CubeIcon from '@patternfly/react-icons/dist/esm/icons/cube-icon';
-export const MenuDrilldown: React.FunctionComponent = () => {
+export const MenuWithDrilldown: React.FunctionComponent = () => {
const [menuDrilledIn, setMenuDrilledIn] = React.useState([]);
const [drilldownPath, setDrilldownPath] = React.useState([]);
const [menuHeights, setMenuHeights] = React.useState({});
diff --git a/packages/react-core/src/components/Menu/examples/MenuWithDrilldownBreadcrumbs.tsx b/packages/react-core/src/components/Menu/examples/MenuWithDrilldownBreadcrumbs.tsx
new file mode 100644
index 00000000000..61ed5fc80c4
--- /dev/null
+++ b/packages/react-core/src/components/Menu/examples/MenuWithDrilldownBreadcrumbs.tsx
@@ -0,0 +1,331 @@
+import React from 'react';
+import {
+ Menu,
+ MenuContent,
+ MenuList,
+ MenuItem,
+ Divider,
+ DrilldownMenu,
+ Breadcrumb,
+ BreadcrumbItem,
+ BreadcrumbHeading,
+ MenuBreadcrumb,
+ Dropdown,
+ DropdownItem,
+ BadgeToggle,
+ Checkbox
+} from '@patternfly/react-core';
+import StorageDomainIcon from '@patternfly/react-icons/dist/esm/icons/storage-domain-icon';
+import CodeBranchIcon from '@patternfly/react-icons/dist/esm/icons/code-branch-icon';
+import LayerGroupIcon from '@patternfly/react-icons/dist/esm/icons/layer-group-icon';
+import AngleLeftIcon from '@patternfly/react-icons/dist/esm/icons/angle-left-icon';
+import CubeIcon from '@patternfly/react-icons/dist/esm/icons/cube-icon';
+
+export const MenuWithDrilldownBreadcrumbs: React.FunctionComponent = () => {
+ const [menuDrilledIn, setMenuDrilledIn] = React.useState([]);
+ const [drilldownPath, setDrilldownPath] = React.useState([]);
+ const [menuHeights, setMenuHeights] = React.useState({});
+ const [activeMenu, setActiveMenu] = React.useState('breadcrumbs-rootMenu');
+ const [breadcrumb, setBreadcrumb] = React.useState();
+ const [withMaxMenuHeight, setWithMaxMenuHeight] = React.useState(false);
+
+ const onToggle = (isOpen: boolean, key: string) => {
+ switch (key) {
+ case 'app':
+ setBreadcrumb(appGroupingBreadcrumb(isOpen));
+ break;
+ case 'label':
+ setBreadcrumb(labelsBreadcrumb(isOpen));
+ break;
+ default:
+ break;
+ }
+ };
+
+ const onToggleMaxMenuHeight = (checked: boolean) => {
+ setWithMaxMenuHeight(checked);
+ };
+
+ const drillOut = (toMenuId: string, fromPathId: string, _breadcrumb: JSX.Element | null) => {
+ const indexOfMenuId = menuDrilledIn.indexOf(toMenuId);
+ const menuDrilledInSansLast = menuDrilledIn.slice(0, indexOfMenuId);
+ const indexOfMenuIdPath = drilldownPath.indexOf(fromPathId);
+ const pathSansLast = drilldownPath.slice(0, indexOfMenuIdPath);
+ setMenuDrilledIn(menuDrilledInSansLast);
+ setDrilldownPath(pathSansLast);
+ setActiveMenu(toMenuId);
+ };
+ const setHeight = (menuId: string, height: number) => {
+ if (!menuHeights[menuId]) {
+ setMenuHeights({ ...menuHeights, [menuId]: height });
+ }
+ };
+ const drillIn = (fromMenuId: string, toMenuId: string, pathId: string) => {
+ setMenuDrilledIn([...menuDrilledIn, fromMenuId]);
+ setDrilldownPath([...drilldownPath, pathId]);
+ setActiveMenu(toMenuId);
+ };
+
+ const startRolloutBreadcrumb = (
+
+ drillOut('breadcrumbs-rootMenu', 'group:start_rollout', null)}>
+ Root
+
+ Start rollout
+
+ );
+
+ const appGroupingBreadcrumb = (isOpen: boolean) => (
+
+ drillOut('breadcrumbs-rootMenu', 'group:start_rollout', null)}>
+ Root
+
+
+ onToggle(open, 'app')}>
+ 1
+
+ }
+ isOpen={isOpen}
+ dropdownItems={[
+ }
+ onClick={() =>
+ drillOut('breadcrumbs-drilldownMenuStart', 'group:app_grouping_start', startRolloutBreadcrumb)
+ }
+ >
+ Start rollout
+
+ ]}
+ />
+
+ Application Grouping
+
+ );
+
+ const labelsBreadcrumb = (isOpen: boolean) => (
+
+ drillOut('breadcrumbs-rootMenu', 'group:start_rollout', null)}>
+ Root
+
+
+ onToggle(open, 'label')}>
+ 1
+
+ }
+ isOpen={isOpen}
+ dropdownItems={[
+ }
+ onClick={() => drillOut('breadcrumbs-drilldownMenuStart', 'group:labels_start', startRolloutBreadcrumb)}
+ >
+ Start rollout
+
+ ]}
+ />
+
+ Labels
+
+ );
+
+ const pauseRolloutsBreadcrumb = (
+
+ drillOut('breadcrumbs-rootMenu', 'group:pause_rollout', null)}>
+ Root
+
+ Pause rollouts
+
+ );
+
+ const pauseRolloutsAppGrpBreadcrumb = (
+
+ drillOut('breadcrumbs-rootMenu', 'group:pause_rollout', null)}>
+ Root
+
+ drillOut('breadcrumbs-drilldownMenuPause', 'group:app_grouping', pauseRolloutsBreadcrumb)}
+ >
+ Pause rollouts
+
+ Application Grouping
+
+ );
+
+ const pauseRolloutsLabelsBreadcrumb = (
+
+ drillOut('breadcrumbs-rootMenu', 'group:pause_rollout', null)}>
+ Root
+
+ drillOut('breadcrumbs-drilldownMenuPause', 'group:labels', pauseRolloutsBreadcrumb)}
+ >
+ Pause rollouts
+
+ Labels
+
+ );
+
+ const addStorageBreadcrumb = (
+
+ drillOut('breadcrumbs-rootMenu', 'group:storage', null)}>
+ Root
+
+ Add storage
+
+ );
+
+ return (
+ <>
+
+
+
+ >
+ );
+};
diff --git a/packages/react-core/src/components/Menu/examples/MenuDrilldownInitialState.tsx b/packages/react-core/src/components/Menu/examples/MenuWithDrilldownInitialState.tsx
similarity index 100%
rename from packages/react-core/src/components/Menu/examples/MenuDrilldownInitialState.tsx
rename to packages/react-core/src/components/Menu/examples/MenuWithDrilldownInitialState.tsx
diff --git a/packages/react-core/src/components/Menu/examples/MenuDrilldownSubmenuFunctions.tsx b/packages/react-core/src/components/Menu/examples/MenuWithDrilldownSubmenuFunctions.tsx
similarity index 98%
rename from packages/react-core/src/components/Menu/examples/MenuDrilldownSubmenuFunctions.tsx
rename to packages/react-core/src/components/Menu/examples/MenuWithDrilldownSubmenuFunctions.tsx
index 5568ee484dc..9629b890d2a 100644
--- a/packages/react-core/src/components/Menu/examples/MenuDrilldownSubmenuFunctions.tsx
+++ b/packages/react-core/src/components/Menu/examples/MenuWithDrilldownSubmenuFunctions.tsx
@@ -5,7 +5,7 @@ import CodeBranchIcon from '@patternfly/react-icons/dist/esm/icons/code-branch-i
import LayerGroupIcon from '@patternfly/react-icons/dist/esm/icons/layer-group-icon';
import CubeIcon from '@patternfly/react-icons/dist/esm/icons/cube-icon';
-export const MenuDrilldownSubmenuFunctions: React.FunctionComponent = () => {
+export const MenuWithDrilldownSubmenuFunctions: React.FunctionComponent = () => {
const [menuDrilledIn, setMenuDrilledIn] = React.useState([]);
const [drilldownPath, setDrilldownPath] = React.useState([]);
const [menuHeights, setMenuHeights] = React.useState({});
diff --git a/packages/react-core/src/components/Menu/examples/MenuWithFavorites.tsx b/packages/react-core/src/components/Menu/examples/MenuWithFavorites.tsx
new file mode 100644
index 00000000000..2e778d95533
--- /dev/null
+++ b/packages/react-core/src/components/Menu/examples/MenuWithFavorites.tsx
@@ -0,0 +1,104 @@
+import React from 'react';
+import { Menu, MenuContent, MenuItem, MenuItemAction, MenuGroup, MenuList, Divider } from '@patternfly/react-core';
+import BarsIcon from '@patternfly/react-icons/dist/esm/icons/bars-icon';
+import ClipboardIcon from '@patternfly/react-icons/dist/esm/icons/clipboard-icon';
+import BellIcon from '@patternfly/react-icons/dist/esm/icons/bell-icon';
+
+export const MenuWithFavorites: React.FunctionComponent = () => {
+ const [activeItem, setActiveItem] = React.useState(0);
+ const [favorites, setFavorites] = React.useState([]);
+
+ const onSelect = (_event: React.MouseEvent | undefined, itemId: number | string | undefined) => {
+ const item = itemId as number; // eslint-disable-next-line no-console
+ console.log(`clicked ${item}`);
+ setActiveItem(item);
+ };
+
+ const onFavorite = (event, itemId: string, actionId: string) => {
+ // eslint-disable-next-line no-console
+ console.log(`clicked ${itemId} - ${actionId}`);
+ if (actionId === 'fav') {
+ const isFavorite = favorites.includes(itemId);
+ if (isFavorite) {
+ setFavorites(favorites.filter(fav => fav !== itemId));
+ } else {
+ setFavorites([...favorites, itemId]);
+ }
+ }
+ };
+
+ const items = [
+ {
+ text: 'Item 1',
+ description: 'Description 1',
+ itemId: 'item-1',
+ action: ,
+ actionId: 'bars'
+ },
+ {
+ text: 'Item 2',
+ description: 'Description 2',
+ itemId: 'item-2',
+ action: ,
+ actionId: 'clipboard'
+ },
+ {
+ text: 'Item 3',
+ description: 'Description 3',
+ itemId: 'item-3',
+ action: ,
+ actionId: 'bell'
+ }
+ ];
+
+ return (
+
+ );
+};
diff --git a/packages/react-core/src/components/Menu/examples/MenuWithFooter.tsx b/packages/react-core/src/components/Menu/examples/MenuWithFooter.tsx
new file mode 100644
index 00000000000..eb6f09f059b
--- /dev/null
+++ b/packages/react-core/src/components/Menu/examples/MenuWithFooter.tsx
@@ -0,0 +1,40 @@
+import React from 'react';
+import { Menu, MenuList, MenuItem, MenuContent, MenuFooter, Button } from '@patternfly/react-core';
+
+export const MenuWithFooter: React.FunctionComponent = () => {
+ const [activeItem, setActiveItem] = React.useState(0);
+
+ const onSelect = (_event: React.MouseEvent | undefined, itemId: number | string | undefined) => {
+ const item = itemId as number;
+ // eslint-disable-next-line no-console
+ console.log(`clicked ${item}`);
+ setActiveItem(item);
+ };
+
+ return (
+
+ );
+};
diff --git a/packages/react-core/src/components/Menu/examples/MenuWithIcons.tsx b/packages/react-core/src/components/Menu/examples/MenuWithIcons.tsx
new file mode 100644
index 00000000000..d8b65ceb977
--- /dev/null
+++ b/packages/react-core/src/components/Menu/examples/MenuWithIcons.tsx
@@ -0,0 +1,33 @@
+import React from 'react';
+import { Menu, MenuContent, MenuList, MenuItem } from '@patternfly/react-core';
+import CodeBranchIcon from '@patternfly/react-icons/dist/esm/icons/code-branch-icon';
+import LayerGroupIcon from '@patternfly/react-icons/dist/esm/icons/layer-group-icon';
+import CubeIcon from '@patternfly/react-icons/dist/esm/icons/cube-icon';
+
+export const MenuWithIcons: React.FunctionComponent = () => {
+ const [activeItem, setActiveItem] = React.useState(0);
+
+ const onSelect = (_event: React.MouseEvent | undefined, itemId: number | string | undefined) => {
+ const item = itemId as number; // eslint-disable-next-line no-console
+ console.log(`clicked ${item}`);
+ setActiveItem(item);
+ };
+
+ return (
+
+ );
+};
diff --git a/packages/react-core/src/components/Menu/examples/MenuWithLinks.tsx b/packages/react-core/src/components/Menu/examples/MenuWithLinks.tsx
new file mode 100644
index 00000000000..07d4fc5eb0f
--- /dev/null
+++ b/packages/react-core/src/components/Menu/examples/MenuWithLinks.tsx
@@ -0,0 +1,30 @@
+import React from 'react';
+import { Menu, MenuContent, MenuList, MenuItem } from '@patternfly/react-core';
+
+export const MenuWithLinks: React.FunctionComponent = () => {
+ const [activeItem, setActiveItem] = React.useState(0);
+
+ const onSelect = (_event: React.MouseEvent | undefined, itemId: number | string | undefined) => {
+ const item = itemId as number; // eslint-disable-next-line no-console
+ console.log(`clicked ${item}`);
+ setActiveItem(item);
+ };
+
+ return (
+
+ );
+};
diff --git a/packages/react-core/src/components/Menu/examples/MenuWithSeparators.tsx b/packages/react-core/src/components/Menu/examples/MenuWithSeparators.tsx
new file mode 100644
index 00000000000..6c1980def14
--- /dev/null
+++ b/packages/react-core/src/components/Menu/examples/MenuWithSeparators.tsx
@@ -0,0 +1,26 @@
+import React from 'react';
+import { Divider, Menu, MenuContent, MenuList, MenuItem } from '@patternfly/react-core';
+
+export const MenuWithSeparators: React.FunctionComponent = () => {
+ const [activeItem, setActiveItem] = React.useState(0);
+
+ const onSelect = (_event: React.MouseEvent | undefined, itemId: number | string | undefined) => {
+ const item = itemId as number;
+ // eslint-disable-next-line no-console
+ console.log(`clicked ${item}`);
+ setActiveItem(item);
+ };
+
+ return (
+
+ );
+};
diff --git a/packages/react-core/src/components/Menu/examples/MenuWithTitledGroups.tsx b/packages/react-core/src/components/Menu/examples/MenuWithTitledGroups.tsx
new file mode 100644
index 00000000000..6318fbaaf04
--- /dev/null
+++ b/packages/react-core/src/components/Menu/examples/MenuWithTitledGroups.tsx
@@ -0,0 +1,46 @@
+import React from 'react';
+import { Menu, MenuContent, MenuGroup, MenuList, MenuItem, Divider } from '@patternfly/react-core';
+
+export const MenuWithTitledGroups: React.FunctionComponent = () => {
+ const [activeItem, setActiveItem] = React.useState(0);
+
+ const onSelect = (_event: React.MouseEvent | undefined, itemId: number | string | undefined) => {
+ const item = itemId as number; // eslint-disable-next-line no-console
+ console.log(`clicked ${item}`);
+ setActiveItem(item);
+ };
+
+ return (
+
+ );
+};
diff --git a/packages/react-core/src/components/Menu/examples/MenuWithViewMore.tsx b/packages/react-core/src/components/Menu/examples/MenuWithViewMore.tsx
new file mode 100644
index 00000000000..9d84e8b56de
--- /dev/null
+++ b/packages/react-core/src/components/Menu/examples/MenuWithViewMore.tsx
@@ -0,0 +1,118 @@
+import React from 'react';
+import { Menu, MenuList, MenuItem, MenuContent, Spinner } from '@patternfly/react-core';
+
+export const MenuWithViewMore: React.FunctionComponent = () => {
+ const [activeItem, setActiveItem] = React.useState(0);
+ const [isLoading, setIsLoading] = React.useState(false);
+ const [numOptions, setNumOptions] = React.useState(3);
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ const [menuOptions, setMenuOptions] = React.useState([
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+
+ ]);
+ const [visibleOptions, setVisibleOptions] = React.useState(menuOptions.slice(0, numOptions));
+
+ const activeItemRef = React.useRef(null);
+ const viewMoreRef = React.useRef(null);
+
+ React.useEffect(() => {
+ activeItemRef.current?.focus();
+ }, [visibleOptions]);
+
+ const onSelect = (_event: React.MouseEvent | undefined, itemId: number | string | undefined) => {
+ // eslint-disable-next-line no-console
+ console.log(`clicked ${itemId}`);
+ setActiveItem(itemId as string);
+ };
+
+ const simulateNetworkCall = (networkCallback: () => void) => {
+ setTimeout(networkCallback, 2000);
+ };
+
+ const getNextValidItem = (startingIndex: number, maxLength: number) => {
+ let validItem;
+ for (let i = startingIndex; i < maxLength; i++) {
+ if (menuOptions[i].props.isDisabled) {
+ continue;
+ } else {
+ validItem = menuOptions[i];
+ break;
+ }
+ }
+ return validItem;
+ };
+
+ const loadMoreOptions = () => {
+ const newLength = numOptions + 3 <= menuOptions.length ? numOptions + 3 : menuOptions.length;
+ const prevPosition = numOptions;
+ const nextValidItem = getNextValidItem(prevPosition, newLength);
+
+ setNumOptions(newLength);
+ setIsLoading(false);
+ setActiveItem(nextValidItem.props.itemId);
+ setVisibleOptions(menuOptions.slice(0, newLength));
+ };
+
+ const onViewMoreClick = () => {
+ setIsLoading(true);
+ simulateNetworkCall(() => {
+ loadMoreOptions();
+ });
+ };
+
+ return (
+
+ );
+};
diff --git a/packages/react-core/tsconfig.json b/packages/react-core/tsconfig.json
index 15945ac6cfa..f3062d0890f 100644
--- a/packages/react-core/tsconfig.json
+++ b/packages/react-core/tsconfig.json
@@ -1,6 +1,7 @@
{
"extends": "../tsconfig.base.json",
"compilerOptions": {
+ "jsx": "react",
"rootDir": "./src",
"outDir": "./dist/esm",
"tsBuildInfoFile": "dist/esm.tsbuildinfo"