Skip to content

Commit

Permalink
feat(Table): update ActionsColumn to use Dropdown next (#8629)
Browse files Browse the repository at this point in the history
* feat(Table): update ActionsColumn to use Dropdown next

* fix umd build

* update tests

* add aria-label to default kebab

* test

* allow no onSelect, update integration and snap

* add missing memo deps

* fix wizard build
  • Loading branch information
kmcfaul authored Feb 10, 2023
1 parent 8824534 commit 43601af
Show file tree
Hide file tree
Showing 12 changed files with 539 additions and 529 deletions.
8 changes: 6 additions & 2 deletions packages/react-core/src/next/components/Dropdown/Dropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import { css } from '@patternfly/react-styles';
import { Menu, MenuContent, MenuProps } from '../../../components/Menu';
import { Popper } from '../../../helpers/Popper/Popper';
import { Popper, PopperProps } from '../../../helpers/Popper/Popper';
import { useOUIAProps, OUIAProps } from '../../../helpers';

export interface DropdownProps extends MenuProps, OUIAProps {
Expand Down Expand Up @@ -32,6 +32,8 @@ export interface DropdownProps extends MenuProps, OUIAProps {
ouiaSafe?: boolean;
/** z-index of the dropdown menu */
zIndex?: number;
/** Additional properties to pass to the Popper */
popperProps?: PopperProps;
}

const DropdownBase: React.FunctionComponent<DropdownProps> = ({
Expand All @@ -48,6 +50,7 @@ const DropdownBase: React.FunctionComponent<DropdownProps> = ({
ouiaId,
ouiaSafe = true,
zIndex = 9999,
popperProps,
...props
}: DropdownProps) => {
const localMenuRef = React.useRef<HTMLDivElement>();
Expand Down Expand Up @@ -100,7 +103,7 @@ const DropdownBase: React.FunctionComponent<DropdownProps> = ({
<Menu
className={css(className)}
ref={menuRef}
onSelect={(event, itemId) => onSelect(event, itemId)}
onSelect={(event, itemId) => onSelect && onSelect(event, itemId)}
isPlain={isPlain}
isScrollable={isScrollable}
{...(minWidth && {
Expand All @@ -121,6 +124,7 @@ const DropdownBase: React.FunctionComponent<DropdownProps> = ({
appendTo={containerRef.current || undefined}
isVisible={isOpen}
zIndex={zIndex}
{...popperProps}
/>
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion packages/react-core/src/next/components/Select/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ const SelectBase: React.FunctionComponent<SelectProps & OUIAProps> = ({
role={role}
className={css(className)}
ref={menuRef}
onSelect={(event, itemId) => onSelect(event, itemId)}
onSelect={(event, itemId) => onSelect && onSelect(event, itemId)}
isPlain={isPlain}
selected={selected}
{...(minWidth && {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React from 'react';

import { isCustomWizardFooter, isWizardSubStep, WizardStepType, WizardFooterType } from './types';
import { getActiveStep } from './utils';
import { WizardFooter, WizardFooterProps } from './WizardFooter';

export interface WizardContextProps {
Expand Down Expand Up @@ -74,7 +73,8 @@ export const WizardContextProvider: React.FunctionComponent<WizardContextProvide
})),
[initialSteps, currentSteps]
);
const activeStep = React.useMemo(() => getActiveStep(steps, activeStepIndex), [activeStepIndex, steps]);

const activeStep = React.useMemo(() => steps.find(step => step.index === activeStepIndex), [activeStepIndex, steps]);

const close = React.useCallback(() => onClose?.(null), [onClose]);
const goToNextStep = React.useCallback(() => onNext(null, steps), [onNext, steps]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ import AngleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-right-i
import CaretDownIcon from '@patternfly/react-icons/dist/esm/icons/caret-down-icon';

import { KeyTypes } from '../../../helpers/constants';
import { WizardNavProps, WizardBody, WizardStep, WizardStepProps } from '../Wizard';
import { WizardStepType, isWizardSubStep } from './types';
import { WizardNavProps } from './WizardNav';
import { WizardStep, WizardStepProps } from './WizardStep';
import { WizardBody } from './WizardBody';

/**
* Used to toggle between step content, including the body and footer. This is also where the navigation and its expandability is controlled.
Expand Down
3 changes: 0 additions & 3 deletions packages/react-core/src/next/components/Wizard/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,3 @@ export const normalizeStepProps = ({
steps: _steps,
...controlStep
}: WizardStepProps): Omit<WizardStepType, 'index'> => controlStep;

export const getActiveStep = (steps: WizardStepType[], activeStepIndex: number) =>
steps.find(step => step.index === activeStepIndex);
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ describe('Table Simple Actions Test', () => {
});

it('Verify dropdown toggle', () => {
cy.get('td .pf-c-dropdown__toggle')
cy.get('td .pf-c-menu-toggle')
.first()
.should('exist');
cy.get('td .pf-c-dropdown__toggle')
cy.get('td .pf-c-menu-toggle')
.first()
.click();
cy.get('.pf-c-dropdown__menu').should('exist');
cy.get('.pf-c-dropdown__menu-item')
cy.get('.pf-c-menu').should('exist');
cy.get('.pf-c-menu__item')
.first()
.click();
});
Expand Down
195 changes: 92 additions & 103 deletions packages/react-table/src/components/Table/ActionsColumn.tsx
Original file line number Diff line number Diff line change
@@ -1,71 +1,59 @@
import * as React from 'react';
import { Dropdown } from '@patternfly/react-core/dist/esm/components/Dropdown';
import { KebabToggle } from '@patternfly/react-core/dist/esm/components/Dropdown/KebabToggle';
import { DropdownItem } from '@patternfly/react-core/dist/esm/components/Dropdown/DropdownItem';
import { DropdownSeparator } from '@patternfly/react-core/dist/esm/components/Dropdown/DropdownSeparator';
import { Button } from '@patternfly/react-core/dist/esm/components/Button/Button';

import {
DropdownDirection,
DropdownPosition
} from '@patternfly/react-core/dist/esm/components/Dropdown/dropdownConstants';

import { Dropdown, DropdownItem, DropdownList } from '@patternfly/react-core/dist/esm/next/components';
import { Button } from '@patternfly/react-core/dist/esm/components/Button';
import { Divider } from '@patternfly/react-core/dist/esm/components/Divider';
import { MenuToggle } from '@patternfly/react-core/dist/esm/components/MenuToggle';
import { IAction, IExtraData, IRowData } from './TableTypes';
import EllipsisVIcon from '@patternfly/react-icons/dist/esm/icons/ellipsis-v-icon';

export interface CustomActionsToggleProps {
onToggle: (isOpen: boolean) => void;
onToggle: (evt: React.MouseEvent) => void;
isOpen: boolean;
isDisabled: boolean;
toggleRef: React.Ref<any>;
}

export interface ActionsColumnProps {
children?: React.ReactNode;
export interface ActionsColumnProps extends Omit<React.HTMLProps<HTMLElement>, 'label'> {
/** Actions to be rendered within or without the action dropdown */
items: IAction[];
/** Indicates whether the actions dropdown is disabled */
isDisabled?: boolean;
menuAppendTo?: HTMLElement | (() => HTMLElement) | 'inline' | 'parent';
dropdownPosition?: DropdownPosition;
dropdownDirection?: DropdownDirection;
/** Data of the row the action dropdown is located */
rowData?: IRowData;
/** Extra data of a row */
extraData?: IExtraData;
/** Custom actions toggle for the actions dropdown */
actionsToggle?: (props: CustomActionsToggleProps) => React.ReactNode;
/** Additional properties for the actions dropdown popper */
popperProps?: any;
/** @hide Forwarded ref */
innerRef?: React.Ref<any>;
}

export interface ActionsColumnState {
isOpen: boolean;
}

export class ActionsColumn extends React.Component<ActionsColumnProps, ActionsColumnState> {
static displayName = 'ActionsColumn';
private toggleRef = React.createRef<HTMLButtonElement>();
static defaultProps = {
children: null as React.ReactNode,
items: [] as IAction[],
dropdownPosition: DropdownPosition.right,
dropdownDirection: DropdownDirection.down,
menuAppendTo: 'inline',
rowData: {} as IRowData,
extraData: {} as IExtraData
};
constructor(props: ActionsColumnProps) {
super(props);
this.state = {
isOpen: false
};
}
const ActionsColumnBase: React.FunctionComponent<ActionsColumnProps> = ({
items,
isDisabled,
rowData,
extraData,
actionsToggle,
popperProps = {
position: 'right',
direction: 'down',
popperMatchesTriggerWidth: false
},
...props
}: ActionsColumnProps) => {
const [isOpen, setIsOpen] = React.useState(false);

onToggle = (isOpen: boolean): void => {
this.setState({
isOpen
});
const onToggle = () => {
setIsOpen(!isOpen);
};

onClick = (
const onActionClick = (
event: React.MouseEvent<any> | React.KeyboardEvent | MouseEvent,
onClick:
| ((event: React.MouseEvent, rowIndex: number | undefined, rowData: IRowData, extraData: IExtraData) => void)
| undefined
): void => {
const { rowData, extraData } = this.props;
// Only prevent default if onClick is provided. This allows href support.
if (onClick) {
event.preventDefault();
Expand All @@ -74,62 +62,61 @@ export class ActionsColumn extends React.Component<ActionsColumnProps, ActionsCo
}
};

render() {
const { isOpen } = this.state;
const {
items,
children,
dropdownPosition,
dropdownDirection,
menuAppendTo,
isDisabled,
rowData,
actionsToggle
} = this.props;

const actionsToggleClone = actionsToggle ? (
actionsToggle({ onToggle: this.onToggle, isOpen, isDisabled })
) : (
<KebabToggle isDisabled={isDisabled} onToggle={this.onToggle} />
);
return (
<React.Fragment>
{items
.filter(item => item.isOutsideDropdown)
// eslint-disable-next-line @typescript-eslint/no-unused-vars
.map(({ title, itemKey, onClick, isOutsideDropdown, ...props }, key) =>
typeof title === 'string' ? (
<Button
onClick={event => onActionClick(event, onClick)}
{...(props as any)}
isDisabled={isDisabled}
key={itemKey || `outside_dropdown_${key}`}
data-key={itemKey || `outside_dropdown_${key}`}
>
{title}
</Button>
) : (
React.cloneElement(title as React.ReactElement, { onClick, isDisabled, ...props })
)
)}

return (
<React.Fragment>
{items
.filter(item => item.isOutsideDropdown)
// eslint-disable-next-line @typescript-eslint/no-unused-vars
.map(({ title, itemKey, onClick, isOutsideDropdown, ...props }, key) =>
typeof title === 'string' ? (
<Button
onClick={event => this.onClick(event, onClick)}
{...(props as any)}
isDisabled={isDisabled}
key={itemKey || `outside_dropdown_${key}`}
data-key={itemKey || `outside_dropdown_${key}`}
>
{title}
</Button>
) : (
React.cloneElement(title as React.ReactElement, { onClick, isDisabled, ...props })
)
)}
<Dropdown
toggle={actionsToggleClone}
position={dropdownPosition}
direction={dropdownDirection}
menuAppendTo={menuAppendTo}
isOpen={isOpen}
dropdownItems={items
<Dropdown
isOpen={isOpen}
onOpenChange={isOpen => setIsOpen(isOpen)}
toggle={toggleRef =>
actionsToggle ? (
actionsToggle({ onToggle, isOpen, isDisabled, toggleRef })
) : (
<MenuToggle
aria-label="Kebab toggle"
ref={toggleRef}
onClick={onToggle}
isExpanded={isOpen}
isDisabled={isDisabled}
variant="plain"
>
<EllipsisVIcon />
</MenuToggle>
)
}
{...(rowData && rowData.actionProps)}
{...props}
popperProps={popperProps}
>
<DropdownList>
{items
.filter(item => !item.isOutsideDropdown)
.map(({ title, itemKey, onClick, isSeparator, ...props }, key) =>
isSeparator ? (
<DropdownSeparator {...props} key={itemKey || key} data-key={itemKey || key} />
<Divider key={itemKey || key} data-key={itemKey || key} />
) : (
<DropdownItem
component="button"
onClick={event => {
this.onClick(event, onClick);
this.onToggle(!isOpen);
onActionClick(event, onClick);
onToggle();
}}
{...props}
key={itemKey || key}
Expand All @@ -139,11 +126,13 @@ export class ActionsColumn extends React.Component<ActionsColumnProps, ActionsCo
</DropdownItem>
)
)}
isPlain
{...(rowData && rowData.actionProps)}
/>
{children}
</React.Fragment>
);
}
}
</DropdownList>
</Dropdown>
</React.Fragment>
);
};

export const ActionsColumn = React.forwardRef((props: ActionsColumnProps, ref: React.Ref<HTMLElement>) => (
<ActionsColumnBase {...props} innerRef={ref} />
));
ActionsColumn.displayName = 'ActionsColumn';
3 changes: 2 additions & 1 deletion packages/react-table/src/components/Table/TableTypes.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { DropdownItemProps } from '@patternfly/react-core/dist/esm/components/Dropdown/DropdownItem';
import { DropdownItemProps } from '@patternfly/react-core/next';
import { formatterValueType, ColumnType, RowType, RowKeyType, HeaderType } from './base';
import { SortByDirection } from './SortColumn';
import {
Expand Down Expand Up @@ -108,6 +108,7 @@ export interface IColumn {
dropdownDirection?: DropdownDirection;
menuAppendTo?: HTMLElement | (() => HTMLElement) | 'inline' | 'parent';
actionsToggle?: (props: CustomActionsToggleProps) => React.ReactNode;
actionsPopperProps?: any;
allRowsSelected?: boolean;
allRowsExpanded?: boolean;
isHeaderSelectDisabled?: boolean;
Expand Down
Loading

0 comments on commit 43601af

Please sign in to comment.