Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import styles from '@patternfly/react-styles/css/components/Dropdown/dropdown';
import { css } from '@patternfly/react-styles';
import { PickOptional } from '../../helpers/typeUtils';
import { getOUIAProps, OUIAProps, getDefaultOUIAId } from '../../helpers';
import { Spinner } from '../Spinner';

export interface DropdownToggleCheckboxProps
extends Omit<React.HTMLProps<HTMLInputElement>, 'type' | 'onChange' | 'disabled' | 'checked'>,
Expand All @@ -15,6 +16,8 @@ export interface DropdownToggleCheckboxProps
isDisabled?: boolean;
/** Flag to show if the checkbox is checked */
isChecked?: boolean | null;
/** @beta Flag to show if the checkbox is in progress */
isInProgress?: boolean | null;
/** Alternate Flag to show if the checkbox is checked */
checked?: boolean | null;
/** A callback for when the checkbox selection changes */
Expand All @@ -25,6 +28,10 @@ export interface DropdownToggleCheckboxProps
id: string;
/** Aria-label of the checkbox */
'aria-label': string;
/** @beta Text describing current loading status or progress */
defaultProgressAriaValueText?: string;
/** @beta Aria-label for the default progress icon to describe what is loading */
defaultProgressAriaLabel?: string;
/** Value to overwrite the randomly generated data-ouia-component-id.*/
ouiaId?: number | string;
/** Set the value of data-ouia-safe. Only set to true when the component is in a static state, i.e. no animations are occurring. At all other times, this value must be false. */
Expand Down Expand Up @@ -68,6 +75,9 @@ export class DropdownToggleCheckbox extends React.Component<DropdownToggleCheckb
isValid,
isDisabled,
isChecked,
isInProgress = false,
defaultProgressAriaLabel,
defaultProgressAriaValueText = 'Loading...',
children,
ouiaId,
ouiaSafe,
Expand All @@ -77,13 +87,27 @@ export class DropdownToggleCheckbox extends React.Component<DropdownToggleCheckb
/* eslint-enable @typescript-eslint/no-unused-vars */
...props
} = this.props;

const spinner = (
<Spinner
diameter="1em"
isSVG
aria-valuetext={defaultProgressAriaValueText}
aria-live="polite"
aria-label={defaultProgressAriaLabel}
/>
);

const text = children && (
<span className={css(styles.dropdownToggleText, className)} aria-hidden="true" id={`${props.id}-text`}>
{children}
</span>
);
return (
<label className={css(styles.dropdownToggleCheck, className)} htmlFor={props.id}>
<label
className={css(styles.dropdownToggleCheck, isInProgress && styles.modifiers.inProgress, className)}
htmlFor={props.id}
>
<input
{...props}
{...(this.calculateChecked() !== undefined && { onChange: this.handleChange })}
Expand All @@ -98,6 +122,7 @@ export class DropdownToggleCheckbox extends React.Component<DropdownToggleCheckb
ouiaSafe
)}
/>
{isInProgress && <span className={css(styles.dropdownToggleProgress, className)}>{spinner}</span>}
{text}
</label>
);
Expand Down
11 changes: 9 additions & 2 deletions packages/react-core/src/components/Dropdown/examples/Dropdown.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ You may use a `<BadgeToggle>` to display the number of dropdown menu items.
```ts file="./DropdownBadge.tsx"
```

### Split button
### Split button (checkbox)

To combine an action button or other control with a dropdown menu, use a split button. A `<DropdownToggle>` can be rendered as a split button by using the `splitButtonItems` property. Elements to be displayed before the toggle button must be included in `splitButtonItems`.

Expand All @@ -125,13 +125,20 @@ The following example shows a split button with a `<DropdownToggleCheckbox>`, wh
```ts file="./DropdownSplitButton.tsx"
```

### Split button text label
### Split button (checkbox with toggle text)

Text labels may optionally be used alongside actions within split buttons.

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

### Split button (progress checkbox)

The `isInProgress` property can be used to indicate a progress state by rendering a spinner in place of a checkbox.

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

