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

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import * as React from 'react';
import styles from '@patternfly/react-styles/css/components/DataToolbar/data-toolbar';
import { css } from '@patternfly/react-styles';
import { DataToolbarExpandableContent } from './DataToolbarExpandableContent';
import { DataToolbarContext } from './DataToolbarUtils';

export interface DataToolbarProps extends React.HTMLProps<HTMLDivElement> {
/** Classes applied to root element of the Data toolbar */
className?: string;
/** Content to be rendered as rows in the Data toolbar */
children?: React.ReactNode;
/** Flag indicating if a Data toolbar toggle group's expandable content is expanded */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add a comment that setting this means that the consumer will be responsible for managing the toogle group expansion. Also how would a consumer find out if the toogle group is currently expanded after construction??? Can we use a getter to return the correct value.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the consumer is managing the expansion, they would already know, right? If the consumer is not managing the expansion, why do they need to be able to find out if it's currently expanded?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is some documentation just before the toggle group examples that i believe explains this? should I add more info to that documentation?

isExpanded?: boolean;
/** A callback for setting the isExpanded flag */
toggleIsExpanded?: () => void;
/** Id of the Data toolbar */
id: string;
}

export interface DataToolbarState {
/** Flag indicating the if the expandable content's expanded state is consumer managed or not */
isConsumerManagedToggleGroup: boolean;
/** Flag indicating if the component managed state has expanded content or not */
componentManagedIsExpanded: boolean;
}

