Skip to content

Commit

Permalink
feat(MenuToggle/Select): add status (#10209)
Browse files Browse the repository at this point in the history
* feat(MenuToggle/Select): add status

* pr feedback

* update menutoggle example

* update example desc

* mark prop as beta
  • Loading branch information
kmcfaul committed Mar 27, 2024
1 parent 41a22fb commit 2afe1bf
Show file tree
Hide file tree
Showing 6 changed files with 198 additions and 24 deletions.
33 changes: 33 additions & 0 deletions packages/react-core/src/components/MenuToggle/MenuToggle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@ import styles from '@patternfly/react-styles/css/components/MenuToggle/menu-togg
import { css } from '@patternfly/react-styles';
import CaretDownIcon from '@patternfly/react-icons/dist/esm/icons/caret-down-icon';
import { BadgeProps } from '../Badge';
import CheckCircleIcon from '@patternfly/react-icons/dist/esm/icons/check-circle-icon';
import ExclamationCircleIcon from '@patternfly/react-icons/dist/esm/icons/exclamation-circle-icon';
import ExclamationTriangleIcon from '@patternfly/react-icons/dist/esm/icons/exclamation-triangle-icon';

export enum MenuToggleStatus {
success = 'success',
danger = 'danger',
warning = 'warning'
}

export type MenuToggleElement = HTMLDivElement | HTMLButtonElement;

Expand Down Expand Up @@ -37,6 +46,10 @@ export interface MenuToggleProps
splitButtonOptions?: SplitButtonOptions;
/** Variant styles of the menu toggle */
variant?: 'default' | 'plain' | 'primary' | 'plainText' | 'secondary' | 'typeahead';
/** @beta Status styles of the menu toggle */
status?: 'success' | 'warning' | 'danger';
/** Overrides the status icon */
statusIcon?: React.ReactNode;
/** Optional icon or image rendered inside the toggle, before the children content. It is
* recommended to wrap most basic icons in our icon component.
*/
Expand Down Expand Up @@ -69,6 +82,8 @@ class MenuToggleBase extends React.Component<MenuToggleProps> {
isFullWidth,
splitButtonOptions,
variant,
status,
statusIcon,
innerRef,
onClick,
'aria-label': ariaLabel,
Expand All @@ -77,8 +92,25 @@ class MenuToggleBase extends React.Component<MenuToggleProps> {
const isPlain = variant === 'plain';
const isPlainText = variant === 'plainText';
const isTypeahead = variant === 'typeahead';

let _statusIcon = statusIcon;
if (!statusIcon) {
switch (status) {
case MenuToggleStatus.success:
_statusIcon = <CheckCircleIcon aria-hidden="true" />;
break;
case MenuToggleStatus.warning:
_statusIcon = <ExclamationTriangleIcon aria-hidden="true" />;
break;
case MenuToggleStatus.danger:
_statusIcon = <ExclamationCircleIcon aria-hidden="true" />;
break;
}
}

const toggleControls = (
<span className={css(styles.menuToggleControls)}>
{status !== undefined && <span className={css(styles.menuToggleStatusIcon)}>{_statusIcon}</span>}
<span className={css(styles.menuToggleToggleIcon)}>
<CaretDownIcon aria-hidden />
</span>
Expand Down Expand Up @@ -111,6 +143,7 @@ class MenuToggleBase extends React.Component<MenuToggleProps> {
isExpanded && styles.modifiers.expanded,
variant === 'primary' && styles.modifiers.primary,
variant === 'secondary' && styles.modifiers.secondary,
status && styles.modifiers[status],
(isPlain || isPlainText) && styles.modifiers.plain,
isPlainText && styles.modifiers.text,
isFullHeight && styles.modifiers.fullHeight,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,21 @@ describe('menu toggle', () => {
expect(asFragment()).toMatchSnapshot();
});

test('shows success status', () => {
render(<MenuToggle status="success">Toggle</MenuToggle>);
expect(screen.getByRole('button')).toHaveClass('pf-m-success');
});

test('shows warning status', () => {
render(<MenuToggle status="warning">Toggle</MenuToggle>);
expect(screen.getByRole('button')).toHaveClass('pf-m-warning');
});

test('shows danger status', () => {
render(<MenuToggle status="danger">Toggle</MenuToggle>);
expect(screen.getByRole('button')).toHaveClass('pf-m-danger');
});

test('split toggle passes onClick', async () => {
const mockClick = jest.fn();
const user = userEvent.setup();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import TimesIcon from '@patternfly/react-icons/dist/esm/icons/times-icon';

### Collapsed toggle

A toggle is collapsed until it is selected by a user.
A toggle is collapsed until it is selected by a user.

```ts
import React from 'react';
Expand All @@ -29,7 +29,7 @@ import { MenuToggle } from '@patternfly/react-core';

### Expanded toggle

When a user selects a toggle, it becomes expanded and is styled with a blue underline. To flag expanded toggles, and apply expanded styling, use the `isExpanded` property .
When a user selects a toggle, it becomes expanded and is styled with a blue underline. To flag expanded toggles, and apply expanded styling, use the `isExpanded` property .

```ts
import React from 'react';
Expand All @@ -41,7 +41,7 @@ import { MenuToggle } from '@patternfly/react-core';

### Disabled toggle

To disable the selection and expansion of a toggle, use the `isDisabled` property.
To disable the selection and expansion of a toggle, use the `isDisabled` property.

```ts
import React from 'react';
Expand All @@ -65,7 +65,7 @@ import { MenuToggle, Badge } from '@patternfly/react-core';

### With icons

To add a recognizable icon to a menu toggle, use the `icon` property. The following example adds a `CogIcon` to the toggle.
To add a recognizable icon to a menu toggle, use the `icon` property. The following example adds a `CogIcon` to the toggle.

For most basic icons, it is recommended to wrap it inside our [icon component](/components/icon).

Expand Down Expand Up @@ -111,7 +111,7 @@ import CogIcon from '@patternfly/react-icons/dist/esm/icons/cog-icon';

### With avatar and text

You can also pass images into the `icon` property. The following example passes in an `<Avatar>` component with an `imgAvatar`.
You can also pass images into the `icon` property. The following example passes in an `<Avatar>` component with an `imgAvatar`.

This can be used alongside a text label that provides more context for the image.

Expand Down Expand Up @@ -150,9 +150,9 @@ import CogIcon from '@patternfly/react-icons/dist/esm/icons/cog-icon';

### Plain toggle with icon

To apply plain styling to a menu toggle with an icon, pass in `variant="plain"`. This will remove the default bottom border and caret. You may pass in an `icon` to serve as the menu toggle. The following example passes in an `EllipsisVIcon`.
To apply plain styling to a menu toggle with an icon, pass in `variant="plain"`. This will remove the default bottom border and caret. You may pass in an `icon` to serve as the menu toggle. The following example passes in an `EllipsisVIcon`.

If the toggle does not have any visible text content, use the `aria-label` property to provide an accessible name.
If the toggle does not have any visible text content, use the `aria-label` property to provide an accessible name.

```ts
import React from 'react';
Expand All @@ -171,6 +171,7 @@ import EllipsisVIcon from '@patternfly/react-icons/dist/esm/icons/ellipsis-v-ico
</MenuToggle>
</React.Fragment>
```

### Plain toggle with text label

To apply plain styling to a menu toggle with a text label, pass in `variant="plainText"`. Unlike the “plain” variant, “plainText” adds a caret pointing down in the toggle.
Expand Down Expand Up @@ -201,27 +202,30 @@ The following example shows a split button with a `<MenuToggleCheckbox>`.
Variant styling can be applied to split button toggles to adjust their appearance for different scenarios. Both "primary" and "secondary" variants can be used with split button toggles.

```ts file='MenuToggleSplitButtonCheckbox.tsx'

```

### Split button toggle with text label

To display text in a split button menu toggle, add a label to the `items` property of `splitButtonOptions`.
To display text in a split button menu toggle, add a label to the `items` property of `splitButtonOptions`.

```ts file='MenuToggleSplitButtonCheckboxWithText.tsx'

```

### Split button toggle with action

To add an action to a split button, pass `variant='action'` into `splitButtonOptions` and add a `<MenuToggleAction>` to the `items` property of `splitButtonOptions`.
To add an action to a split button, pass `variant='action'` into `splitButtonOptions` and add a `<MenuToggleAction>` to the `items` property of `splitButtonOptions`.

Actions may be used with primary and secondary toggle variants.

```ts file='MenuToggleSplitButtonAction.tsx'

```

### Full height toggle

A full height toggle fills the height of its parent. To flag a full height toggle, use the `isFullHeight` property.
A full height toggle fills the height of its parent. To flag a full height toggle, use the `isFullHeight` property.

In the following example, the toggle fills the size of the "80px" `<div>` element that it is within.

Expand All @@ -238,7 +242,7 @@ import { MenuToggle } from '@patternfly/react-core';

### Full width toggle

A full width toggle fills the width of its parent. To flag a full width toggle, use the `isFullWidth` property.
A full width toggle fills the width of its parent. To flag a full width toggle, use the `isFullWidth` property.

In the following example, the toggle fills the width of its parent as the window size changes.

Expand All @@ -249,7 +253,7 @@ import { MenuToggle } from '@patternfly/react-core';
const fullWidth: React.FunctionComponent = () => {
return (
<MenuToggle isFullWidth aria-label="Full width menu toggle" >
Full width
Full width
</MenuToggle>
);
}
Expand All @@ -262,4 +266,15 @@ To create a typeahead toggle, pass in `variant="typeahead"` to the `<MenuToggle>
To create a multiple typeahead toggle, pass a `<TextInputGroup>` component implemented like the [text input group's filter example](/components/text-input-group#filters) as a child of `<MenuToggle>`.

```ts file='MenuToggleTypeahead.tsx'

```

### Status toggle

To create a toggle with a status, pass in the `status` property to the `MenuToggle`. The default icon associated with each status may be overridden by using the `statusIcon` property.

When the status value is "warning" or "danger", you must include helper text that conveys what is causing the warning/error.

```ts isBeta file='MenuToggleStatus.tsx'

```
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react';
import { MenuToggle, HelperText, HelperTextItem } from '@patternfly/react-core';

export const MenuToggleStatus: React.FunctionComponent = () => (
<React.Fragment>
<MenuToggle status="success">Success</MenuToggle>
<br />
<br />
<MenuToggle status="warning">Warning</MenuToggle>
<HelperText>
<HelperTextItem variant="warning">Warning text that explains the issue.</HelperTextItem>
</HelperText>
<br />
<br />
<MenuToggle status="danger">Danger</MenuToggle>
<HelperText>
<HelperTextItem variant="error">Danger text that explains the issue.</HelperTextItem>
</HelperText>
</React.Fragment>
);
39 changes: 28 additions & 11 deletions packages/react-core/src/components/Select/examples/Select.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Select builds off of the menu component suite to adapt commonly used properties
For use cases that do not require a lot of customization, there are various template components available to use in the `@react-templates` package. These templates have a streamlined API and logic, making them easier to set up and use, but are limited in scope and flexibility. See the [templates page](/components/menus/select/react-templates) for details.

### Single select

To let users select a single item from a list, use a single select menu.

You can add multiple `<SelectOption>` components to build out a list of menu items. For each select option, pass a relevant option label to the `value` property.
Expand All @@ -30,30 +31,42 @@ To disable the select menu toggle, use the `isDisabled` property. In the followi

### Select option variants

The following example showcases different option variants and customizations that are commonly used in a select menu.
The following example showcases different option variants and customizations that are commonly used in a select menu.

To create these variants, you can pass different properties into a `<SelectOption>` component.
To create these variants, you can pass different properties into a `<SelectOption>` component.

This example provides examples of:
This example provides examples of:

- An option with a description, which is created by using the `description` property.
- An option with a link, which is created by passing a URL into the `to` property. For external links, use the `isExternalLink` property so that the option is styled with an outbound link icon.
- An option with an icon, which is created by using the `icon` property.
- An option that is disabled by using the `isDisabled` property.
- An option with a link, which is created by passing a URL into the `to` property. For external links, use the `isExternalLink` property so that the option is styled with an outbound link icon.
- An option with an icon, which is created by using the `icon` property.
- An option that is disabled by using the `isDisabled` property.

```ts file="./SelectOptionVariations.tsx"

```

### With grouped items

To group related select options together, use 1 or more `<SelectGroup>` components and title each group using the `label` property.

```ts file="./SelectGrouped.tsx"

```

### With validation

To create a toggle with a status, pass in `status` to the `MenuToggle`. The default icon associated with each status may be overridden by using the `statusIcon` property.

When the status value is "warning" or "danger", you must include helper text that conveys what is causing the warning/error.

```ts isBeta file="./SelectValidated.tsx"

```

### Checkbox select
To let users select multiple list options via checkbox input, use a checkbox select.

To let users select multiple list options via checkbox input, use a checkbox select.

To create a checkbox select, pass `role="menu"` to the `<Select>`, and `hasCheckbox` into each `<SelectOption>` component. To indicate that an option is selected, use the `isSelected` property.

Expand All @@ -64,6 +77,7 @@ By default, the menu toggle will display a badge to indicate the number of items
```

### Typeahead

Typeahead is a select variant that replaces the typical button toggle for opening the select menu with a text input and button toggle combo. As a user enters characters into the text input, the menu options will be filtered to match.

To make a typeahead, pass `variant=typeahead` into the `<MenuToggle>` component and link an `onClick` function to the `<TextInputGroupMain>` component.
Expand All @@ -73,7 +87,8 @@ To make a typeahead, pass `variant=typeahead` into the `<MenuToggle>` component
```

### Typeahead with create option
If a user enters a value into a typeahead select menu that does not exist, you can allow them to create an option of that value.

If a user enters a value into a typeahead select menu that does not exist, you can allow them to create an option of that value.

To enable the creation ability, pass a predetermined `value` into a `<SelectOption>` component. You can use the `placeholder` property to change the default text shown in the text input.

Expand All @@ -84,6 +99,7 @@ The following example outlines the code implementation required to create a work
```

### Multiple typeahead with chips

A multiple typeahead can be used to allow users to select multiple options from a list. Additionally, you can render a chip group to be placed in the select toggle.

When more items than the allowed limit are selected, overflowing items will be hidden under a "more" button. The following example hides items after more than 3 are selected. To show hidden items, select the “more” button. Select "show less" to hide extra items again.
Expand All @@ -93,20 +109,22 @@ When more items than the allowed limit are selected, overflowing items will be h
```

### Multiple typeahead with create option

If the text that is entered into a typeahead doesn't match a menu item, users can choose to create a new option that matches the text input. You can also combine this create functionality with a chip group to display created items as chips."

```ts file="./SelectMultiTypeaheadCreatable.tsx"

```

### Multiple typeahead with checkboxes

By default, a multiple typeahead select allows you to select multiple menu items, placing a checkmark beside selected items. Like basic checkbox select menus, you can add checkboxes to your menu items. This approach may be more accurate and comprehensive for more complex menu scenarios like filtering.

```ts file="./SelectMultiTypeaheadCheckbox.tsx"

```

### With view more
### With view more

To reduce the processing load for long select lists, you can add a "View more" action to load additional items.

Expand All @@ -120,7 +138,6 @@ The following example shows 3 items before the "View more" link, but you can adj

You can add a `<MenuFooter>` component to a select menu to hold additional actions that users can take on menu items, through elements such as link buttons. A footer will be placed beneath a divider at the end of the select menu.


```ts file="./SelectFooter.tsx"

```
```
Loading

0 comments on commit 2afe1bf

Please sign in to comment.