diff --git a/packages/patternfly-4/react-core/src/experimental/components/DataToolbar/DataToolbar.md b/packages/patternfly-4/react-core/src/experimental/components/DataToolbar/DataToolbar.md new file mode 100644 index 00000000000..bacc94003b9 --- /dev/null +++ b/packages/patternfly-4/react-core/src/experimental/components/DataToolbar/DataToolbar.md @@ -0,0 +1,800 @@ +--- +title: 'DataToolbar' +cssPrefix: 'pf-c-data-toolbar' +typescript: true +propComponents: ['DataToolbar', 'DataToolbarContent', 'DataToolbarItem', 'DataToolbarGroup', 'DataToolbarToggleGroup', 'DataToolbarBreakpointMod', 'DataToolbarSpacer'] +section: 'experimental' +stage: 'early' +--- + +import { DataToolbar , DataToolbarItem, DataToolbarGroup, DataToolbarContent, DataToolbarToggleGroup } from '@patternfly/react-core/dist/esm/experimental'; +import { Alert, Button, InputGroup, TextInput, Select, SelectOption } from '@patternfly/react-core'; +import { EditIcon, CloneIcon, SyncIcon, SearchIcon, FilterIcon } from '@patternfly/react-icons' +import '@patternfly/react-styles/css/components/Divider/divider'; + +## Data toolbar +```js +import React from 'react'; +import { DataToolbar , DataToolbarItem, DataToolbarContent } from '@patternfly/react-core/dist/esm/experimental'; +import { Button, InputGroup, TextInput, SearchIcon } from '@patternfly/react-core'; + +class DataToolbarItems extends React.Component { + constructor(props) { + super(props); + } + + render() { + const items = + + + + + + + + + + ; + + return {items}; + } +} + +``` + +## Data toolbar spacers +```js +import React from 'react'; +import { DataToolbar , DataToolbarItem, DataToolbarGroup, DataToolbarContent } from '@patternfly/react-core/dist/esm/experimental'; +import { Button } from '@patternfly/react-core'; + +class DataToolbarSpacers extends React.Component { + constructor(props) { + super(props); + } + + render() { + const firstSpacers = [ + {spacerSize: 'none'} + ]; + const secondSpacers = [ + {spacerSize: 'sm'} + ]; + const thirdSpacers = [ + {spacerSize: 'md'} + ]; + const fourthSpacers = [ + {spacerSize: 'lg'} + ]; + const fifthSpacers = [ + {spacerSize: 'none'}, + {spacerSize: 'sm', breakpoint: 'md'}, + {spacerSize: 'md', breakpoint: 'lg'}, + {spacerSize: 'lg', breakpoint: 'xl'} + ]; + + const items = + + + + + + + + + + + + + ; + + return {items}; + } +} + +``` + +## Data toolbar group types +```js +import React from 'react'; +import { DataToolbar, DataToolbarContent, DataToolbarGroup, DataToolbarItem } from '@patternfly/react-core/dist/esm/experimental'; +import { Button, Select, SelectOption } from '@patternfly/react-core'; +import { EditIcon, CloneIcon, SyncIcon } from '@patternfly/react-icons' + +class DataToolbarGroupTypes extends React.Component { + constructor(props) { + super(props); + + this.firstOptions = [ + { value: 'Filter 1', disabled: false, isPlaceholder: true }, + { value: 'A', disabled: false }, + { value: 'B', disabled: false }, + { value: 'C', disabled: false }, + ]; + + this.secondOptions = [ + { value: 'Filter 2', disabled: false, isPlaceholder: true }, + { value: '1', disabled: false }, + { value: '2', disabled: false }, + { value: '3', disabled: false }, + ]; + + this.thirdOptions = [ + { value: 'Filter 3', disabled: false, isPlaceholder: true }, + { value: 'I', disabled: false }, + { value: 'II', disabled: false }, + { value: 'III', disabled: false }, + ]; + + this.state = { + firstIsExpanded: false, + firstSelected: null, + secondIsExpanded: false, + secondSelected: null, + thirdIsExpanded: false, + thirdSelected: null, + }; + + this.onFirstToggle = isExpanded => { + this.setState({ + firstIsExpanded: isExpanded + }); + }; + + this.onFirstSelect = (event, selection) => { + this.setState({ + firstSelected: selection, + firstIsExpanded: false + }); + }; + + this.onSecondToggle = isExpanded => { + this.setState({ + secondIsExpanded: isExpanded + }); + }; + + this.onSecondSelect = (event, selection) => { + this.setState({ + secondSelected: selection, + secondIsExpanded: false + }); + }; + + this.onThirdToggle = isExpanded => { + this.setState({ + thirdIsExpanded: isExpanded + }); + }; + + this.onThirdSelect = (event, selection) => { + this.setState({ + thirdSelected: selection, + thirdIsExpanded: false + }); + }; + + } + + render() { + const { firstIsExpanded, firstSelected, secondIsExpanded, secondSelected, thirdIsExpanded, thirdSelected } = this.state; + + const filterGroupItems = + + + + + + + + + + ; + + const iconButtonGroupItems = + + + + ; + + const buttonGroupItems = + + + + ; + + const items = + {filterGroupItems} + {iconButtonGroupItems} + {buttonGroupItems} + ; + + return {items}; + } +} + +``` + +## Data toolbar toggle groups +- The Toggle group can either have the toggle state managed by the consumer, or the component. The first Toggle group example demonstrates a component managed toggle state. +- The second Toggle group example below demonstrates a consumer managed toggle state. If the consumer would prefer to manage the expanded state of the toggle group for smaller screen widths: + + 1. Add a toggleIsExpanded callback to DataToolbar + 1. Pass in a boolean into the isExpanded prop to DataToolbar + +- Note: Although the toggle group is aware of the consumer provided breakpoint, the expandable content is not. So if the expandable content is expanded and the screen width surpasses that of the breakpoint, then the expandable content will not know that and will remain open, this case should be considered and handled by the consumer as well. + +### Data toolbar component managed toggle groups +```js +import React from 'react'; +import { DataToolbar , DataToolbarItem, DataToolbarContent, DataToolbarToggleGroup, DataToolbarGroup } from '@patternfly/react-core/dist/esm/experimental'; +import { Button, InputGroup, Select, SelectOption } from '@patternfly/react-core'; +import { TextInput, SearchIcon, FilterIcon } from '@patternfly/react-icons' + +class DataToolbarComponentMangedToggleGroup extends React.Component { + constructor(props) { + super(props); + this.state = { + inputValue: "", + statusIsExpanded: false, + statusSelected: null, + riskIsExpanded: false, + riskSelected: null + }; + + this.statusOptions = [ + { value: 'Status', disabled: false, isPlaceholder: true }, + { value: 'New', disabled: false }, + { value: 'Pending', disabled: false }, + { value: 'Running', disabled: false }, + { value: 'Cancelled', disabled: false }, + ]; + + this.riskOptions = [ + { value: 'Risk', disabled: false, isPlaceholder: true }, + { value: 'Low', disabled: false }, + { value: 'Medium', disabled: false }, + { value: 'High', disabled: false }, + ]; + + this.onInputChange = (newValue) => { + this.setState({inputValue: newValue}); + }; + + this.onStatusToggle = isExpanded => { + this.setState({ + statusIsExpanded: isExpanded + }); + }; + + this.onStatusSelect = (event, selection, isPlaceholder) => { + if (isPlaceholder) this.clearStatusSelection(); + this.setState({ + statusSelected: selection, + statusIsExpanded: false + }); + }; + + this.clearStatusSelection = () => { + this.setState({ + statusSelected: null, + statusIsExpanded: false + }); + }; + + this.onRiskToggle = isExpanded => { + this.setState({ + riskIsExpanded: isExpanded + }); + }; + + this.onRiskSelect = (event, selection, isPlaceholder) => { + if (isPlaceholder) this.clearRiskSelection(); + this.setState({ + riskSelected: selection, + riskIsExpanded: false + }); + }; + + this.clearRiskSelection = () => { + this.setState({ + riskSelected: null, + riskIsExpanded: false + }); + }; + } + + render() { + const { inputValue, statusIsExpanded, statusSelected, riskIsExpanded, riskSelected } = this.state; + + const toggleGroupItems = + + + + + + + + + + + + + + + ; + + const items = } breakpoint='xl'>{toggleGroupItems}; + + return {items}; + } +} +``` + +### Data toolbar consumer managed toggle groups +```js +import React from 'react'; +import { DataToolbar , DataToolbarItem, DataToolbarContent, DataToolbarToggleGroup, DataToolbarGroup } from '@patternfly/react-core/dist/esm/experimental'; +import { Button, InputGroup, Select, SelectOption } from '@patternfly/react-core'; +import { TextInput, SearchIcon, FilterIcon } from '@patternfly/react-icons' + +class DataToolbarConsumerMangedToggleGroup extends React.Component { + constructor(props) { + super(props); + this.state = { + isExpanded: false, + inputValue: "", + statusIsExpanded: false, + statusSelected: null, + riskIsExpanded: false, + riskSelected: null + }; + + this.toggleIsExpanded = () => { + this.setState((prevState) => ({ + isExpanded: !prevState.isExpanded + })); + }; + + this.statusOptions = [ + { value: 'Status', disabled: false, isPlaceholder: true }, + { value: 'New', disabled: false }, + { value: 'Pending', disabled: false }, + { value: 'Running', disabled: false }, + { value: 'Cancelled', disabled: false }, + ]; + + this.riskOptions = [ + { value: 'Risk', disabled: false, isPlaceholder: true }, + { value: 'Low', disabled: false }, + { value: 'Medium', disabled: false }, + { value: 'High', disabled: false }, + ]; + + this.onInputChange = (newValue) => { + this.setState({inputValue: newValue}); + }; + + this.onStatusToggle = isExpanded => { + this.setState({ + statusIsExpanded: isExpanded + }); + }; + + this.onStatusSelect = (event, selection, isPlaceholder) => { + if (isPlaceholder) this.clearStatusSelection(); + this.setState({ + statusSelected: selection, + statusIsExpanded: false + }); + }; + + this.clearStatusSelection = () => { + this.setState({ + statusSelected: null, + statusIsExpanded: false + }); + }; + + this.onRiskToggle = isExpanded => { + this.setState({ + riskIsExpanded: isExpanded + }); + }; + + this.onRiskSelect = (event, selection, isPlaceholder) => { + if (isPlaceholder) this.clearRiskSelection(); + this.setState({ + riskSelected: selection, + riskIsExpanded: false + }); + }; + + this.clearRiskSelection = () => { + this.setState({ + riskSelected: null, + riskIsExpanded: false + }); + }; + } + + render() { + const { isExpanded, inputValue, statusIsExpanded, statusSelected, riskIsExpanded, riskSelected } = this.state; + + const toggleGroupItems = + + + + + + + + + + + + + + + ; + + const items = } breakpoint='xl'>{toggleGroupItems}; + + return {items}; + } +} +``` + +## Data toolbar group stacked +```js +import React from 'react'; +import { DataToolbar, DataToolbarContent, DataToolbarToggleGroup, DataToolbarGroup, DataToolbarItem } from '@patternfly/react-core/dist/esm/experimental'; +import { Button, Select, SelectOption, Pagination, Dropdown, DropdownToggle, DropdownToggleCheckbox, DropdownItem } from '@patternfly/react-core'; +import { FilterIcon, CloneIcon, SyncIcon } from '@patternfly/react-icons' + +class DataToolbarStacked extends React.Component { + constructor(props) { + super(props); + + // toggle group - three option menus with labels, two icon buttons, Kebab menu - right aligned + // pagination - right aligned + this.resourceOptions = [ + { value: 'All', disabled: false }, + { value: 'Deployment', disabled: false }, + { value: 'Pod', disabled: false }, + ]; + + this.statusOptions = [ + { value: 'Running', disabled: false, }, + { value: 'New', disabled: false }, + { value: 'Pending', disabled: false }, + { value: 'Cancelled', disabled: false }, + ]; + + this.typeOptions = [ + { value: 'Any', disabled: false, isPlaceholder: true }, + { value: 'No Type', disabled: false }, + ]; + + this.state = { + resourceIsExpanded: false, + resourceSelected: null, + statusIsExpanded: false, + statusSelected: null, + typeIsExpanded: false, + typeSelected: null, + splitButtonDropdownIsOpen: false, + page: 1, + perPage: 20 + }; + + this.onResourceToggle = isExpanded => { + this.setState({ + resourceIsExpanded: isExpanded + }); + }; + + this.onResourceSelect = (event, selection) => { + this.setState({ + resourceSelected: selection, + resourceIsExpanded: false + }); + }; + + this.onStatusToggle = isExpanded => { + this.setState({ + statusIsExpanded: isExpanded + }); + }; + + this.onStatusSelect = (event, selection) => { + this.setState({ + statusSelected: selection, + statusIsExpanded: false + }); + }; + + this.onTypeToggle = isExpanded => { + this.setState({ + typeIsExpanded: isExpanded + }); + }; + + this.onTypeSelect = (event, selection) => { + this.setState({ + typeSelected: selection, + typeIsExpanded: false + }); + }; + + this.onSetPage = (_event, pageNumber) => { + this.setState({ + page: pageNumber + }); + }; + + this.onPerPageSelect = (_event, perPage) => { + this.setState({ + perPage + }); + }; + + this.onSplitButtonToggle = isOpen => { + console.log("hm"); + this.setState({ + splitButtonDropdownIsOpen: isOpen + }); + }; + + this.onSplitButtonSelect = event => { + this.setState({ + splitButtonDropdownIsOpen: !this.state.splitButtonDropdownIsOpen + }); + }; + + } + + render() { + const { resourceIsExpanded, resourceSelected, statusIsExpanded, statusSelected, typeIsExpanded, typeSelected, splitButtonDropdownIsOpen } = this.state; + + const splitButtonDropdownItems = [ + Link, + + Action + , + + Disabled Link + , + + Disabled Action + + ]; + + const toggleGroupItems = + Resource + + + + Status + + + + Type + + + + ; + + const iconButtonGroupItems = + + + ; + + + const firstRowItems = + } breakpoint='xl'>{toggleGroupItems} + {iconButtonGroupItems} + Overflow Menu + ; + + const secondRowItems = + + + ]} + onToggle={this.onSplitButtonToggle} + /> + )} + isOpen={splitButtonDropdownIsOpen} + dropdownItems={splitButtonDropdownItems} + /> + + + + + ; + + return + {firstRowItems} +
+ {secondRowItems} +
; + } +} + +``` + diff --git a/packages/patternfly-4/react-core/src/experimental/components/DataToolbar/DataToolbar.tsx b/packages/patternfly-4/react-core/src/experimental/components/DataToolbar/DataToolbar.tsx new file mode 100644 index 00000000000..649a4741ebe --- /dev/null +++ b/packages/patternfly-4/react-core/src/experimental/components/DataToolbar/DataToolbar.tsx @@ -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 { + /** 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 */ + 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 { + private expandableContentRef = React.createRef(); + + 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 ( +
+ + {children} + + +
+ ); + } + +} diff --git a/packages/patternfly-4/react-core/src/experimental/components/DataToolbar/DataToolbarContent.tsx b/packages/patternfly-4/react-core/src/experimental/components/DataToolbar/DataToolbarContent.tsx new file mode 100644 index 00000000000..b870ffc36d8 --- /dev/null +++ b/packages/patternfly-4/react-core/src/experimental/components/DataToolbar/DataToolbarContent.tsx @@ -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 { + /** 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 = ({ + className, + children, + breakpointMods = [] as DataToolbarBreakpointMod[], + ...props + }: DataToolbarContentProps) => { + + return ( +
+ {children} +
+ ); +}; diff --git a/packages/patternfly-4/react-core/src/experimental/components/DataToolbar/DataToolbarExpandableContent.tsx b/packages/patternfly-4/react-core/src/experimental/components/DataToolbar/DataToolbarExpandableContent.tsx new file mode 100644 index 00000000000..ee6107f53e7 --- /dev/null +++ b/packages/patternfly-4/react-core/src/experimental/components/DataToolbar/DataToolbarExpandableContent.tsx @@ -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 { + /** 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; +} + +export const DataToolbarExpandableContent: React.FunctionComponent = ({ + className, + isExpanded = false, + expandableContentRef, + ...props + }: DataToolbarExpandableContentProps) => { + + return ( +
+ ); +}; diff --git a/packages/patternfly-4/react-core/src/experimental/components/DataToolbar/DataToolbarGroup.tsx b/packages/patternfly-4/react-core/src/experimental/components/DataToolbar/DataToolbarGroup.tsx new file mode 100644 index 00000000000..2163294c777 --- /dev/null +++ b/packages/patternfly-4/react-core/src/experimental/components/DataToolbar/DataToolbarGroup.tsx @@ -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 { + /** 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 = ({ + breakpointMods = [] as DataToolbarBreakpointMod[], + spacers = [] as DataToolbarSpacer[], + itemSpacers = [] as DataToolbarSpacer[], + className, + variant, + children, + ...props + }: DataToolbarGroupProps) => { + + return ( +
+ {children} +
+ ); + +}; diff --git a/packages/patternfly-4/react-core/src/experimental/components/DataToolbar/DataToolbarItem.tsx b/packages/patternfly-4/react-core/src/experimental/components/DataToolbar/DataToolbarItem.tsx new file mode 100644 index 00000000000..7d9e2724270 --- /dev/null +++ b/packages/patternfly-4/react-core/src/experimental/components/DataToolbar/DataToolbarItem.tsx @@ -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 { + /** 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 = ({ + className, + variant, + breakpointMods = [] as DataToolbarBreakpointMod[], + spacers = [] as DataToolbarSpacer[], + id, + children, + ...props + }: DataToolbarItemProps) => { + + const labelVariant = variant === 'label'; + + return ( +
+ {children} +
+ ); +}; diff --git a/packages/patternfly-4/react-core/src/experimental/components/DataToolbar/DataToolbarToggleGroup.tsx b/packages/patternfly-4/react-core/src/experimental/components/DataToolbar/DataToolbarToggleGroup.tsx new file mode 100644 index 00000000000..e9fa70fc0db --- /dev/null +++ b/packages/patternfly-4/react-core/src/experimental/components/DataToolbar/DataToolbarToggleGroup.tsx @@ -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 { + + static defaultProps = { + breakpointMods: [] as DataToolbarBreakpointMod[], + spacers: [] as DataToolbarSpacer[], + }; + + render() { + const { toggleIcon, breakpoint, variant, breakpointMods, spacers, className, children, ...props } = this.props; + + return ( + + {({ isExpanded, toggleIsExpanded, expandableContentRef, expandableContentId}) => { + return ( +
+
+ +
+ {isExpanded ? ReactDOM.createPortal(children, expandableContentRef.current) : children} +
+ ); + }} +
+ ); + } +} diff --git a/packages/patternfly-4/react-core/src/experimental/components/DataToolbar/DataToolbarUtils.tsx b/packages/patternfly-4/react-core/src/experimental/components/DataToolbar/DataToolbarUtils.tsx new file mode 100644 index 00000000000..c551a53d132 --- /dev/null +++ b/packages/patternfly-4/react-core/src/experimental/components/DataToolbar/DataToolbarUtils.tsx @@ -0,0 +1,40 @@ +import { getModifier } from '@patternfly/react-styles'; +import styles from '@patternfly/react-styles/css/components/DataToolbar/data-toolbar'; +import * as React from 'react'; +import { RefObject } from 'react'; + +interface DataToolbarContextProps { + isExpanded: boolean; + toggleIsExpanded: () => void; + expandableContentRef: RefObject; + expandableContentId: string; +} + +export const DataToolbarContext = React.createContext>({}); + +export type DataToolbarBreakpointMod = { + /** The attribute to modify */ + modifier: 'hidden' | 'visible' | 'align-right' | 'align-left'; + /** The breakpoint at which to apply the modifier */ + breakpoint: 'md' | 'lg' | 'xl' | '2xl'; +}; + +export const formatBreakpointMods = (breakpointMods: DataToolbarBreakpointMod[]) => { + return breakpointMods.reduce((acc, curr) => ( + `${acc} ${getModifier(styles, `${curr.modifier}${curr.breakpoint ? `-on-${curr.breakpoint}` : ''}`)}` + ), ''); +}; + +export type DataToolbarSpacer = { + /** The size of the spacer */ + spacerSize: 'none' | 'sm' | 'md' | 'lg'; + /** The breakpoint at which to apply the spacer */ + breakpoint?: 'md' | 'lg' | 'xl'; +}; + +export const formatSpacers = (spacers: DataToolbarSpacer[], type = 'pf-m-spacer') => { + + return spacers.reduce((acc, curr) => ( + `${acc} ${type}-${curr.spacerSize}${curr.breakpoint ? `-on-${curr.breakpoint}` : ''}` + ), ''); +}; diff --git a/packages/patternfly-4/react-core/src/experimental/components/DataToolbar/index.ts b/packages/patternfly-4/react-core/src/experimental/components/DataToolbar/index.ts new file mode 100644 index 00000000000..13954bc8158 --- /dev/null +++ b/packages/patternfly-4/react-core/src/experimental/components/DataToolbar/index.ts @@ -0,0 +1,5 @@ +export * from './DataToolbar'; +export * from './DataToolbarContent'; +export * from './DataToolbarGroup'; +export * from './DataToolbarItem'; +export * from './DataToolbarToggleGroup'; diff --git a/packages/patternfly-4/react-core/src/experimental/components/index.ts b/packages/patternfly-4/react-core/src/experimental/components/index.ts index 065a721dc37..bece9bcf754 100644 --- a/packages/patternfly-4/react-core/src/experimental/components/index.ts +++ b/packages/patternfly-4/react-core/src/experimental/components/index.ts @@ -1,2 +1,3 @@ +export * from './DataToolbar'; export * from './Drawer'; export * from './Spinner'; diff --git a/packages/patternfly-4/react-docs/gatsby-browser.js b/packages/patternfly-4/react-docs/gatsby-browser.js index 9d36bfed315..b13bd7b4e8f 100644 --- a/packages/patternfly-4/react-docs/gatsby-browser.js +++ b/packages/patternfly-4/react-docs/gatsby-browser.js @@ -6,4 +6,4 @@ import './static/base.css'; // eslint-disable-line import/no-unresolved import '@patternfly-safe/patternfly/patternfly.css'; // Add experimental styles here and in eslintrc.js for the moment. Once they have been moved out of experimental remove the CSS here and in .eslintrc.js import '@patternfly-safe/patternfly/components/Drawer/drawer.css'; -import '@patternfly-safe/patternfly/components/Spinner/spinner.css'; +import '@patternfly-safe/patternfly/components/DataToolbar/data-toolbar.css';