export class DataToolbar extends React.Component<DataToolbarProps, DataToolbarState> {
private expandableContentRef = React.createRef<HTMLDivElement>();

static defaultProps = {
isExpanded: false
};

constructor(props: DataToolbarProps) {
super(props);

this.state = {
isConsumerManagedToggleGroup: props.isExpanded || !!props.toggleIsExpanded,
componentManagedIsExpanded: false
};
}

toggleIsExpanded = () => {
this.setState((prevState) => ({
componentManagedIsExpanded: !prevState.componentManagedIsExpanded
}));
}

closeExpandableContent = () => {
this.setState(() => ({
componentManagedIsExpanded: false
}));
}

componentDidMount() {
const { isConsumerManagedToggleGroup } = this.state;

if ( !isConsumerManagedToggleGroup ) {
window.addEventListener('resize', this.closeExpandableContent);
}
}

componentWillUnmount() {
const { isConsumerManagedToggleGroup } = this.state;
if (isConsumerManagedToggleGroup) {
window.removeEventListener('resize', this.closeExpandableContent);
}
}

render() {

const { className, children, isExpanded, toggleIsExpanded, id, ...props} = this.props;
const { isConsumerManagedToggleGroup, componentManagedIsExpanded } = this.state;

const expandableContentId = `${id}-expandable-content`;

return (
<div className={css(styles.dataToolbar, className)} id={id} {...props}>
<DataToolbarContext.Provider
value={
{
isExpanded: isConsumerManagedToggleGroup ? isExpanded : componentManagedIsExpanded,
toggleIsExpanded: isConsumerManagedToggleGroup ? toggleIsExpanded : this.toggleIsExpanded,
expandableContentRef: this.expandableContentRef,
expandableContentId
}
}
>
{children}
</DataToolbarContext.Provider>
<DataToolbarExpandableContent
id={expandableContentId}
isExpanded={isConsumerManagedToggleGroup ? isExpanded : componentManagedIsExpanded}
expandableContentRef={this.expandableContentRef}
/>
</div>
);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import * as React from 'react';
import styles from '@patternfly/react-styles/css/components/DataToolbar/data-toolbar';
import { css } from '@patternfly/react-styles';

import { DataToolbarBreakpointMod, formatBreakpointMods } from './DataToolbarUtils';

export interface DataToolbarContentProps extends React.HTMLProps<HTMLDivElement> {
/** Classes applied to root element of the Data toolbar content row */
className?: string;
/** An array of objects representing the various modifiers to apply to the content row at various breakpoints */
breakpointMods?: DataToolbarBreakpointMod[];
/** Content to be rendered as children of the content row */
children?: React.ReactNode;
}

export const DataToolbarContent: React.FunctionComponent<DataToolbarContentProps> = ({
className,
children,
breakpointMods = [] as DataToolbarBreakpointMod[],
...props
}: DataToolbarContentProps) => {

return (
<div
className={
css(styles.dataToolbarContent,
formatBreakpointMods(breakpointMods),
className)}
{...props}
>
{children}
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import * as React from 'react';
import styles from '@patternfly/react-styles/css/components/DataToolbar/data-toolbar';
import { css, getModifier } from '@patternfly/react-styles';

import { RefObject } from 'react';

export interface DataToolbarExpandableContentProps extends React.HTMLProps<HTMLDivElement> {
/** Classes added to the root element of the Data toolbar expandable content */
className?: string;
/** Flag indicating the expandable content is expanded */
isExpanded?: boolean;
/** Expandable content reference for passing to Data toolbar children */
expandableContentRef: RefObject<HTMLDivElement>;
}

export const DataToolbarExpandableContent: React.FunctionComponent<DataToolbarExpandableContentProps> = ({
className,
isExpanded = false,
expandableContentRef,
...props
}: DataToolbarExpandableContentProps) => {

return (
<div
className={css(
styles.dataToolbarExpandableContent,
isExpanded && getModifier(styles, 'expanded'),
className)}
ref={expandableContentRef}
{...props}
/>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import * as React from 'react';
import styles from '@patternfly/react-styles/css/components/DataToolbar/data-toolbar';
import { css, getModifier } from '@patternfly/react-styles';

import {
DataToolbarBreakpointMod,
DataToolbarSpacer,
formatBreakpointMods,
formatSpacers
} from './DataToolbarUtils';

export enum DataToolbarGroupVariant {
'filter-group' = 'filter-group',
'icon-button-group' = 'icon-button-group',
'button-group' = 'button-group',
}

export interface DataToolbarGroupProps extends React.HTMLProps<HTMLDivElement> {
/** Classes applied to root element of the Data toolbar group */
className?: string;
/** A type modifier which modifies spacing specifically depending on the type of group */
variant?: DataToolbarGroupVariant | 'filter-group' | 'icon-button-group' | 'button-group';
/** Array of objects representing the various modifiers to apply to the Data toolbar group at various breakpoints */
breakpointMods?: DataToolbarBreakpointMod[];
/** Array of objects representing the various spacers to apply to the Data toolbar group at various breakpoints */
spacers?: DataToolbarSpacer[];
/** Array of objects representing the spacers to apply to the items in this group at various breakpoints */
itemSpacers?: DataToolbarSpacer[];
/** Content to be rendered inside the Data toolbar group */
children?: React.ReactNode;
}

export const DataToolbarGroup: React.FunctionComponent<DataToolbarGroupProps> = ({
breakpointMods = [] as DataToolbarBreakpointMod[],
spacers = [] as DataToolbarSpacer[],
itemSpacers = [] as DataToolbarSpacer[],
className,
variant,
children,
...props
}: DataToolbarGroupProps) => {

return (
<div
className={css(
styles.dataToolbarGroup,
variant && getModifier(styles, variant),
formatBreakpointMods(breakpointMods),
formatSpacers(itemSpacers, 'pf-m-space-items'),
formatSpacers(spacers),
className)}
{...props}
>
{children}
</div>
);

};
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import * as React from 'react';
import styles from '@patternfly/react-styles/css/components/DataToolbar/data-toolbar';
import { css, getModifier } from '@patternfly/react-styles';

import {
DataToolbarBreakpointMod,
DataToolbarSpacer,
formatBreakpointMods,
formatSpacers
} from './DataToolbarUtils';

export enum DataToolbarItemVariant {
separator = 'separator',
'bulk-select' = 'bulk-select',
'overflow-menu' = 'overflow-menu',
pagination = 'pagination',
'search-filter' = 'search-filter',
label = 'label',
}

export interface DataToolbarItemProps extends React.HTMLProps<HTMLDivElement> {
/** Classes applied to root element of the Data toolbar item */
className?: string;
/** A type modifier which modifies spacing specifically depending on the type of item */
variant?: DataToolbarItemVariant |
'separator' | 'bulk-select' | 'overflow-menu' | 'pagination' | 'search-filter' | 'label';
/** An array of objects representing the various modifiers to apply to the Data toolbar item at various breakpoints */
breakpointMods?: DataToolbarBreakpointMod[];
/** An array of objects representing the various spacers to apply to the Data toolbar item at various breakpoints */
spacers?: DataToolbarSpacer[];
/** id for this Data toolbar item */
id?: string;
/** Content to be rendered inside the Data toolbar item */
children?: React.ReactNode;
}

export const DataToolbarItem: React.FunctionComponent<DataToolbarItemProps> = ({
className,
variant,
breakpointMods = [] as DataToolbarBreakpointMod[],
spacers = [] as DataToolbarSpacer[],
id,
children,
...props
}: DataToolbarItemProps) => {

const labelVariant = variant === 'label';

return (
<div
className={css(
styles.dataToolbarItem,
variant && getModifier(styles, variant),
formatBreakpointMods(breakpointMods),
formatSpacers(spacers),
className)}
{...labelVariant && { 'aria-hidden': true }}
id={id}
{...props}
>
{children}
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import styles from '@patternfly/react-styles/css/components/DataToolbar/data-toolbar';
import { css, getModifier } from '@patternfly/react-styles';
import { DataToolbarGroupProps } from './DataToolbarGroup';
import { DataToolbarContext } from './DataToolbarUtils';
import { Button } from '../../../components/Button';

import {
DataToolbarBreakpointMod,
DataToolbarSpacer,
formatBreakpointMods,
formatSpacers
} from './DataToolbarUtils';

export interface DataToolbarToggleGroupProps extends DataToolbarGroupProps {
/** An Icon to be rendered when the toggle group has collapsed down */
toggleIcon: React.ReactNode;
/** The breakpoint at which the toggle group is collapsed down */
breakpoint: 'md' | 'lg' | 'xl' | '2xl';
}

export class DataToolbarToggleGroup extends React.Component<DataToolbarToggleGroupProps> {

static defaultProps = {
breakpointMods: [] as DataToolbarBreakpointMod[],
spacers: [] as DataToolbarSpacer[],
};

render() {
const { toggleIcon, breakpoint, variant, breakpointMods, spacers, className, children, ...props } = this.props;

return (
<DataToolbarContext.Consumer>
{({ isExpanded, toggleIsExpanded, expandableContentRef, expandableContentId}) => {
return (
<div
className={css(
styles.dataToolbarGroup,
variant && getModifier(styles, variant),
formatBreakpointMods(breakpointMods),
formatSpacers(spacers, 'pf-m-space-items'),
getModifier(styles, 'toggle-group'),
getModifier(styles, `reveal-on-${breakpoint}`),
className)}
{...props}
>
<div className={css(styles.dataToolbarToggle)}>
<Button
variant="plain"
onClick={toggleIsExpanded}
{...isExpanded && { 'aria-expanded': true }}
// TODO aria-haspopup when isExpanded = true && viewport is smaller than lg global breakpoint
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this something we need to implement with this PR, and if not can we open an issue to track it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good thought

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's open an issue for this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue created

aria-controls={expandableContentId}
>
{toggleIcon}
</Button>
</div>
{isExpanded ? ReactDOM.createPortal(children, expandableContentRef.current) : children}
</div>
);
}}
</DataToolbarContext.Consumer>
);
}
}
Loading