utilize new DataToolbar experimental patternfly components

This commit is contained in:
John Mitchell
2019-12-10 13:12:19 -05:00
parent c69d497093
commit a31661ce08
6 changed files with 245 additions and 370 deletions

View File

@@ -4,73 +4,16 @@ import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import { import {
Checkbox, Checkbox,
Toolbar as PFToolbar,
ToolbarGroup as PFToolbarGroup,
ToolbarItem,
} from '@patternfly/react-core'; } from '@patternfly/react-core';
import styled from 'styled-components'; import styled from 'styled-components';
import { SearchIcon } from '@patternfly/react-icons';
import { DataToolbarGroup, DataToolbarToggleGroup, DataToolbarItem } from '@patternfly/react-core/dist/esm/experimental';
import ExpandCollapse from '../ExpandCollapse'; import ExpandCollapse from '../ExpandCollapse';
import Search from '../Search'; import Search from '../Search';
import Sort from '../Sort'; import Sort from '../Sort';
import VerticalSeparator from '../VerticalSeparator';
import { SearchColumns, SortColumns, QSConfig } from '@types'; import { SearchColumns, SortColumns, QSConfig } from '@types';
const AWXToolbar = styled.div`
--awx-toolbar--BackgroundColor: var(--pf-global--BackgroundColor--light-100);
--awx-toolbar--BorderColor: #ebebeb;
--awx-toolbar--BorderWidth: var(--pf-global--BorderWidth--sm);
--pf-global--target-size--MinHeight: 0;
--pf-global--target-size--MinWidth: 0;
--pf-global--FontSize--md: 14px;
border-bottom: var(--awx-toolbar--BorderWidth) solid
var(--awx-toolbar--BorderColor);
background-color: var(--awx-toolbar--BackgroundColor);
display: flex;
min-height: 70px;
flex-grow: 1;
`;
const Toolbar = styled(PFToolbar)`
flex-grow: 1;
margin-left: 20px;
margin-right: 20px;
`;
const ToolbarGroup = styled(PFToolbarGroup)`
&&& {
margin: 0;
}
`;
const ColumnLeft = styled.div`
display: flex;
flex-basis: ${props => (props.fillWidth ? 'auto' : '100%')};
flex-grow: ${props => (props.fillWidth ? '1' : '0')};
justify-content: flex-start;
align-items: center;
padding: 10px 0 8px 0;
@media screen and (min-width: 980px) {
flex-basis: ${props => (props.fillWidth ? 'auto' : '50%')};
}
`;
const ColumnRight = styled.div`
display: flex;
flex-basis: ${props => (props.fillWidth ? 'auto' : '100%')};
flex-grow: 0;
justify-content: flex-start;
align-items: center;
padding: 8px 0 10px 0;
@media screen and (min-width: 980px) {
flex-basis: ${props => (props.fillWidth ? 'auto' : '50%')};
}
`;
const AdditionalControlsWrapper = styled.div` const AdditionalControlsWrapper = styled.div`
display: flex; display: flex;
flex-grow: 1; flex-grow: 1;
@@ -82,6 +25,18 @@ const AdditionalControlsWrapper = styled.div`
} }
`; `;
const AdditionalControlsDataToolbarGroup = styled(DataToolbarGroup)`
margin-left: auto;
margin-right: 0 !important;
`;
const DataToolbarSeparator = styled(DataToolbarItem)`
width: 1px !important;
height: 30px !important;
margin-left: 3px !important;
margin-right: 10px !important;
`;
class DataListToolbar extends React.Component { class DataListToolbar extends React.Component {
render() { render() {
const { const {
@@ -90,9 +45,9 @@ class DataListToolbar extends React.Component {
showSelectAll, showSelectAll,
isAllSelected, isAllSelected,
isCompact, isCompact,
fillWidth,
onSort, onSort,
onSearch, onSearch,
onRemove,
onCompact, onCompact,
onExpand, onExpand,
onSelectAll, onSelectAll,
@@ -103,58 +58,58 @@ class DataListToolbar extends React.Component {
const showExpandCollapse = onCompact && onExpand; const showExpandCollapse = onCompact && onExpand;
return ( return (
<AWXToolbar> <Fragment>
<Toolbar css={fillWidth ? 'margin-right: 0; margin-left: 0' : ''}> {showSelectAll && (
<ColumnLeft fillWidth={fillWidth}> <DataToolbarGroup>
{showSelectAll && ( <DataToolbarItem>
<Fragment> <Checkbox
<ToolbarItem> isChecked={isAllSelected}
<Checkbox onChange={onSelectAll}
isChecked={isAllSelected} aria-label={i18n._(t`Select all`)}
onChange={onSelectAll} id="select-all"
aria-label={i18n._(t`Select all`)}
id="select-all"
/>
</ToolbarItem>
<VerticalSeparator />
</Fragment>
)}
<ToolbarItem css="flex-grow: 1;">
<Search
qsConfig={qsConfig}
columns={searchColumns}
onSearch={onSearch}
/> />
</ToolbarItem> </DataToolbarItem>
<VerticalSeparator /> <DataToolbarSeparator variant="separator" />
</ColumnLeft> </DataToolbarGroup>
<ColumnRight fillWidth={fillWidth}> )}
<ToolbarItem> <DataToolbarToggleGroup toggleIcon={<SearchIcon />} breakpoint="xl">
<Sort <DataToolbarItem>
qsConfig={qsConfig} <Search
columns={sortColumns} qsConfig={qsConfig}
onSort={onSort} columns={searchColumns}
/> onSearch={onSearch}
</ToolbarItem> onRemove={onRemove}
{showExpandCollapse && ( />
<Fragment> </DataToolbarItem>
<VerticalSeparator /> <DataToolbarItem>
<ToolbarGroup> <Sort
<ExpandCollapse qsConfig={qsConfig}
isCompact={isCompact} columns={sortColumns}
onCompact={onCompact} onSort={onSort}
onExpand={onExpand} />
/> </DataToolbarItem>
</ToolbarGroup> </DataToolbarToggleGroup>
{additionalControls && <VerticalSeparator />} <DataToolbarGroup>
</Fragment> {showExpandCollapse && (
)} <Fragment>
<DataToolbarItem>
<ExpandCollapse
isCompact={isCompact}
onCompact={onCompact}
onExpand={onExpand}
/>
</DataToolbarItem>
</Fragment>
)}
</DataToolbarGroup>
<AdditionalControlsDataToolbarGroup>
<DataToolbarItem>
<AdditionalControlsWrapper> <AdditionalControlsWrapper>
{additionalControls} {additionalControls}
</AdditionalControlsWrapper> </AdditionalControlsWrapper>
</ColumnRight> </DataToolbarItem>
</Toolbar> </AdditionalControlsDataToolbarGroup>
</AWXToolbar> </Fragment>
); );
} }
} }
@@ -166,7 +121,6 @@ DataListToolbar.propTypes = {
showSelectAll: PropTypes.bool, showSelectAll: PropTypes.bool,
isAllSelected: PropTypes.bool, isAllSelected: PropTypes.bool,
isCompact: PropTypes.bool, isCompact: PropTypes.bool,
fillWidth: PropTypes.bool,
onCompact: PropTypes.func, onCompact: PropTypes.func,
onExpand: PropTypes.func, onExpand: PropTypes.func,
onSearch: PropTypes.func, onSearch: PropTypes.func,
@@ -179,7 +133,6 @@ DataListToolbar.defaultProps = {
showSelectAll: false, showSelectAll: false,
isAllSelected: false, isAllSelected: false,
isCompact: false, isCompact: false,
fillWidth: false,
onCompact: null, onCompact: null,
onExpand: null, onExpand: null,
onSearch: null, onSearch: null,

View File

@@ -1,21 +1,13 @@
import React from 'react'; import React, { Fragment } from 'react';
import { withRouter } from 'react-router-dom'; import { withRouter } from 'react-router-dom';
import { withI18n } from '@lingui/react'; import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import styled from 'styled-components'; import styled from 'styled-components';
import { Button } from '@patternfly/react-core'; import { DataToolbarGroup, DataToolbarItem } from '@patternfly/react-core/dist/esm/experimental';
import { parseQueryString } from '@util/qs'; import { parseQueryString } from '@util/qs';
import { ChipGroup as _ChipGroup, Chip } from '@components/Chip'; import { Button, Chip, ChipGroup, ChipGroupToolbarItem } from '@patternfly/react-core';
import VerticalSeparator from '@components/VerticalSeparator'; import VerticalSeparator from '@components/VerticalSeparator';
const FilterTagsRow = styled.div`
display: flex;
padding: 15px 20px;
border-top: 1px solid #d2d2d2;
font-size: 14px;
align-items: center;
`;
const ResultCount = styled.span` const ResultCount = styled.span`
font-weight: bold; font-weight: bold;
`; `;
@@ -24,12 +16,6 @@ const FilterLabel = styled.span`
padding-right: 20px; padding-right: 20px;
`; `;
const ChipGroup = styled(_ChipGroup)`
li.pf-m-overflow {
display: none;
}
`;
// remove non-default query params so they don't show up as filter tags // remove non-default query params so they don't show up as filter tags
const filterDefaultParams = (paramsArr, config) => { const filterDefaultParams = (paramsArr, config) => {
const defaultParamsKeys = Object.keys(config.defaultParams); const defaultParamsKeys = Object.keys(config.defaultParams);
@@ -45,7 +31,7 @@ const FilterTags = ({
onRemoveAll, onRemoveAll,
}) => { }) => {
const queryParams = parseQueryString(qsConfig, location.search); const queryParams = parseQueryString(qsConfig, location.search);
const queryParamsArr = []; const queryParamsByKey = {};
const nonDefaultParams = filterDefaultParams( const nonDefaultParams = filterDefaultParams(
Object.keys(queryParams), Object.keys(queryParams),
qsConfig qsConfig
@@ -56,45 +42,45 @@ const FilterTags = ({
.split('_') .split('_')
.map(word => `${word.charAt(0).toUpperCase()}${word.slice(1)}`) .map(word => `${word.charAt(0).toUpperCase()}${word.slice(1)}`)
.join(' '); .join(' ');
queryParamsByKey[key] = { label, tags: [] };
if (Array.isArray(queryParams[key])) { if (Array.isArray(queryParams[key])) {
queryParams[key].forEach(val => queryParams[key].forEach(val =>
queryParamsArr.push({ key, value: val, label }) queryParamsByKey[key].tags.push(val)
); );
} else { } else {
queryParamsArr.push({ key, value: queryParams[key], label }); queryParamsByKey[key].tags.push(queryParams[key]);
} }
}); });
return ( return (
queryParamsArr.length > 0 && ( Object.keys(queryParamsByKey).length > 0 && (
<FilterTagsRow> <Fragment>
<ResultCount>{i18n._(t`${itemCount} results`)}</ResultCount> <DataToolbarGroup>
<VerticalSeparator /> <ResultCount>{i18n._(t`${itemCount} results`)}</ResultCount>
<FilterLabel>{i18n._(t`Active Filters:`)}</FilterLabel> </DataToolbarGroup>
<ChipGroup defaultIsOpen> <DataToolbarGroup>
{queryParamsArr.map(({ key, label, value }) => ( <FilterLabel>{i18n._(t`Active Filters:`)}</FilterLabel>
<Chip <DataToolbarItem variant="chip-group">
className="searchTagChip" {Object.keys(queryParamsByKey).map(key => (
key={`${key}__${value}`} <ChipGroup withToolbar key={`${key}-group`}>
isReadOnly={false} <ChipGroupToolbarItem key={key} categoryName={queryParamsByKey[key].label}>
onClick={() => onRemove(key, value)} {queryParamsByKey[key].tags.map(chip => (
> <Chip key={chip} onClick={() => onRemove(key, chip)}>
<b>{label}:</b>&nbsp;{value} {chip}
</Chip> </Chip>
))} ))}
<div className="pf-c-chip pf-m-overflow"> </ChipGroupToolbarItem>
<Button </ChipGroup>
variant="plain" ))}
type="button" </DataToolbarItem>
aria-label={i18n._(t`Clear all search filters`)} <DataToolbarItem>
onClick={onRemoveAll} <Button variant="link" onClick={onRemoveAll} isInline>
> {i18n._(t`Clear all search filters`)}
<span className="pf-c-chip__text">{i18n._(t`Clear all`)}</span>
</Button> </Button>
</div> </DataToolbarItem>
</ChipGroup> </DataToolbarGroup>
</FilterTagsRow> </Fragment>
) )
); );
}; };

