diff --git a/packages/react-core/src/components/Toolbar/examples/Toolbar.md b/packages/react-core/src/components/Toolbar/examples/Toolbar.md index 46b74804b0f..491f1e3503c 100644 --- a/packages/react-core/src/components/Toolbar/examples/Toolbar.md +++ b/packages/react-core/src/components/Toolbar/examples/Toolbar.md @@ -17,464 +17,34 @@ import SearchIcon from '@patternfly/react-icons/dist/esm/icons/search-icon'; Toolbar items are individual components that can be placed inside of a toolbar. Buttons or select lists are examples of items. (Note: This example does not demonstrate the desired responsive behavior of the toolbar. That is handled in later examples.) -```js -import React from 'react'; -import { Toolbar, ToolbarItem, ToolbarContent } from '@patternfly/react-core'; -import { Button, SearchInput } from '@patternfly/react-core'; - -class ToolbarItems extends React.Component { - constructor(props) { - super(props); - } - - render() { - const items = ( - - - - - - - - - - - - - ); - - return ( - - {items} - - ); - } -} +```ts file="./ToolbarItems.tsx" ``` ### Adjusting item spacers -```js -import React from 'react'; -import { Toolbar, ToolbarItem, ToolbarGroup, ToolbarContent } from '@patternfly/react-core'; -import { Button } from '@patternfly/react-core'; - -class ToolbarSpacers extends React.Component { - constructor(props) { - super(props); - } - - render() { - const firstSpacers = { - default: 'spacerNone' - }; - const secondSpacers = { - default: 'spacerSm' - }; - const thirdSpacers = { - default: 'spacerMd' - }; - const fourthSpacers = { - default: 'spacerLg' - }; - const fifthSpacers = { - default: 'spacerNone', - md: 'spacerSm', - lg: 'spacerMd', - xl: 'spacerLg' - }; - const spaceItems = { - lg: 'spaceItemsLg' - }; - - const items = ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ); - - return ( - - {items} - - ); - } -} +```ts file="./ToolbarSpacers.tsx" ``` ### Adjusting item widths -```js -import React from 'react'; -import { Toolbar, ToolbarItem, ToolbarContent } from '@patternfly/react-core'; -import { Button } from '@patternfly/react-core'; - -class ToolbarWidths extends React.Component { - constructor(props) { - super(props); - } - - render() { - const widths = { - default: '100px', - sm: '80px', - md: '150px', - lg: '200px', - xl: '250px', - '2xl': '300px' - }; - - const items = ( - - - - - - ); - - return ( - - {items} - - ); - } -} +```ts file="./ToolbarWidths.tsx" ``` ### Adjusting toolbar inset -```js -import React from 'react'; -import { Toolbar, ToolbarItem, ToolbarGroup, ToolbarContent } from '@patternfly/react-core'; -import { Button } from '@patternfly/react-core'; - -class ToolbarSpacers extends React.Component { - constructor(props) { - super(props); - } - - render() { - const items = ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ); - - return ( - - {items} - - ); - } -} +```ts file="./ToolbarInsets.tsx" ``` ### Sticky -```js -import React from 'react'; -import { Toolbar, ToolbarItem, ToolbarContent, SearchInput, Checkbox } from '@patternfly/react-core'; -import SearchIcon from '@patternfly/react-icons/dist/esm/icons/search-icon'; - -const ToolbarItems = () => { - const [isSticky, setIsSticky] = React.useState(true); - const [showEvenOnly, setShowEvenOnly] = React.useState(true); - const array = [...Array(30).keys()]; - const numbers = showEvenOnly ? array.filter(number => number % 2 === 0) : array; - - return ( - -
- - - - - - - - - - - -
- -
- ); -}; +```ts file="./ToolbarSticky.tsx" ``` ### Groups Often, it makes sense to group sets of like items to create desired associations and to enable items to respond together to changes in viewport width. (Note: This example does not demonstrate the desired responsive behavior of the toolbar. That is handled in later examples.) -```js -import React from 'react'; -import { Toolbar, ToolbarContent, ToolbarGroup, ToolbarItem } from '@patternfly/react-core'; -import { Button, Select, SelectOption, SelectVariant } from '@patternfly/react-core'; -import EditIcon from '@patternfly/react-icons/dist/esm/icons/edit-icon'; -import CloneIcon from '@patternfly/react-icons/dist/esm/icons/clone-icon'; -import SyncIcon from '@patternfly/react-icons/dist/esm/icons/sync-icon'; - -class ToolbarGroupTypes 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} - - ); - } -} +```ts file="./ToolbarGroups.tsx" ``` ## Examples with toggle groups and filters @@ -483,146 +53,7 @@ A toggle group can be used when you want to collapse a set of items into an over ### Component managed toggle groups -```js -import React from 'react'; -import { Toolbar, ToolbarItem, ToolbarContent, ToolbarToggleGroup, ToolbarGroup } from '@patternfly/react-core'; -import { Select, SelectOption, SelectVariant, SearchInput } from '@patternfly/react-core'; -import FilterIcon from '@patternfly/react-icons/dist/esm/icons/filter-icon'; - -class ToolbarComponentMangedToggleGroup 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 = ( - - - { - this.onInputChange(''); - }} - /> - - - - - - - - - - - ); - - const items = ( - } breakpoint="xl"> - {toggleGroupItems} - - ); - - return ( - - {items} - - ); - } -} +```ts file="./ToolbarComponentManagedToggleGroups.tsx" ``` ### Consumer managed toggle groups @@ -634,158 +65,7 @@ If the consumer would prefer to manage the expanded state of the toggle group fo - 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. -```js -import React from 'react'; -import { Toolbar, ToolbarItem, ToolbarContent, ToolbarToggleGroup, ToolbarGroup } from '@patternfly/react-core'; -import { Select, SelectOption, SelectVariant, SearchInput } from '@patternfly/react-core'; -import FilterIcon from '@patternfly/react-icons/dist/esm/icons/filter-icon'; - -class ToolbarConsumerManagedToggleGroup 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 = ( - - - { - this.onInputChange(''); - }} - /> - - - - - - - - - - - ); - - const items = ( - } breakpoint="xl"> - {toggleGroupItems} - - ); - - return ( - - {items} - - ); - } -} +```ts file="./ToolbarConsumerManagedToggleGroups.tsx" ``` ### With filters @@ -793,268 +73,7 @@ class ToolbarConsumerManagedToggleGroup extends React.Component { The ToolbarFilter component expects a consumer managed list of applied filters and a delete chip handler to be passed as props. Pass a deleteChipGroup prop to provide both a handler and visual styling to remove all chips in a group. Then the rendering of chips will be handled responsively by the Toolbar When filters are applied, the toolbar will expand in height to make space for a row of filter chips. Upon clearing the applied filters, the toolbar will collapse to its default height. -```js -import React from 'react'; -import { - Toolbar, - ToolbarItem, - ToolbarContent, - ToolbarFilter, - ToolbarToggleGroup, - ToolbarGroup -} from '@patternfly/react-core'; -import { - Button, - Select, - SelectOption, - SelectVariant, - Dropdown, - DropdownItem, - DropdownSeparator, - DropdownPosition, - KebabToggle, - SearchInput -} from '@patternfly/react-core'; -import FilterIcon from '@patternfly/react-icons/dist/esm/icons/filter-icon'; -import EditIcon from '@patternfly/react-icons/dist/esm/icons/edit-icon'; -import CloneIcon from '@patternfly/react-icons/dist/esm/icons/clone-icon'; -import SyncIcon from '@patternfly/react-icons/dist/esm/icons/sync-icon'; - -class ToolbarWithFilterExample extends React.Component { - constructor(props) { - super(props); - this.state = { - isExpanded: false, - inputValue: '', - statusIsExpanded: false, - riskIsExpanded: false, - filters: { - risk: ['Low'], - status: ['New', 'Pending'] - }, - kebabIsOpen: false - }; - - this.toggleIsExpanded = () => { - this.setState(prevState => ({ - isExpanded: !prevState.isExpanded - })); - }; - - this.closeExpandableContent = () => { - this.setState(() => ({ - isExpanded: false - })); - }; - - this.onInputChange = newValue => { - this.setState({ inputValue: newValue }); - }; - - this.onSelect = (type, event, selection) => { - const checked = event.target.checked; - this.setState(prevState => { - const prevSelections = prevState.filters[type]; - return { - filters: { - ...prevState.filters, - [type]: checked ? [...prevSelections, selection] : prevSelections.filter(value => value !== selection) - } - }; - }); - }; - - this.onStatusSelect = (event, selection) => { - this.onSelect('status', event, selection); - }; - - this.onRiskSelect = (event, selection) => { - this.onSelect('risk', event, selection); - }; - - this.onDelete = (type = '', id = '') => { - if (type) { - this.setState(prevState => { - const newState = Object.assign(prevState); - newState.filters[type.toLowerCase()] = newState.filters[type.toLowerCase()].filter(s => s !== id); - return { - filters: newState.filters - }; - }); - } else { - this.setState({ - filters: { - risk: [], - status: [] - } - }); - } - }; - - this.onDeleteGroup = type => { - this.setState(prevState => { - prevState.filters[type.toLowerCase()] = []; - return { - filters: prevState.filters - }; - }); - }; - - this.onStatusToggle = isExpanded => { - this.setState({ - statusIsExpanded: isExpanded - }); - }; - - this.onRiskToggle = isExpanded => { - this.setState({ - riskIsExpanded: isExpanded - }); - }; - - this.onKebabToggle = isOpen => { - this.setState({ - kebabIsOpen: isOpen - }); - }; - } - - componentDidMount() { - window.addEventListener('resize', this.closeExpandableContent); - } - - componentWillUnmount() { - window.removeEventListener('resize', this.closeExpandableContent); - } - - render() { - const { inputValue, filters, statusIsExpanded, riskIsExpanded, kebabIsOpen } = this.state; - - const statusMenuItems = [ - , - , - , - - ]; - - const riskMenuItems = [ - , - , - - ]; - - const toggleGroupItems = ( - - - { - this.onInputChange(''); - }} - /> - - - - - - - - - - - ); - - const dropdownItems = [ - Link, - - Action - , - - Disabled Link - , - - Disabled Action - , - , - Separated Link, - - Separated Action - - ]; - - const toolbarItems = ( - - } breakpoint="xl"> - {toggleGroupItems} - - - - - - - - - - - - - - } - isOpen={kebabIsOpen} - isPlain - dropdownItems={dropdownItems} - position={DropdownPosition.right} - /> - - - ); - - return ( - - {toolbarItems} - - ); - } -} +```ts file="./ToolbarWithFilters.tsx" ``` ### With custom chip group content @@ -1068,286 +87,5 @@ The chip groups generated by toolbar filters may be further customized through t There may be situations where all of the required elements simply cannot fit in a single line. -```js -import React from 'react'; -import { - Button, - KebabToggle, - Select, - SelectOption, - SelectVariant, - Pagination, - Dropdown, - DropdownSeparator, - DropdownToggle, - DropdownToggleCheckbox, - DropdownItem, - DropdownPosition, - Divider, - OverflowMenu, - OverflowMenuContent, - OverflowMenuControl, - OverflowMenuGroup, - OverflowMenuItem, - Toolbar, - ToolbarContent, - ToolbarToggleGroup, - ToolbarItem -} from '@patternfly/react-core'; -import FilterIcon from '@patternfly/react-icons/dist/esm/icons/filter-icon'; - -class ToolbarStacked 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 resources', 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 type', disabled: false, isPlaceholder: true }, - { value: 'No type', disabled: false } - ]; - - this.state = { - kebabIsOpen: false, - resourceIsExpanded: false, - resourceSelected: null, - statusIsExpanded: false, - statusSelected: null, - splitButtonDropdownIsOpen: false, - page: 1, - perPage: 20 - }; - - this.onKebabToggle = isOpen => { - this.setState({ - kebabIsOpen: isOpen - }); - }; - - 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.onSetPage = (_event, pageNumber) => { - this.setState({ - page: pageNumber - }); - }; - - this.onPerPageSelect = (_event, perPage, page) => { - this.setState({ - page, perPage - }); - }; - - this.onSplitButtonToggle = isOpen => { - this.setState({ - splitButtonDropdownIsOpen: isOpen - }); - }; - - this.onSplitButtonSelect = event => { - this.setState({ - splitButtonDropdownIsOpen: !this.state.splitButtonDropdownIsOpen - }); - }; - } - - render() { - const { - kebabIsOpen, - resourceIsExpanded, - resourceSelected, - statusIsExpanded, - statusSelected, - splitButtonDropdownIsOpen - } = this.state; - - const dropdownItems = [ - Link, - - Action - , - - Disabled Link - , - - Disabled Action - , - , - Separated Link, - - Separated Action - - ]; - - const splitButtonDropdownItems = [ - Link, - - Action - , - - Disabled Link - , - - Disabled Action - - ]; - - const toggleGroupItems = ( - - - Resource - - - - - - Status - - - - - - ); - - const firstRowItems = ( - - - - } breakpoint="lg"> - {toggleGroupItems} - - - - - - - - - - - - - - - } - isOpen={kebabIsOpen} - isPlain - dropdownItems={dropdownItems} - position={DropdownPosition.right} - /> - - - - - - - ); - - const secondRowItems = ( - - - - - - ]} - onToggle={this.onSplitButtonToggle} - /> - } - isOpen={splitButtonDropdownIsOpen} - dropdownItems={splitButtonDropdownItems} - /> - - - - - - - - ); - - return ( - - {firstRowItems} - - {secondRowItems} - - ); - } -} +```ts file="./ToolbarStacked.tsx" ``` diff --git a/packages/react-core/src/components/Toolbar/examples/ToolbarComponentManagedToggleGroups.tsx b/packages/react-core/src/components/Toolbar/examples/ToolbarComponentManagedToggleGroups.tsx new file mode 100644 index 00000000000..048775a90c8 --- /dev/null +++ b/packages/react-core/src/components/Toolbar/examples/ToolbarComponentManagedToggleGroups.tsx @@ -0,0 +1,137 @@ +import React from 'react'; +import { + Toolbar, + ToolbarItem, + ToolbarContent, + ToolbarToggleGroup, + ToolbarGroup, + SelectOptionObject +} from '@patternfly/react-core'; +import { Select, SelectOption, SelectVariant, SearchInput } from '@patternfly/react-core'; +import FilterIcon from '@patternfly/react-icons/dist/esm/icons/filter-icon'; + +export const ToolbarComponentManagedToggleGroup: React.FunctionComponent = () => { + const [inputValue, setInputValue] = React.useState(''); + const [statusIsExpanded, setStatusIsExpanded] = React.useState(false); + const [statusSelected, setStatusSelected] = React.useState(); + const [riskIsExpanded, setRiskIsExpanded] = React.useState(false); + const [riskSelected, setRiskSelected] = React.useState(); + + const statusOptions = [ + { value: 'Status', disabled: false, isPlaceholder: true }, + { value: 'New', disabled: false }, + { value: 'Pending', disabled: false }, + { value: 'Running', disabled: false }, + { value: 'Cancelled', disabled: false } + ]; + + const riskOptions = [ + { value: 'Risk', disabled: false, isPlaceholder: true }, + { value: 'Low', disabled: false }, + { value: 'Medium', disabled: false }, + { value: 'High', disabled: false } + ]; + + const onInputChange = (newValue: string) => { + setInputValue(newValue); + }; + + const onStatusToggle = (isExpanded: boolean) => { + setStatusIsExpanded(isExpanded); + }; + + const onStatusSelect = ( + _event: React.MouseEvent | React.ChangeEvent, + selection: string | SelectOptionObject, + isPlaceholder: boolean | undefined + ) => { + if (isPlaceholder) { + clearStatusSelection(); + } + setStatusSelected(selection); + setStatusIsExpanded(false); + }; + + const clearStatusSelection = () => { + setStatusSelected(undefined); + setStatusIsExpanded(false); + }; + + const onRiskToggle = (isExpanded: boolean) => { + setRiskIsExpanded(isExpanded); + }; + + const onRiskSelect = ( + _event: React.MouseEvent | React.ChangeEvent, + selection: string | SelectOptionObject, + isPlaceholder: boolean | undefined + ) => { + if (isPlaceholder) { + clearRiskSelection(); + } + setRiskSelected(selection); + setRiskIsExpanded(false); + }; + + const clearRiskSelection = () => { + setRiskSelected(undefined); + setRiskIsExpanded(false); + }; + + const toggleGroupItems = ( + + + { + onInputChange(''); + }} + /> + + + + + + + + + + + ); + + const items = ( + } breakpoint="xl"> + {toggleGroupItems} + + ); + + return ( + + {items} + + ); +}; diff --git a/packages/react-core/src/components/Toolbar/examples/ToolbarConsumerManagedToggleGroups.tsx b/packages/react-core/src/components/Toolbar/examples/ToolbarConsumerManagedToggleGroups.tsx new file mode 100644 index 00000000000..3430a3e85d7 --- /dev/null +++ b/packages/react-core/src/components/Toolbar/examples/ToolbarConsumerManagedToggleGroups.tsx @@ -0,0 +1,147 @@ +import React from 'react'; +import { + Toolbar, + ToolbarItem, + ToolbarContent, + ToolbarToggleGroup, + ToolbarGroup, + SelectOptionObject +} from '@patternfly/react-core'; +import { Select, SelectOption, SelectVariant, SearchInput } from '@patternfly/react-core'; +import FilterIcon from '@patternfly/react-icons/dist/esm/icons/filter-icon'; + +export const ToolbarConsumerManagedToggleGroup: React.FunctionComponent = () => { + const [isExpanded, setIsExpanded] = React.useState(false); + const [inputValue, setInputValue] = React.useState(''); + const [statusIsExpanded, setStatusIsExpanded] = React.useState(false); + const [statusSelected, setStatusSelected] = React.useState(); + const [riskIsExpanded, setRiskIsExpanded] = React.useState(false); + const [riskSelected, setRiskSelected] = React.useState(); + + const toggleIsExpanded = () => { + setIsExpanded(!isExpanded); + }; + + const statusOptions = [ + { value: 'Status', disabled: false, isPlaceholder: true }, + { value: 'New', disabled: false }, + { value: 'Pending', disabled: false }, + { value: 'Running', disabled: false }, + { value: 'Cancelled', disabled: false } + ]; + + const riskOptions = [ + { value: 'Risk', disabled: false, isPlaceholder: true }, + { value: 'Low', disabled: false }, + { value: 'Medium', disabled: false }, + { value: 'High', disabled: false } + ]; + + const onInputChange = (newValue: string) => { + setInputValue(newValue); + }; + + const onStatusToggle = (isExpanded: boolean) => { + setStatusIsExpanded(isExpanded); + }; + + const onStatusSelect = ( + _event: React.MouseEvent | React.ChangeEvent, + selection: string | SelectOptionObject, + isPlaceholder: boolean | undefined + ) => { + if (isPlaceholder) { + clearStatusSelection(); + } + setStatusSelected(selection); + setStatusIsExpanded(false); + }; + + const clearStatusSelection = () => { + setStatusSelected(undefined); + setStatusIsExpanded(false); + }; + + const onRiskToggle = (isExpanded: boolean) => { + setRiskIsExpanded(isExpanded); + }; + + const onRiskSelect = ( + _event: React.MouseEvent | React.ChangeEvent, + selection: string | SelectOptionObject, + isPlaceholder: boolean | undefined + ) => { + if (isPlaceholder) { + clearRiskSelection(); + } + setRiskSelected(selection); + setRiskIsExpanded(false); + }; + + const clearRiskSelection = () => { + setRiskSelected(undefined); + setRiskIsExpanded(false); + }; + + const toggleGroupItems = ( + + + { + onInputChange(''); + }} + /> + + + + + + + + + + + ); + + const items = ( + } breakpoint="xl"> + {toggleGroupItems} + + ); + + return ( + + {items} + + ); +}; diff --git a/packages/react-core/src/components/Toolbar/examples/ToolbarGroups.tsx b/packages/react-core/src/components/Toolbar/examples/ToolbarGroups.tsx new file mode 100644 index 00000000000..db34f306479 --- /dev/null +++ b/packages/react-core/src/components/Toolbar/examples/ToolbarGroups.tsx @@ -0,0 +1,158 @@ +import React from 'react'; +import { SelectOptionObject, Toolbar, ToolbarContent, ToolbarGroup, ToolbarItem } from '@patternfly/react-core'; +import { Button, Select, SelectOption, SelectVariant } from '@patternfly/react-core'; +import EditIcon from '@patternfly/react-icons/dist/esm/icons/edit-icon'; +import CloneIcon from '@patternfly/react-icons/dist/esm/icons/clone-icon'; +import SyncIcon from '@patternfly/react-icons/dist/esm/icons/sync-icon'; + +export const ToolbarGroups: React.FunctionComponent = () => { + const firstOptions = [ + { value: 'Filter 1', disabled: false, isPlaceholder: true }, + { value: 'A', disabled: false }, + { value: 'B', disabled: false }, + { value: 'C', disabled: false } + ]; + + const secondOptions = [ + { value: 'Filter 2', disabled: false, isPlaceholder: true }, + { value: '1', disabled: false }, + { value: '2', disabled: false }, + { value: '3', disabled: false } + ]; + + const thirdOptions = [ + { value: 'Filter 3', disabled: false, isPlaceholder: true }, + { value: 'I', disabled: false }, + { value: 'II', disabled: false }, + { value: 'III', disabled: false } + ]; + + const [firstIsExpanded, setFirstIsExpanded] = React.useState(false); + const [firstSelected, setFirstSelected] = React.useState(); + const [secondIsExpanded, setSecondIsExpanded] = React.useState(false); + const [secondSelected, setSecondSelected] = React.useState(); + const [thirdIsExpanded, setThirdIsExpanded] = React.useState(false); + const [thirdSelected, setThirdSelected] = React.useState(); + + const onFirstToggle = (isExpanded: boolean) => { + setFirstIsExpanded(isExpanded); + }; + + const onFirstSelect = (_event: React.MouseEvent | React.ChangeEvent, selection: string | SelectOptionObject) => { + setFirstSelected(selection); + setFirstIsExpanded(false); + }; + + const onSecondToggle = (isExpanded: boolean) => { + setSecondIsExpanded(isExpanded); + }; + + const onSecondSelect = (_event: React.MouseEvent | React.ChangeEvent, selection: string | SelectOptionObject) => { + setSecondSelected(selection); + setSecondIsExpanded(false); + }; + + const onThirdToggle = (isExpanded: boolean) => { + setThirdIsExpanded(isExpanded); + }; + + const onThirdSelect = (_event: React.MouseEvent | React.ChangeEvent, selection: string | SelectOptionObject) => { + setThirdSelected(selection); + setThirdIsExpanded(false); + }; + + const filterGroupItems = ( + + + + + + + + + + + + ); + + const iconButtonGroupItems = ( + + + + + + + + + + + + ); + + const buttonGroupItems = ( + + + + + + + + + + + + ); + + const items = ( + + {filterGroupItems} + {iconButtonGroupItems} + {buttonGroupItems} + + ); + + return ( + + {items} + + ); +}; diff --git a/packages/react-core/src/components/Toolbar/examples/ToolbarInsets.tsx b/packages/react-core/src/components/Toolbar/examples/ToolbarInsets.tsx new file mode 100644 index 00000000000..d10e2663ebd --- /dev/null +++ b/packages/react-core/src/components/Toolbar/examples/ToolbarInsets.tsx @@ -0,0 +1,52 @@ +import React from 'react'; +import { Toolbar, ToolbarItem, ToolbarGroup, ToolbarContent } from '@patternfly/react-core'; +import { Button } from '@patternfly/react-core'; + +export const ToolbarInsets: React.FunctionComponent = () => { + const items = ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); + + return ( + + {items} + + ); +}; diff --git a/packages/react-core/src/components/Toolbar/examples/ToolbarItems.tsx b/packages/react-core/src/components/Toolbar/examples/ToolbarItems.tsx new file mode 100644 index 00000000000..46175893e6b --- /dev/null +++ b/packages/react-core/src/components/Toolbar/examples/ToolbarItems.tsx @@ -0,0 +1,26 @@ +import React from 'react'; +import { Toolbar, ToolbarItem, ToolbarContent } from '@patternfly/react-core'; +import { Button, SearchInput } from '@patternfly/react-core'; + +export const ToolbarItems: React.FunctionComponent = () => { + const items = ( + + + + + + + + + + + + + ); + + return ( + + {items} + + ); +}; diff --git a/packages/react-core/src/components/Toolbar/examples/ToolbarSpacers.tsx b/packages/react-core/src/components/Toolbar/examples/ToolbarSpacers.tsx new file mode 100644 index 00000000000..8afb2db24b7 --- /dev/null +++ b/packages/react-core/src/components/Toolbar/examples/ToolbarSpacers.tsx @@ -0,0 +1,51 @@ +import React from 'react'; +import { Toolbar, ToolbarItem, ToolbarGroup, ToolbarContent } from '@patternfly/react-core'; +import { Button } from '@patternfly/react-core'; + +export const ToolbarSpacers: React.FunctionComponent = () => { + const items = ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); + + return ( + + {items} + + ); +}; diff --git a/packages/react-core/src/components/Toolbar/examples/ToolbarStacked.tsx b/packages/react-core/src/components/Toolbar/examples/ToolbarStacked.tsx new file mode 100644 index 00000000000..26d902b8e7f --- /dev/null +++ b/packages/react-core/src/components/Toolbar/examples/ToolbarStacked.tsx @@ -0,0 +1,253 @@ +import React from 'react'; +import { + Button, + ButtonVariant, + KebabToggle, + Select, + SelectOption, + SelectOptionObject, + SelectVariant, + Pagination, + Dropdown, + DropdownSeparator, + DropdownToggle, + DropdownToggleCheckbox, + DropdownItem, + DropdownPosition, + Divider, + OverflowMenu, + OverflowMenuContent, + OverflowMenuControl, + OverflowMenuGroup, + OverflowMenuItem, + Toolbar, + ToolbarContent, + ToolbarToggleGroup, + ToolbarItem +} from '@patternfly/react-core'; +import FilterIcon from '@patternfly/react-icons/dist/esm/icons/filter-icon'; + +export const ToolbarStacked: React.FunctionComponent = () => { + // toggle group - three option menus with labels, two icon buttons, Kebab menu - right aligned + // pagination - right aligned + const resourceOptions = [ + { value: 'All resources', disabled: false }, + { value: 'Deployment', disabled: false }, + { value: 'Pod', disabled: false } + ]; + + const statusOptions = [ + { value: 'Running', disabled: false }, + { value: 'New', disabled: false }, + { value: 'Pending', disabled: false }, + { value: 'Cancelled', disabled: false } + ]; + + const [kebabIsOpen, setKebabIsOpen] = React.useState(false); + const [resourceIsExpanded, setResourceIsExpanded] = React.useState(false); + const [resourceSelected, setResourceSelected] = React.useState(); + const [statusIsExpanded, setStatusIsExpanded] = React.useState(false); + const [statusSelected, setStatusSelected] = React.useState(); + const [splitButtonDropdownIsOpen, setSplitButtonDropdownIsOpen] = React.useState(false); + const [page, setPage] = React.useState(1); + const [perPage, setPerPage] = React.useState(20); + + const onKebabToggle = (isOpen: boolean) => { + setKebabIsOpen(isOpen); + }; + + const onResourceToggle = (isExpanded: boolean) => { + setResourceIsExpanded(isExpanded); + }; + + const onResourceSelect = (_event: React.ChangeEvent | React.MouseEvent, selection: string | SelectOptionObject) => { + setResourceSelected(selection); + setResourceIsExpanded(false); + }; + + const onResourceSelectDropdown = (event: React.SyntheticEvent | undefined) => { + setResourceSelected(event?.target); + setResourceIsExpanded(false); + }; + + const onStatusToggle = (isExpanded: boolean) => { + setStatusIsExpanded(isExpanded); + }; + + const onStatusSelect = (_event: React.ChangeEvent | React.MouseEvent, selection: string | SelectOptionObject) => { + setStatusSelected(selection); + setStatusIsExpanded(false); + }; + + const onSetPage = (_event: React.MouseEvent | React.KeyboardEvent | MouseEvent, pageNumber: number) => { + setPage(pageNumber); + }; + + const onPerPageSelect = ( + _event: React.MouseEvent | React.KeyboardEvent | MouseEvent, + perPage: number, + page: number + ) => { + setPage(page); + setPerPage(perPage); + }; + + const onSplitButtonToggle = (isOpen: boolean) => { + setSplitButtonDropdownIsOpen(isOpen); + }; + + const onSplitButtonSelect = () => { + setSplitButtonDropdownIsOpen(!splitButtonDropdownIsOpen); + }; + + const dropdownItems = [ + Link, + + Action + , + + Disabled Link + , + + Disabled Action + , + , + Separated Link, + + Separated Action + + ]; + + const splitButtonDropdownItems = [ + Link, + + Action + , + + Disabled Link + , + + Disabled Action + + ]; + + const toggleGroupItems = ( + + + Resource + + + + + + Status + + + + + + ); + + const firstRowItems = ( + + + + } breakpoint="lg"> + {toggleGroupItems} + + + + + + + + + + + + + + + } + isOpen={kebabIsOpen} + isPlain + dropdownItems={dropdownItems} + position={DropdownPosition.right} + /> + + + + + + + ); + + const secondRowItems = ( + + + + + + ]} + onToggle={onSplitButtonToggle} + /> + } + isOpen={splitButtonDropdownIsOpen} + dropdownItems={splitButtonDropdownItems} + /> + + + + + + + + ); + + return ( + + {firstRowItems} + + {secondRowItems} + + ); +}; diff --git a/packages/react-core/src/components/Toolbar/examples/ToolbarSticky.tsx b/packages/react-core/src/components/Toolbar/examples/ToolbarSticky.tsx new file mode 100644 index 00000000000..3b12f68b9f9 --- /dev/null +++ b/packages/react-core/src/components/Toolbar/examples/ToolbarSticky.tsx @@ -0,0 +1,37 @@ +import React from 'react'; +import { Toolbar, ToolbarItem, ToolbarContent, SearchInput, Checkbox } from '@patternfly/react-core'; + +export const ToolbarSticky: React.FunctionComponent = () => { + const [isSticky, setIsSticky] = React.useState(true); + const [showEvenOnly, setShowEvenOnly] = React.useState(true); + const array = Array.from(Array(30), (_, x) => x); // create array of numbers from 1-30 for demo purposes + const numbers = showEvenOnly ? array.filter(number => number % 2 === 0) : array; + + return ( + +
+ + + + + + + + + + +
    + {numbers.map(number => ( +
  • {`item ${number}`}
  • + ))} +