### Split button toggle variants

Use the `toggleVariant` and `splitButtonVariant` properties to adjust the styling of split button toggles.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export const DropdownSplitButton: React.FunctionComponent = () => {
toggle={
<DropdownToggle
splitButtonItems={[
<DropdownToggleCheckbox id="split-button-checkbox" key="split-checkbox" aria-label="Select all" />
<DropdownToggleCheckbox id="split-button-toggle-checkbox" key="split-checkbox" aria-label="Select all" />
]}
onToggle={onToggle}
id="toggle-split-button"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import React from 'react';
import {
Dropdown,
DropdownToggle,
DropdownToggleCheckbox,
DropdownItem,
DropdownSeparator
} from '@patternfly/react-core';

export const DropdownSplitButtonProgressCheckbox: React.FunctionComponent = () => {
const [isOpen1, setIsOpen1] = React.useState(false);
const [isOpen2, setIsOpen2] = React.useState(false);

const [isChecked1, setIsChecked1] = React.useState(false);
const [isChecked2, setIsChecked2] = React.useState(false);

const [isTimedOut1, setIsTimedOut1] = React.useState(true);
const [isTimedOut2, setIsTimedOut2] = React.useState(true);

const onToggle1 = (isOpen: boolean) => {
setIsOpen1(isOpen);
};

const onToggle2 = (isOpen: boolean) => {
setIsOpen2(isOpen);
};

const showProgressIcon1 = () => {
setIsTimedOut1(false);
setTimeout(() => setIsTimedOut1(true), 3000);
};

const showProgressIcon2 = () => {
setIsTimedOut2(false);
setTimeout(() => setIsTimedOut2(true), 3000);
};

const onFocus1 = () => {
const element = document.getElementById('toggle-split-button-progress');
element.focus();
};

const onFocus2 = () => {
const element = document.getElementById('toggle-split-button-progress-text');
element.focus();
};

const onChange1 = (checked: boolean) => {
setIsChecked1(checked);
showProgressIcon1();
};

const onChange2 = (checked: boolean) => {
setIsChecked2(checked);
showProgressIcon2();
};

const onSelect1 = () => {
setIsOpen1(false);
onFocus1();
};

const onSelect2 = () => {
setIsOpen2(false);
onFocus2();
};

const dropdownItems = [
<DropdownItem key="link">Link</DropdownItem>,
<DropdownItem key="action" component="button">
Action
</DropdownItem>,
<DropdownItem key="disabled link" isDisabled href="www.google.com">
Disabled link
</DropdownItem>,
<DropdownItem
key="disabled action"
isAriaDisabled
component="button"
tooltip="Tooltip for disabled item"
tooltipProps={{ position: 'top' }}
>
Disabled action
</DropdownItem>,
<DropdownSeparator key="separator" />,
<DropdownItem key="separated link">Separated link</DropdownItem>,
<DropdownItem key="separated action" component="button">
Separated action
</DropdownItem>
];

return (
<>
<Dropdown
onSelect={onSelect1}
toggle={
<DropdownToggle
splitButtonItems={[
<DropdownToggleCheckbox
onChange={onChange1}
isChecked={isChecked1}
isInProgress={!isTimedOut1}
id="split-button-progress-toggle-checkbox"
key="split-progress-checkbox"
aria-label="Select all"
/>
]}
onToggle={onToggle1}
id="toggle-split-button-progress"
/>
}
isOpen={isOpen1}
dropdownItems={dropdownItems}
/>{' '}
<Dropdown
onSelect={onSelect2}
toggle={
<DropdownToggle
splitButtonItems={[
<DropdownToggleCheckbox
onChange={onChange2}
isChecked={isChecked2}
isInProgress={!isTimedOut2}
id="split-button-progress-text-checkbox"
key="split-progress-checkbox"
aria-label="Select all"
>
10 selected
</DropdownToggleCheckbox>
]}
onToggle={onToggle2}
id="toggle-split-button-progress-text"
/>
}
isOpen={isOpen2}
dropdownItems={dropdownItems}
/>
</>
);
};