View File

@@ -2,8 +2,8 @@ import React, { Fragment } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { withRouter } from 'react-router-dom'; import { withRouter } from 'react-router-dom';
import styled from 'styled-components'; import styled from 'styled-components';
import { DataToolbar, DataToolbarContent } from '@patternfly/react-core/dist/esm/experimental';
import DataListToolbar from '@components/DataListToolbar'; import DataListToolbar from '@components/DataListToolbar';
import FilterTags from '@components/FilterTags';
import { import {
encodeNonDefaultQueryString, encodeNonDefaultQueryString,
@@ -84,33 +84,32 @@ class ListHeader extends React.Component {
return ( return (
<Fragment> <Fragment>
{isEmpty ? ( {isEmpty ? (
<Fragment> <DataToolbar id={`${qsConfig.namespace}-list-toolbar`}
<EmptyStateControlsWrapper> clearAllFilters={this.handleRemoveAll}
{emptyStateControls} collapseListedFiltersBreakpoint="md"
</EmptyStateControlsWrapper> >
<FilterTags <DataToolbarContent>
itemCount={itemCount} <EmptyStateControlsWrapper>
qsConfig={qsConfig} {emptyStateControls}
onRemove={this.handleRemove} </EmptyStateControlsWrapper>
onRemoveAll={this.handleRemoveAll} </DataToolbarContent>
/> </DataToolbar>
</Fragment>
) : ( ) : (
<Fragment> <DataToolbar id={`${qsConfig.namespace}-list-toolbar`}
{renderToolbar({ clearAllFilters={this.handleRemoveAll}
searchColumns, collapseListedFiltersBreakpoint="xl"
sortColumns, >
onSearch: this.handleSearch, <DataToolbarContent>
onSort: this.handleSort, {renderToolbar({
qsConfig, searchColumns,
})} sortColumns,
<FilterTags onSearch: this.handleSearch,
itemCount={itemCount} onSort: this.handleSort,
qsConfig={qsConfig} onRemove: this.handleRemove,
onRemove={this.handleRemove} qsConfig,
onRemoveAll={this.handleRemoveAll} })}
/> </DataToolbarContent>
</Fragment> </DataToolbar>
)} )}
</Fragment> </Fragment>
); );

View File

@@ -2,80 +2,33 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { withI18n } from '@lingui/react'; import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import { withRouter } from 'react-router-dom';
import { import {
Button as PFButton, Button,
Dropdown as PFDropdown, ButtonVariant,
Dropdown,
DropdownPosition, DropdownPosition,
DropdownToggle, DropdownToggle,
DropdownItem, DropdownItem,
Form, InputGroup,
FormGroup, TextInput,
TextInput as PFTextInput,
} from '@patternfly/react-core'; } from '@patternfly/react-core';
import {
DataToolbarGroup,
DataToolbarItem,
DataToolbarFilter
} from '@patternfly/react-core/dist/esm/experimental';
import { SearchIcon } from '@patternfly/react-icons'; import { SearchIcon } from '@patternfly/react-icons';
import { parseQueryString } from '@util/qs';
import { QSConfig, SearchColumns } from '@types'; import { QSConfig, SearchColumns } from '@types';
import styled from 'styled-components'; import styled from 'styled-components';
const TextInput = styled(PFTextInput)`
min-height: 0px;
height: 30px;
--pf-c-form-control--BorderTopColor: var(--pf-global--BorderColor--200);
--pf-c-form-control--BorderLeftColor: var(--pf-global--BorderColor--200);
`;
const Button = styled(PFButton)`
width: 34px;
padding: 0px;
::after {
border: var(--pf-c-button--BorderWidth) solid
var(--pf-global--BorderColor--200);
}
`;
const Dropdown = styled(PFDropdown)`
&&& {
/* Higher specificity required because we are selecting unclassed elements */
> button {
min-height: 30px;
min-width: 70px;
height: 30px;
padding: 0 10px;
margin: 0px;
::before {
border-color: var(--pf-global--BorderColor--200);
border-top-left-radius: 3px;
border-bottom-left-radius: 3px;
}
> span {
/* text element */
width: auto;
}
> svg {
/* caret icon */
margin: 0px;
padding-top: 3px;
padding-left: 3px;
}
}
}
`;
const NoOptionDropdown = styled.div` const NoOptionDropdown = styled.div`
align-self: stretch; align-self: stretch;
border: 1px solid var(--pf-global--BorderColor--200); border: 1px solid var(--pf-global--BorderColor--300);
border-top-left-radius: 3px; padding: 5px 15px;
border-bottom-left-radius: 3px;
padding: 3px 7px;
white-space: nowrap; white-space: nowrap;
`; border-bottom-color: var(--pf-global--BorderColor--200);
const InputFormGroup = styled(FormGroup)`
flex: 1;
`; `;
class Search extends React.Component { class Search extends React.Component {
@@ -94,6 +47,7 @@ class Search extends React.Component {
this.handleDropdownToggle = this.handleDropdownToggle.bind(this); this.handleDropdownToggle = this.handleDropdownToggle.bind(this);
this.handleDropdownSelect = this.handleDropdownSelect.bind(this); this.handleDropdownSelect = this.handleDropdownSelect.bind(this);
this.handleSearch = this.handleSearch.bind(this); this.handleSearch = this.handleSearch.bind(this);
this.handleTextKeyDown = this.handleTextKeyDown.bind(this);
} }
handleDropdownToggle(isSearchDropdownOpen) { handleDropdownToggle(isSearchDropdownOpen) {
@@ -134,9 +88,15 @@ class Search extends React.Component {
this.setState({ searchValue }); this.setState({ searchValue });
} }
handleTextKeyDown(e) {
if (e.key && e.key === 'Enter') {
this.handleSearch(e);
}
}
render() { render() {
const { up } = DropdownPosition; const { up } = DropdownPosition;
const { columns, i18n } = this.props; const { columns, i18n, onRemove, qsConfig, location } = this.props;
const { isSearchDropdownOpen, searchKey, searchValue } = this.state; const { isSearchDropdownOpen, searchKey, searchValue } = this.state;
const { name: searchColumnName } = columns.find( const { name: searchColumnName } = columns.find(
({ key }) => key === searchKey ({ key }) => key === searchKey
@@ -150,65 +110,95 @@ class Search extends React.Component {
</DropdownItem> </DropdownItem>
)); ));
const filterDefaultParams = (paramsArr, config) => {
const defaultParamsKeys = Object.keys(config.defaultParams);
return paramsArr.filter(key => defaultParamsKeys.indexOf(key) === -1);
};
const getChipsByKey = () => {
const queryParams = parseQueryString(qsConfig, location.search);
const queryParamsByKey = {};
columns.forEach(({name, key}) => {
queryParamsByKey[key] = {key, label: name, chips: []};
});
const nonDefaultParams = filterDefaultParams(
Object.keys(queryParams),
qsConfig
);
nonDefaultParams.forEach(key => {
const columnKey = key
.replace('__icontains', '');
const label = key
.replace('__icontains', '')
.split('_')
.map(word => `${word.charAt(0).toUpperCase()}${word.slice(1)}`)
.join(' ');
queryParamsByKey[columnKey] = { key, label, chips: [] };
if (Array.isArray(queryParams[key])) {
queryParams[key].forEach(val =>
queryParamsByKey[columnKey].chips.push(val)
);
} else {
queryParamsByKey[columnKey].chips.push(queryParams[key]);
}
});
return queryParamsByKey;
}
const chipsByKey = getChipsByKey();
return ( return (
<Form autoComplete="off"> <DataToolbarGroup variant="filter-group">
<div className="pf-c-input-group"> <DataToolbarItem>
{searchDropdownItems.length > 0 ? ( {searchDropdownItems.length > 0 ? (
<FormGroup <Dropdown
fieldId="searchKeyDropdown" onToggle={this.handleDropdownToggle}
label={ onSelect={this.handleDropdownSelect}
<span className="pf-screen-reader"> direction={up}
{i18n._(t`Search key dropdown`)} toggle={
</span> <DropdownToggle
id="awx-search"
onToggle={this.handleDropdownToggle}
style={{ width: '100%' }}
>
{searchColumnName}
</DropdownToggle>
} }
> isOpen={isSearchDropdownOpen}
<Dropdown dropdownItems={searchDropdownItems}
onToggle={this.handleDropdownToggle} style={{ width: '100%' }}
onSelect={this.handleDropdownSelect} />) : (<NoOptionDropdown>{searchColumnName}</NoOptionDropdown>)}
direction={up} </DataToolbarItem>
isOpen={isSearchDropdownOpen} {columns.map(({key}) => (<DataToolbarFilter
toggle={ chips={chipsByKey[key] ? chipsByKey[key].chips : []}
<DropdownToggle deleteChip={(unusedKey, val) => { onRemove(chipsByKey[key].key, val) }}
id="awx-search" categoryName={chipsByKey[key] ? chipsByKey[key].label : key}
onToggle={this.handleDropdownToggle} key={key}
> showToolbarItem={searchKey === key}
{searchColumnName} >
</DropdownToggle> <InputGroup>
}
dropdownItems={searchDropdownItems}
/>
</FormGroup>
) : (
<NoOptionDropdown>{searchColumnName}</NoOptionDropdown>
)}
<InputFormGroup
fieldId="searchValueTextInput"
label={
<span className="pf-screen-reader">
{i18n._(t`Search value text input`)}
</span>
}
style={{ width: '100%' }}
suppressClassNameWarning
>
<TextInput <TextInput
type="search" type="search"
aria-label={i18n._(t`Search text input`)} aria-label={i18n._(t`Search text input`)}
value={searchValue} value={searchValue}
onChange={this.handleSearchInputChange} onChange={this.handleSearchInputChange}
style={{ height: '30px' }} onKeyDown={this.handleTextKeyDown}
/> />
</InputFormGroup> <Button
<Button variant={ButtonVariant.control}
variant="tertiary" aria-label={i18n._(t`Search submit button`)}
type="submit" onClick={this.handleSearch}
aria-label={i18n._(t`Search submit button`)} >
onClick={this.handleSearch} <SearchIcon />
> </Button>
<SearchIcon /> </InputGroup>
</Button> </DataToolbarFilter>))}
</div> </DataToolbarGroup>
</Form>
); );
} }
} }
@@ -216,11 +206,13 @@ class Search extends React.Component {
Search.propTypes = { Search.propTypes = {
qsConfig: QSConfig.isRequired, qsConfig: QSConfig.isRequired,
columns: SearchColumns.isRequired, columns: SearchColumns.isRequired,
onSearch: PropTypes.func onSearch: PropTypes.func,
onRemove: PropTypes.func
}; };
Search.defaultProps = { Search.defaultProps = {
onSearch: null, onSearch: null,
onRemove: null
}; };
export default withI18n()(Search); export default withI18n()(withRouter(Search));

View File

@@ -1,15 +1,16 @@
import React from 'react'; import React, { Fragment } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { withI18n } from '@lingui/react'; import { withI18n } from '@lingui/react';
import { withRouter } from 'react-router-dom'; import { withRouter } from 'react-router-dom';
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import { import {
Button, Button,
Dropdown as PFDropdown, ButtonVariant,
Dropdown,
DropdownPosition, DropdownPosition,
DropdownToggle, DropdownToggle,
DropdownItem, DropdownItem,
Tooltip, InputGroup,
} from '@patternfly/react-core'; } from '@patternfly/react-core';
import { import {
SortAlphaDownIcon, SortAlphaDownIcon,
@@ -18,58 +19,11 @@ import {
SortNumericUpIcon, SortNumericUpIcon,
} from '@patternfly/react-icons'; } from '@patternfly/react-icons';
import styled from 'styled-components';
import { import {
parseQueryString parseQueryString
} from '@util/qs'; } from '@util/qs';
import { SortColumns, QSConfig } from '@types'; import { SortColumns, QSConfig } from '@types';
const Dropdown = styled(PFDropdown)`
&&& {
> button {
min-height: 30px;
min-width: 70px;
height: 30px;
padding: 0 10px;
margin: 0px;
> span {
/* text element within dropdown */
width: auto;
}
> svg {
/* caret icon */
margin: 0px;
padding-top: 3px;
padding-left: 3px;
}
}
}
`;
const IconWrapper = styled.span`
> svg {
font-size: 18px;
}
`;
const SortButton = styled(Button)`
padding: 5px 8px;
margin-top: 3px;
&:hover {
background-color: #0166cc;
color: white;
}
`;
const SortBy = styled.span`
margin-right: 15px;
font-size: var(--pf-global--FontSize--md);
`;
class Sort extends React.Component { class Sort extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
@@ -165,12 +119,10 @@ class Sort extends React.Component {
} }
return ( return (
<React.Fragment> <Fragment>
{sortDropdownItems.length > 0 && ( {sortDropdownItems.length > 0 && (
<React.Fragment> <InputGroup>
<SortBy>{i18n._(t`Sort By`)}</SortBy>
<Dropdown <Dropdown
style={{ marginRight: '10px' }}
onToggle={this.handleDropdownToggle} onToggle={this.handleDropdownToggle}
onSelect={this.handleDropdownSelect} onSelect={this.handleDropdownSelect}
direction={up} direction={up}
@@ -185,23 +137,16 @@ class Sort extends React.Component {
} }
dropdownItems={sortDropdownItems} dropdownItems={sortDropdownItems}
/> />
</React.Fragment> <Button
variant={ButtonVariant.control}
aria-label={i18n._(t`Reverse Sort Order`)}
onClick={this.handleSort}
>
<SortIcon />
</Button>
</InputGroup>
)} )}
<Tooltip </Fragment>
content={<div>{i18n._(t`Reverse Sort Order`)}</div>}
position="top"
>
<SortButton
onClick={this.handleSort}
variant="plain"
aria-label={i18n._(t`Sort`)}
>
<IconWrapper>
<SortIcon style={{ verticalAlign: '-0.225em' }} />
</IconWrapper>
</SortButton>
</Tooltip>
</React.Fragment>
); );
} }
} }

View File

@@ -5,7 +5,7 @@ const Separator = styled.span`
display: inline-block; display: inline-block;
width: 1px; width: 1px;
height: 30px; height: 30px;
margin-right: 20px; margin-right: 27px;
margin-left: 20px; margin-left: 20px;
background-color: #d7d7d7; background-color: #d7d7d7;
vertical-align: middle; vertical-align: middle;