mirror of
https://github.com/ansible/awx.git
synced 2026-01-16 12:20:45 -03:30
utilize new DataToolbar experimental patternfly components
This commit is contained in:
parent
c69d497093
commit
a31661ce08
@ -4,73 +4,16 @@ import { withI18n } from '@lingui/react';
|
||||
import { t } from '@lingui/macro';
|
||||
import {
|
||||
Checkbox,
|
||||
Toolbar as PFToolbar,
|
||||
ToolbarGroup as PFToolbarGroup,
|
||||
ToolbarItem,
|
||||
} from '@patternfly/react-core';
|
||||
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 Search from '../Search';
|
||||
import Sort from '../Sort';
|
||||
import VerticalSeparator from '../VerticalSeparator';
|
||||
|
||||
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`
|
||||
display: flex;
|
||||
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 {
|
||||
render() {
|
||||
const {
|
||||
@ -90,9 +45,9 @@ class DataListToolbar extends React.Component {
|
||||
showSelectAll,
|
||||
isAllSelected,
|
||||
isCompact,
|
||||
fillWidth,
|
||||
onSort,
|
||||
onSearch,
|
||||
onRemove,
|
||||
onCompact,
|
||||
onExpand,
|
||||
onSelectAll,
|
||||
@ -103,58 +58,58 @@ class DataListToolbar extends React.Component {
|
||||
|
||||
const showExpandCollapse = onCompact && onExpand;
|
||||
return (
|
||||
<AWXToolbar>
|
||||
<Toolbar css={fillWidth ? 'margin-right: 0; margin-left: 0' : ''}>
|
||||
<ColumnLeft fillWidth={fillWidth}>
|
||||
{showSelectAll && (
|
||||
<Fragment>
|
||||
<ToolbarItem>
|
||||
<Checkbox
|
||||
isChecked={isAllSelected}
|
||||
onChange={onSelectAll}
|
||||
aria-label={i18n._(t`Select all`)}
|
||||
id="select-all"
|
||||
/>
|
||||
</ToolbarItem>
|
||||
<VerticalSeparator />
|
||||
</Fragment>
|
||||
)}
|
||||
<ToolbarItem css="flex-grow: 1;">
|
||||
<Search
|
||||
qsConfig={qsConfig}
|
||||
columns={searchColumns}
|
||||
onSearch={onSearch}
|
||||
<Fragment>
|
||||
{showSelectAll && (
|
||||
<DataToolbarGroup>
|
||||
<DataToolbarItem>
|
||||
<Checkbox
|
||||
isChecked={isAllSelected}
|
||||
onChange={onSelectAll}
|
||||
aria-label={i18n._(t`Select all`)}
|
||||
id="select-all"
|
||||
/>
|
||||
</ToolbarItem>
|
||||
<VerticalSeparator />
|
||||
</ColumnLeft>
|
||||
<ColumnRight fillWidth={fillWidth}>
|
||||
<ToolbarItem>
|
||||
<Sort
|
||||
qsConfig={qsConfig}
|
||||
columns={sortColumns}
|
||||
onSort={onSort}
|
||||
/>
|
||||
</ToolbarItem>
|
||||
{showExpandCollapse && (
|
||||
<Fragment>
|
||||
<VerticalSeparator />
|
||||
<ToolbarGroup>
|
||||
<ExpandCollapse
|
||||
isCompact={isCompact}
|
||||
onCompact={onCompact}
|
||||
onExpand={onExpand}
|
||||
/>
|
||||
</ToolbarGroup>
|
||||
{additionalControls && <VerticalSeparator />}
|
||||
</Fragment>
|
||||
)}
|
||||
</DataToolbarItem>
|
||||
<DataToolbarSeparator variant="separator" />
|
||||
</DataToolbarGroup>
|
||||
)}
|
||||
<DataToolbarToggleGroup toggleIcon={<SearchIcon />} breakpoint="xl">
|
||||
<DataToolbarItem>
|
||||
<Search
|
||||
qsConfig={qsConfig}
|
||||
columns={searchColumns}
|
||||
onSearch={onSearch}
|
||||
onRemove={onRemove}
|
||||
/>
|
||||
</DataToolbarItem>
|
||||
<DataToolbarItem>
|
||||
<Sort
|
||||
qsConfig={qsConfig}
|
||||
columns={sortColumns}
|
||||
onSort={onSort}
|
||||
/>
|
||||
</DataToolbarItem>
|
||||
</DataToolbarToggleGroup>
|
||||
<DataToolbarGroup>
|
||||
{showExpandCollapse && (
|
||||
<Fragment>
|
||||
<DataToolbarItem>
|
||||
<ExpandCollapse
|
||||
isCompact={isCompact}
|
||||
onCompact={onCompact}
|
||||
onExpand={onExpand}
|
||||
/>
|
||||
</DataToolbarItem>
|
||||
</Fragment>
|
||||
)}
|
||||
</DataToolbarGroup>
|
||||
<AdditionalControlsDataToolbarGroup>
|
||||
<DataToolbarItem>
|
||||
<AdditionalControlsWrapper>
|
||||
{additionalControls}
|
||||
</AdditionalControlsWrapper>
|
||||
</ColumnRight>
|
||||
</Toolbar>
|
||||
</AWXToolbar>
|
||||
</DataToolbarItem>
|
||||
</AdditionalControlsDataToolbarGroup>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -166,7 +121,6 @@ DataListToolbar.propTypes = {
|
||||
showSelectAll: PropTypes.bool,
|
||||
isAllSelected: PropTypes.bool,
|
||||
isCompact: PropTypes.bool,
|
||||
fillWidth: PropTypes.bool,
|
||||
onCompact: PropTypes.func,
|
||||
onExpand: PropTypes.func,
|
||||
onSearch: PropTypes.func,
|
||||
@ -179,7 +133,6 @@ DataListToolbar.defaultProps = {
|
||||
showSelectAll: false,
|
||||
isAllSelected: false,
|
||||
isCompact: false,
|
||||
fillWidth: false,
|
||||
onCompact: null,
|
||||
onExpand: null,
|
||||
onSearch: null,
|
||||
|
||||
@ -1,21 +1,13 @@
|
||||
import React from 'react';
|
||||
import React, { Fragment } from 'react';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import { withI18n } from '@lingui/react';
|
||||
import { t } from '@lingui/macro';
|
||||
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 { ChipGroup as _ChipGroup, Chip } from '@components/Chip';
|
||||
import { Button, Chip, ChipGroup, ChipGroupToolbarItem } from '@patternfly/react-core';
|
||||
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`
|
||||
font-weight: bold;
|
||||
`;
|
||||
@ -24,12 +16,6 @@ const FilterLabel = styled.span`
|
||||
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
|
||||
const filterDefaultParams = (paramsArr, config) => {
|
||||
const defaultParamsKeys = Object.keys(config.defaultParams);
|
||||
@ -45,7 +31,7 @@ const FilterTags = ({
|
||||
onRemoveAll,
|
||||
}) => {
|
||||
const queryParams = parseQueryString(qsConfig, location.search);
|
||||
const queryParamsArr = [];
|
||||
const queryParamsByKey = {};
|
||||
const nonDefaultParams = filterDefaultParams(
|
||||
Object.keys(queryParams),
|
||||
qsConfig
|
||||
@ -56,45 +42,45 @@ const FilterTags = ({
|
||||
.split('_')
|
||||
.map(word => `${word.charAt(0).toUpperCase()}${word.slice(1)}`)
|
||||
.join(' ');
|
||||
queryParamsByKey[key] = { label, tags: [] };
|
||||
|
||||
if (Array.isArray(queryParams[key])) {
|
||||
queryParams[key].forEach(val =>
|
||||
queryParamsArr.push({ key, value: val, label })
|
||||
queryParamsByKey[key].tags.push(val)
|
||||
);
|
||||
} else {
|
||||
queryParamsArr.push({ key, value: queryParams[key], label });
|
||||
queryParamsByKey[key].tags.push(queryParams[key]);
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
queryParamsArr.length > 0 && (
|
||||
<FilterTagsRow>
|
||||
<ResultCount>{i18n._(t`${itemCount} results`)}</ResultCount>
|
||||
<VerticalSeparator />
|
||||
<FilterLabel>{i18n._(t`Active Filters:`)}</FilterLabel>
|
||||
<ChipGroup defaultIsOpen>
|
||||
{queryParamsArr.map(({ key, label, value }) => (
|
||||
<Chip
|
||||
className="searchTagChip"
|
||||
key={`${key}__${value}`}
|
||||
isReadOnly={false}
|
||||
onClick={() => onRemove(key, value)}
|
||||
>
|
||||
<b>{label}:</b> {value}
|
||||
</Chip>
|
||||
))}
|
||||
<div className="pf-c-chip pf-m-overflow">
|
||||
<Button
|
||||
variant="plain"
|
||||
type="button"
|
||||
aria-label={i18n._(t`Clear all search filters`)}
|
||||
onClick={onRemoveAll}
|
||||
>
|
||||
<span className="pf-c-chip__text">{i18n._(t`Clear all`)}</span>
|
||||
Object.keys(queryParamsByKey).length > 0 && (
|
||||
<Fragment>
|
||||
<DataToolbarGroup>
|
||||
<ResultCount>{i18n._(t`${itemCount} results`)}</ResultCount>
|
||||
</DataToolbarGroup>
|
||||
<DataToolbarGroup>
|
||||
<FilterLabel>{i18n._(t`Active Filters:`)}</FilterLabel>
|
||||
<DataToolbarItem variant="chip-group">
|
||||
{Object.keys(queryParamsByKey).map(key => (
|
||||
<ChipGroup withToolbar key={`${key}-group`}>
|
||||
<ChipGroupToolbarItem key={key} categoryName={queryParamsByKey[key].label}>
|
||||
{queryParamsByKey[key].tags.map(chip => (
|
||||
<Chip key={chip} onClick={() => onRemove(key, chip)}>
|
||||
{chip}
|
||||
</Chip>
|
||||
))}
|
||||
</ChipGroupToolbarItem>
|
||||
</ChipGroup>
|
||||
))}
|
||||
</DataToolbarItem>
|
||||
<DataToolbarItem>
|
||||
<Button variant="link" onClick={onRemoveAll} isInline>
|
||||
{i18n._(t`Clear all search filters`)}
|
||||
</Button>
|
||||
</div>
|
||||
</ChipGroup>
|
||||
</FilterTagsRow>
|
||||
</DataToolbarItem>
|
||||
</DataToolbarGroup>
|
||||
</Fragment>
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
@ -2,8 +2,8 @@ import React, { Fragment } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import styled from 'styled-components';
|
||||
import { DataToolbar, DataToolbarContent } from '@patternfly/react-core/dist/esm/experimental';
|
||||
import DataListToolbar from '@components/DataListToolbar';
|
||||
import FilterTags from '@components/FilterTags';
|
||||
|
||||
import {
|
||||
encodeNonDefaultQueryString,
|
||||
@ -84,33 +84,32 @@ class ListHeader extends React.Component {
|
||||
return (
|
||||
<Fragment>
|
||||
{isEmpty ? (
|
||||
<Fragment>
|
||||
<EmptyStateControlsWrapper>
|
||||
{emptyStateControls}
|
||||
</EmptyStateControlsWrapper>
|
||||
<FilterTags
|
||||
itemCount={itemCount}
|
||||
qsConfig={qsConfig}
|
||||
onRemove={this.handleRemove}
|
||||
onRemoveAll={this.handleRemoveAll}
|
||||
/>
|
||||
</Fragment>
|
||||
<DataToolbar id={`${qsConfig.namespace}-list-toolbar`}
|
||||
clearAllFilters={this.handleRemoveAll}
|
||||
collapseListedFiltersBreakpoint="md"
|
||||
>
|
||||
<DataToolbarContent>
|
||||
<EmptyStateControlsWrapper>
|
||||
{emptyStateControls}
|
||||
</EmptyStateControlsWrapper>
|
||||
</DataToolbarContent>
|
||||
</DataToolbar>
|
||||
) : (
|
||||
<Fragment>
|
||||
{renderToolbar({
|
||||
searchColumns,
|
||||
sortColumns,
|
||||
onSearch: this.handleSearch,
|
||||
onSort: this.handleSort,
|
||||
qsConfig,
|
||||
})}
|
||||
<FilterTags
|
||||
itemCount={itemCount}
|
||||
qsConfig={qsConfig}
|
||||
onRemove={this.handleRemove}
|
||||
onRemoveAll={this.handleRemoveAll}
|
||||
/>
|
||||
</Fragment>
|
||||
<DataToolbar id={`${qsConfig.namespace}-list-toolbar`}
|
||||
clearAllFilters={this.handleRemoveAll}
|
||||
collapseListedFiltersBreakpoint="xl"
|
||||
>
|
||||
<DataToolbarContent>
|
||||
{renderToolbar({
|
||||
searchColumns,
|
||||
sortColumns,
|
||||
onSearch: this.handleSearch,
|
||||
onSort: this.handleSort,
|
||||
onRemove: this.handleRemove,
|
||||
qsConfig,
|
||||
})}
|
||||
</DataToolbarContent>
|
||||
</DataToolbar>
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
@ -2,80 +2,33 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { withI18n } from '@lingui/react';
|
||||
import { t } from '@lingui/macro';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import {
|
||||
Button as PFButton,
|
||||
Dropdown as PFDropdown,
|
||||
Button,
|
||||
ButtonVariant,
|
||||
Dropdown,
|
||||
DropdownPosition,
|
||||
DropdownToggle,
|
||||
DropdownItem,
|
||||
Form,
|
||||
FormGroup,
|
||||
TextInput as PFTextInput,
|
||||
InputGroup,
|
||||
TextInput,
|
||||
} from '@patternfly/react-core';
|
||||
import {
|
||||
DataToolbarGroup,
|
||||
DataToolbarItem,
|
||||
DataToolbarFilter
|
||||
} from '@patternfly/react-core/dist/esm/experimental';
|
||||
import { SearchIcon } from '@patternfly/react-icons';
|
||||
|
||||
import { parseQueryString } from '@util/qs';
|
||||
import { QSConfig, SearchColumns } from '@types';
|
||||
|
||||
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`
|
||||
align-self: stretch;
|
||||
border: 1px solid var(--pf-global--BorderColor--200);
|
||||
border-top-left-radius: 3px;
|
||||
border-bottom-left-radius: 3px;
|
||||
padding: 3px 7px;
|
||||
border: 1px solid var(--pf-global--BorderColor--300);
|
||||
padding: 5px 15px;
|
||||
white-space: nowrap;
|
||||
`;
|
||||
|
||||
const InputFormGroup = styled(FormGroup)`
|
||||
flex: 1;
|
||||
border-bottom-color: var(--pf-global--BorderColor--200);
|
||||
`;
|
||||
|
||||
class Search extends React.Component {
|
||||
@ -94,6 +47,7 @@ class Search extends React.Component {
|
||||
this.handleDropdownToggle = this.handleDropdownToggle.bind(this);
|
||||
this.handleDropdownSelect = this.handleDropdownSelect.bind(this);
|
||||
this.handleSearch = this.handleSearch.bind(this);
|
||||
this.handleTextKeyDown = this.handleTextKeyDown.bind(this);
|
||||
}
|
||||
|
||||
handleDropdownToggle(isSearchDropdownOpen) {
|
||||
@ -134,9 +88,15 @@ class Search extends React.Component {
|
||||
this.setState({ searchValue });
|
||||
}
|
||||
|
||||
handleTextKeyDown(e) {
|
||||
if (e.key && e.key === 'Enter') {
|
||||
this.handleSearch(e);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { up } = DropdownPosition;
|
||||
const { columns, i18n } = this.props;
|
||||
const { columns, i18n, onRemove, qsConfig, location } = this.props;
|
||||
const { isSearchDropdownOpen, searchKey, searchValue } = this.state;
|
||||
const { name: searchColumnName } = columns.find(
|
||||
({ key }) => key === searchKey
|
||||
@ -150,65 +110,95 @@ class Search extends React.Component {
|
||||
</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 (
|
||||
<Form autoComplete="off">
|
||||
<div className="pf-c-input-group">
|
||||
<DataToolbarGroup variant="filter-group">
|
||||
<DataToolbarItem>
|
||||
{searchDropdownItems.length > 0 ? (
|
||||
<FormGroup
|
||||
fieldId="searchKeyDropdown"
|
||||
label={
|
||||
<span className="pf-screen-reader">
|
||||
{i18n._(t`Search key dropdown`)}
|
||||
</span>
|
||||
<Dropdown
|
||||
onToggle={this.handleDropdownToggle}
|
||||
onSelect={this.handleDropdownSelect}
|
||||
direction={up}
|
||||
toggle={
|
||||
<DropdownToggle
|
||||
id="awx-search"
|
||||
onToggle={this.handleDropdownToggle}
|
||||
style={{ width: '100%' }}
|
||||
>
|
||||
{searchColumnName}
|
||||
</DropdownToggle>
|
||||
}
|
||||
>
|
||||
<Dropdown
|
||||
onToggle={this.handleDropdownToggle}
|
||||
onSelect={this.handleDropdownSelect}
|
||||
direction={up}
|
||||
isOpen={isSearchDropdownOpen}
|
||||
toggle={
|
||||
<DropdownToggle
|
||||
id="awx-search"
|
||||
onToggle={this.handleDropdownToggle}
|
||||
>
|
||||
{searchColumnName}
|
||||
</DropdownToggle>
|
||||
}
|
||||
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
|
||||
>
|
||||
isOpen={isSearchDropdownOpen}
|
||||
dropdownItems={searchDropdownItems}
|
||||
style={{ width: '100%' }}
|
||||
/>) : (<NoOptionDropdown>{searchColumnName}</NoOptionDropdown>)}
|
||||
</DataToolbarItem>
|
||||
{columns.map(({key}) => (<DataToolbarFilter
|
||||
chips={chipsByKey[key] ? chipsByKey[key].chips : []}
|
||||
deleteChip={(unusedKey, val) => { onRemove(chipsByKey[key].key, val) }}
|
||||
categoryName={chipsByKey[key] ? chipsByKey[key].label : key}
|
||||
key={key}
|
||||
showToolbarItem={searchKey === key}
|
||||
>
|
||||
<InputGroup>
|
||||
<TextInput
|
||||
type="search"
|
||||
aria-label={i18n._(t`Search text input`)}
|
||||
value={searchValue}
|
||||
onChange={this.handleSearchInputChange}
|
||||
style={{ height: '30px' }}
|
||||
onKeyDown={this.handleTextKeyDown}
|
||||
/>
|
||||
</InputFormGroup>
|
||||
<Button
|
||||
variant="tertiary"
|
||||
type="submit"
|
||||
aria-label={i18n._(t`Search submit button`)}
|
||||
onClick={this.handleSearch}
|
||||
>
|
||||
<SearchIcon />
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
<Button
|
||||
variant={ButtonVariant.control}
|
||||
aria-label={i18n._(t`Search submit button`)}
|
||||
onClick={this.handleSearch}
|
||||
>
|
||||
<SearchIcon />
|
||||
</Button>
|
||||
</InputGroup>
|
||||
</DataToolbarFilter>))}
|
||||
</DataToolbarGroup>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -216,11 +206,13 @@ class Search extends React.Component {
|
||||
Search.propTypes = {
|
||||
qsConfig: QSConfig.isRequired,
|
||||
columns: SearchColumns.isRequired,
|
||||
onSearch: PropTypes.func
|
||||
onSearch: PropTypes.func,
|
||||
onRemove: PropTypes.func
|
||||
};
|
||||
|
||||
Search.defaultProps = {
|
||||
onSearch: null,
|
||||
onRemove: null
|
||||
};
|
||||
|
||||
export default withI18n()(Search);
|
||||
export default withI18n()(withRouter(Search));
|
||||
|
||||
@ -1,15 +1,16 @@
|
||||
import React from 'react';
|
||||
import React, { Fragment } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { withI18n } from '@lingui/react';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import { t } from '@lingui/macro';
|
||||
import {
|
||||
Button,
|
||||
Dropdown as PFDropdown,
|
||||
ButtonVariant,
|
||||
Dropdown,
|
||||
DropdownPosition,
|
||||
DropdownToggle,
|
||||
DropdownItem,
|
||||
Tooltip,
|
||||
InputGroup,
|
||||
} from '@patternfly/react-core';
|
||||
import {
|
||||
SortAlphaDownIcon,
|
||||
@ -18,58 +19,11 @@ import {
|
||||
SortNumericUpIcon,
|
||||
} from '@patternfly/react-icons';
|
||||
|
||||
import styled from 'styled-components';
|
||||
|
||||
import {
|
||||
parseQueryString
|
||||
} from '@util/qs';
|
||||
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 {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@ -165,12 +119,10 @@ class Sort extends React.Component {
|
||||
}
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Fragment>
|
||||
{sortDropdownItems.length > 0 && (
|
||||
<React.Fragment>
|
||||
<SortBy>{i18n._(t`Sort By`)}</SortBy>
|
||||
<InputGroup>
|
||||
<Dropdown
|
||||
style={{ marginRight: '10px' }}
|
||||
onToggle={this.handleDropdownToggle}
|
||||
onSelect={this.handleDropdownSelect}
|
||||
direction={up}
|
||||
@ -185,23 +137,16 @@ class Sort extends React.Component {
|
||||
}
|
||||
dropdownItems={sortDropdownItems}
|
||||
/>
|
||||
</React.Fragment>
|
||||
<Button
|
||||
variant={ButtonVariant.control}
|
||||
aria-label={i18n._(t`Reverse Sort Order`)}
|
||||
onClick={this.handleSort}
|
||||
>
|
||||
<SortIcon />
|
||||
</Button>
|
||||
</InputGroup>
|
||||
)}
|
||||
<Tooltip
|
||||
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>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@ const Separator = styled.span`
|
||||
display: inline-block;
|
||||
width: 1px;
|
||||
height: 30px;
|
||||
margin-right: 20px;
|
||||
margin-right: 27px;
|
||||
margin-left: 20px;
|
||||
background-color: #d7d7d7;
|
||||
vertical-align: middle;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user