+
+ +
+ ); +}; diff --git a/packages/react-core/src/components/Toolbar/examples/ToolbarWidths.tsx b/packages/react-core/src/components/Toolbar/examples/ToolbarWidths.tsx new file mode 100644 index 00000000000..f163a5e86f2 --- /dev/null +++ b/packages/react-core/src/components/Toolbar/examples/ToolbarWidths.tsx @@ -0,0 +1,30 @@ +import React from 'react'; +import { Toolbar, ToolbarItem, ToolbarContent } from '@patternfly/react-core'; +import { Button } from '@patternfly/react-core'; + +export const ToolbarWidths: React.FunctionComponent = () => { + const widths = { + default: '100px', + sm: '80px', + md: '150px', + lg: '200px', + xl: '250px', + '2xl': '300px' + }; + + const items = ( + + + + + + ); + + return ( + + {items} + + ); +}; diff --git a/packages/react-core/src/components/Toolbar/examples/ToolbarWithFilters.tsx b/packages/react-core/src/components/Toolbar/examples/ToolbarWithFilters.tsx new file mode 100644 index 00000000000..74780cbfd70 --- /dev/null +++ b/packages/react-core/src/components/Toolbar/examples/ToolbarWithFilters.tsx @@ -0,0 +1,222 @@ +import React from 'react'; +import { + Toolbar, + ToolbarItem, + ToolbarContent, + ToolbarFilter, + ToolbarToggleGroup, + ToolbarGroup, + SelectOptionObject +} from '@patternfly/react-core'; +import { + Button, + Select, + SelectOption, + SelectVariant, + Dropdown, + DropdownItem, + DropdownSeparator, + DropdownPosition, + KebabToggle, + SearchInput +} from '@patternfly/react-core'; +import FilterIcon from '@patternfly/react-icons/dist/esm/icons/filter-icon'; +import EditIcon from '@patternfly/react-icons/dist/esm/icons/edit-icon'; +import CloneIcon from '@patternfly/react-icons/dist/esm/icons/clone-icon'; +import SyncIcon from '@patternfly/react-icons/dist/esm/icons/sync-icon'; + +export const ToolbarWithFilters: React.FunctionComponent = () => { + const [inputValue, setInputValue] = React.useState(''); + const [statusIsExpanded, setStatusIsExpanded] = React.useState(false); + const [riskIsExpanded, setRiskIsExpanded] = React.useState(false); + const [filters, setFilters] = React.useState({ + risk: ['Low'], + status: ['New', 'Pending'] + }); + const [kebabIsOpen, setKebabIsOpen] = React.useState(false); + + const onInputChange = (newValue: string) => { + setInputValue(newValue); + }; + + const onSelect = ( + type: string, + event: React.MouseEvent | React.ChangeEvent, + selection: string | SelectOptionObject + ) => { + const checked = (event.target as HTMLInputElement).checked; + setFilters(prev => { + const prevSelections = prev[type]; + return { + ...prev, + [type]: checked ? [...prevSelections, selection] : prevSelections.filter(value => value !== selection) + }; + }); + }; + + const onStatusSelect = (event: React.MouseEvent | React.ChangeEvent, selection: string | SelectOptionObject) => { + onSelect('status', event, selection); + }; + + const onRiskSelect = (event: React.MouseEvent | React.ChangeEvent, selection: string | SelectOptionObject) => { + onSelect('risk', event, selection); + }; + + const onDelete = (type: string, id: string) => { + if (type === 'Risk') { + setFilters({ risk: filters.risk.filter((fil: string) => fil !== id), status: filters.status }); + } else if (type === 'Status') { + setFilters({ risk: filters.risk, status: filters.status.filter((fil: string) => fil !== id) }); + } else { + setFilters({ risk: [], status: [] }); + } + }; + + const onDeleteGroup = (type: string) => { + if (type === 'Risk') { + setFilters({ risk: [], status: filters.status }); + } else if (type === 'Status') { + setFilters({ risk: filters.risk, status: [] }); + } + }; + + const onStatusToggle = (isExpanded: boolean) => { + setStatusIsExpanded(isExpanded); + }; + + const onRiskToggle = (isExpanded: boolean) => { + setRiskIsExpanded(isExpanded); + }; + + const onKebabToggle = (isOpen: boolean) => { + setKebabIsOpen(isOpen); + }; + + const statusMenuItems = [ + , + , + , + + ]; + + const riskMenuItems = [ + , + , + + ]; + + const toggleGroupItems = ( + + + { + onInputChange(''); + }} + /> + + + onDelete(category as string, chip as string)} + deleteChipGroup={category => onDeleteGroup(category as string)} + categoryName="Status" + > + + + onDelete(category as string, chip as string)} + categoryName="Risk" + > + + + + + ); + + const dropdownItems = [ + Link, + + Action + , + + Disabled Link + , + + Disabled Action + , + , + Separated Link, + + Separated Action + + ]; + + const toolbarItems = ( + + } breakpoint="xl"> + {toggleGroupItems} + + + + + + + + + + + + + + } + isOpen={kebabIsOpen} + isPlain + dropdownItems={dropdownItems} + position={DropdownPosition.right} + /> + + + ); + + return ( + onDelete('', '')} + > + {toolbarItems} + + ); +};