mirror of
https://github.com/ansible/awx.git
synced 2026-02-02 10:08:10 -03:30
Merge remote-tracking branch 'origin/master' into lookup-form-component
This commit is contained in:
@@ -13,10 +13,8 @@ import heroImg from '@patternfly/patternfly-next/assets/images/pfbg_992.jpg';
|
||||
import brandImg from '../../images/tower-logo-white.svg';
|
||||
import logoImg from '../../images/tower-logo-login.svg';
|
||||
|
||||
import { ConfigContext } from '../context';
|
||||
|
||||
class About extends React.Component {
|
||||
createSpeechBubble = (version) => {
|
||||
static createSpeechBubble (version) {
|
||||
let text = `Tower ${version}`;
|
||||
let top = '';
|
||||
let bottom = '';
|
||||
@@ -33,52 +31,56 @@ class About extends React.Component {
|
||||
return top + text + bottom;
|
||||
}
|
||||
|
||||
handleModalToggle = () => {
|
||||
const { onAboutModalClose } = this.props;
|
||||
onAboutModalClose();
|
||||
};
|
||||
constructor (props) {
|
||||
super(props);
|
||||
|
||||
this.createSpeechBubble = this.constructor.createSpeechBubble.bind(this);
|
||||
}
|
||||
|
||||
render () {
|
||||
const { isOpen } = this.props;
|
||||
const {
|
||||
ansible_version,
|
||||
version,
|
||||
isOpen,
|
||||
onClose
|
||||
} = this.props;
|
||||
|
||||
const speechBubble = this.createSpeechBubble(version);
|
||||
|
||||
return (
|
||||
<I18n>
|
||||
{({ i18n }) => (
|
||||
<ConfigContext.Consumer>
|
||||
{({ ansible_version, version }) => (
|
||||
<AboutModal
|
||||
isOpen={isOpen}
|
||||
onClose={this.handleModalToggle}
|
||||
productName="Ansible Tower"
|
||||
trademark={i18n._(t`Copyright 2018 Red Hat, Inc.`)}
|
||||
brandImageSrc={brandImg}
|
||||
brandImageAlt={i18n._(t`Brand Image`)}
|
||||
logoImageSrc={logoImg}
|
||||
logoImageAlt={i18n._(t`AboutModal Logo`)}
|
||||
heroImageSrc={heroImg}
|
||||
>
|
||||
<pre>
|
||||
{this.createSpeechBubble(version)}
|
||||
{`
|
||||
<AboutModal
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
productName="Ansible Tower"
|
||||
trademark={i18n._(t`Copyright 2018 Red Hat, Inc.`)}
|
||||
brandImageSrc={brandImg}
|
||||
brandImageAlt={i18n._(t`Brand Image`)}
|
||||
logoImageSrc={logoImg}
|
||||
logoImageAlt={i18n._(t`AboutModal Logo`)}
|
||||
heroImageSrc={heroImg}
|
||||
>
|
||||
<pre>
|
||||
{ speechBubble }
|
||||
{`
|
||||
\\
|
||||
\\ ^__^
|
||||
\\ ^__^
|
||||
(oo)\\_______
|
||||
(__) A )\\
|
||||
||----w |
|
||||
|| ||
|
||||
`}
|
||||
</pre>
|
||||
|
||||
<TextContent>
|
||||
<TextList component="dl">
|
||||
<TextListItem component="dt">
|
||||
<Trans>Ansible Version</Trans>
|
||||
</TextListItem>
|
||||
<TextListItem component="dd">{ansible_version}</TextListItem>
|
||||
</TextList>
|
||||
</TextContent>
|
||||
</AboutModal>
|
||||
)}
|
||||
</ConfigContext.Consumer>
|
||||
</pre>
|
||||
<TextContent>
|
||||
<TextList component="dl">
|
||||
<TextListItem component="dt">
|
||||
<Trans>Ansible Version</Trans>
|
||||
</TextListItem>
|
||||
<TextListItem component="dd">{ ansible_version }</TextListItem>
|
||||
</TextList>
|
||||
</TextContent>
|
||||
</AboutModal>
|
||||
)}
|
||||
</I18n>
|
||||
);
|
||||
|
||||
25
src/components/Background.jsx
Normal file
25
src/components/Background.jsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import React, { Fragment } from 'react';
|
||||
|
||||
import {
|
||||
BackgroundImage,
|
||||
BackgroundImageSrc,
|
||||
} from '@patternfly/react-core';
|
||||
|
||||
const backgroundImageConfig = {
|
||||
[BackgroundImageSrc.lg]: '/assets/images/pfbg_1200.jpg',
|
||||
[BackgroundImageSrc.md]: '/assets/images/pfbg_992.jpg',
|
||||
[BackgroundImageSrc.md2x]: '/assets/images/pfbg_992@2x.jpg',
|
||||
[BackgroundImageSrc.sm]: '/assets/images/pfbg_768.jpg',
|
||||
[BackgroundImageSrc.sm2x]: '/assets/images/pfbg_768@2x.jpg',
|
||||
[BackgroundImageSrc.xl]: '/assets/images/pfbg_2000.jpg',
|
||||
[BackgroundImageSrc.xs]: '/assets/images/pfbg_576.jpg',
|
||||
[BackgroundImageSrc.xs2x]: '/assets/images/pfbg_576@2x.jpg',
|
||||
[BackgroundImageSrc.filter]: '/assets/images/background-filter.svg',
|
||||
};
|
||||
|
||||
export default ({ children }) => (
|
||||
<Fragment>
|
||||
<BackgroundImage src={backgroundImageConfig} />
|
||||
{ children }
|
||||
</Fragment>
|
||||
);
|
||||
@@ -1,23 +0,0 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Route,
|
||||
Redirect
|
||||
} from 'react-router-dom';
|
||||
|
||||
const ConditionalRedirect = ({
|
||||
component: Component,
|
||||
shouldRedirect,
|
||||
redirectPath,
|
||||
location,
|
||||
...props
|
||||
}) => (shouldRedirect() ? (
|
||||
<Redirect to={{
|
||||
pathname: redirectPath,
|
||||
state: { from: location }
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Route {...props} render={rest => (<Component {...rest} />)} />
|
||||
));
|
||||
|
||||
export default ConditionalRedirect;
|
||||
@@ -43,47 +43,66 @@ class DataListToolbar extends React.Component {
|
||||
searchKey: sortedColumnKey,
|
||||
searchValue: '',
|
||||
};
|
||||
|
||||
this.handleSearchInputChange = this.handleSearchInputChange.bind(this);
|
||||
this.onSortDropdownToggle = this.onSortDropdownToggle.bind(this);
|
||||
this.onSortDropdownSelect = this.onSortDropdownSelect.bind(this);
|
||||
this.onSearchDropdownToggle = this.onSearchDropdownToggle.bind(this);
|
||||
this.onSearchDropdownSelect = this.onSearchDropdownSelect.bind(this);
|
||||
this.onSearch = this.onSearch.bind(this);
|
||||
this.onSort = this.onSort.bind(this);
|
||||
}
|
||||
|
||||
handleSearchInputChange = searchValue => {
|
||||
handleSearchInputChange (searchValue) {
|
||||
this.setState({ searchValue });
|
||||
};
|
||||
}
|
||||
|
||||
onSortDropdownToggle = isSortDropdownOpen => {
|
||||
onSortDropdownToggle (isSortDropdownOpen) {
|
||||
this.setState({ isSortDropdownOpen });
|
||||
};
|
||||
}
|
||||
|
||||
onSortDropdownSelect = ({ target }) => {
|
||||
onSortDropdownSelect ({ target }) {
|
||||
const { columns, onSort, sortOrder } = this.props;
|
||||
const { innerText } = target;
|
||||
|
||||
const [{ key }] = columns.filter(({ name }) => name === target.innerText);
|
||||
const [{ key: searchKey }] = columns.filter(({ name }) => name === innerText);
|
||||
|
||||
this.setState({ isSortDropdownOpen: false });
|
||||
onSort(searchKey, sortOrder);
|
||||
}
|
||||
|
||||
onSort(key, sortOrder);
|
||||
};
|
||||
|
||||
onSearchDropdownToggle = isSearchDropdownOpen => {
|
||||
onSearchDropdownToggle (isSearchDropdownOpen) {
|
||||
this.setState({ isSearchDropdownOpen });
|
||||
};
|
||||
}
|
||||
|
||||
onSearchDropdownSelect = ({ target }) => {
|
||||
onSearchDropdownSelect ({ target }) {
|
||||
const { columns } = this.props;
|
||||
const { innerText } = target;
|
||||
|
||||
const targetName = target.innerText;
|
||||
const [{ key }] = columns.filter(({ name }) => name === targetName);
|
||||
const [{ key: searchKey }] = columns.filter(({ name }) => name === innerText);
|
||||
this.setState({ isSearchDropdownOpen: false, searchKey });
|
||||
}
|
||||
|
||||
this.setState({ isSearchDropdownOpen: false, searchKey: key });
|
||||
};
|
||||
onSearch () {
|
||||
const { searchValue } = this.state;
|
||||
const { onSearch } = this.props;
|
||||
|
||||
onSearch(searchValue);
|
||||
}
|
||||
|
||||
onSort () {
|
||||
const { onSort, sortedColumnKey, sortOrder } = this.props;
|
||||
const newSortOrder = sortOrder === 'ascending' ? 'descending' : 'ascending';
|
||||
|
||||
onSort(sortedColumnKey, newSortOrder);
|
||||
}
|
||||
|
||||
render () {
|
||||
const { up } = DropdownPosition;
|
||||
const {
|
||||
columns,
|
||||
isAllSelected,
|
||||
onSearch,
|
||||
onSelectAll,
|
||||
onSort,
|
||||
sortedColumnKey,
|
||||
sortOrder,
|
||||
addUrl,
|
||||
@@ -97,29 +116,15 @@ class DataListToolbar extends React.Component {
|
||||
searchValue,
|
||||
} = this.state;
|
||||
|
||||
const [searchColumn] = columns
|
||||
.filter(({ key }) => key === searchKey);
|
||||
const searchColumnName = searchColumn.name;
|
||||
|
||||
const [sortedColumn] = columns
|
||||
const [{ name: searchColumnName }] = columns.filter(({ key }) => key === searchKey);
|
||||
const [{ name: sortedColumnName, isNumeric }] = columns
|
||||
.filter(({ key }) => key === sortedColumnKey);
|
||||
const sortedColumnName = sortedColumn.name;
|
||||
const isSortNumeric = sortedColumn.isNumeric;
|
||||
const displayedSortIcon = () => {
|
||||
let icon;
|
||||
if (sortOrder === 'ascending') {
|
||||
icon = isSortNumeric ? (<SortNumericUpIcon />) : (<SortAlphaUpIcon />);
|
||||
} else {
|
||||
icon = isSortNumeric ? (<SortNumericDownIcon />) : (<SortAlphaDownIcon />);
|
||||
}
|
||||
return icon;
|
||||
};
|
||||
|
||||
const searchDropdownItems = columns
|
||||
.filter(({ key }) => key !== searchKey)
|
||||
.map(({ key, name }) => (
|
||||
<DropdownItem key={key} component="button">
|
||||
{ name }
|
||||
{name}
|
||||
</DropdownItem>
|
||||
));
|
||||
|
||||
@@ -127,17 +132,26 @@ class DataListToolbar extends React.Component {
|
||||
.filter(({ key, isSortable }) => isSortable && key !== sortedColumnKey)
|
||||
.map(({ key, name }) => (
|
||||
<DropdownItem key={key} component="button">
|
||||
{ name }
|
||||
{name}
|
||||
</DropdownItem>
|
||||
));
|
||||
|
||||
let SortIcon;
|
||||
if (isNumeric) {
|
||||
SortIcon = sortOrder === 'ascending' ? SortNumericUpIcon : SortNumericDownIcon;
|
||||
} else {
|
||||
SortIcon = sortOrder === 'ascending' ? SortAlphaUpIcon : SortAlphaDownIcon;
|
||||
}
|
||||
|
||||
return (
|
||||
<I18n>
|
||||
{({ i18n }) => (
|
||||
<div className="awx-toolbar">
|
||||
<Level>
|
||||
<LevelItem>
|
||||
<Toolbar style={{ marginLeft: '20px' }}>
|
||||
<Toolbar
|
||||
style={{ marginLeft: '20px' }}
|
||||
>
|
||||
<ToolbarGroup>
|
||||
<ToolbarItem>
|
||||
<Checkbox
|
||||
@@ -152,6 +166,7 @@ class DataListToolbar extends React.Component {
|
||||
<ToolbarItem>
|
||||
<div className="pf-c-input-group">
|
||||
<Dropdown
|
||||
className="searchKeyDropdown"
|
||||
onToggle={this.onSearchDropdownToggle}
|
||||
onSelect={this.onSearchDropdownSelect}
|
||||
direction={up}
|
||||
@@ -160,7 +175,7 @@ class DataListToolbar extends React.Component {
|
||||
<DropdownToggle
|
||||
onToggle={this.onSearchDropdownToggle}
|
||||
>
|
||||
{ searchColumnName }
|
||||
{searchColumnName}
|
||||
</DropdownToggle>
|
||||
)}
|
||||
dropdownItems={searchDropdownItems}
|
||||
@@ -174,14 +189,16 @@ class DataListToolbar extends React.Component {
|
||||
<Button
|
||||
variant="tertiary"
|
||||
aria-label={i18n._(t`Search`)}
|
||||
onClick={() => onSearch(searchValue)}
|
||||
onClick={this.onSearch}
|
||||
>
|
||||
<i className="fas fa-search" aria-hidden="true" />
|
||||
</Button>
|
||||
</div>
|
||||
</ToolbarItem>
|
||||
</ToolbarGroup>
|
||||
<ToolbarGroup>
|
||||
<ToolbarGroup
|
||||
className="sortDropdownGroup"
|
||||
>
|
||||
<ToolbarItem>
|
||||
<Dropdown
|
||||
onToggle={this.onSortDropdownToggle}
|
||||
@@ -192,7 +209,7 @@ class DataListToolbar extends React.Component {
|
||||
<DropdownToggle
|
||||
onToggle={this.onSortDropdownToggle}
|
||||
>
|
||||
{ sortedColumnName }
|
||||
{sortedColumnName}
|
||||
</DropdownToggle>
|
||||
)}
|
||||
dropdownItems={sortDropdownItems}
|
||||
@@ -200,23 +217,29 @@ class DataListToolbar extends React.Component {
|
||||
</ToolbarItem>
|
||||
<ToolbarItem>
|
||||
<Button
|
||||
onClick={() => onSort(sortedColumnKey, sortOrder === 'ascending' ? 'descending' : 'ascending')}
|
||||
onClick={this.onSort}
|
||||
variant="plain"
|
||||
aria-label={i18n._(t`Sort`)}
|
||||
>
|
||||
{displayedSortIcon()}
|
||||
<SortIcon/>
|
||||
</Button>
|
||||
</ToolbarItem>
|
||||
</ToolbarGroup>
|
||||
{ showExpandCollapse && (
|
||||
{showExpandCollapse && (
|
||||
<ToolbarGroup>
|
||||
<ToolbarItem>
|
||||
<Button variant="plain" aria-label={i18n._(t`Expand`)}>
|
||||
<Button
|
||||
variant="plain"
|
||||
aria-label={i18n._(t`Expand`)}
|
||||
>
|
||||
<BarsIcon />
|
||||
</Button>
|
||||
</ToolbarItem>
|
||||
<ToolbarItem>
|
||||
<Button variant="plain" aria-label={i18n._(t`Collapse`)}>
|
||||
<Button
|
||||
variant="plain"
|
||||
aria-label={i18n._(t`Collapse`)}
|
||||
>
|
||||
<EqualsIcon />
|
||||
</Button>
|
||||
</ToolbarItem>
|
||||
@@ -225,14 +248,23 @@ class DataListToolbar extends React.Component {
|
||||
</Toolbar>
|
||||
</LevelItem>
|
||||
<LevelItem>
|
||||
<Tooltip message={i18n._(t`Delete`)} position="top">
|
||||
<Button variant="plain" aria-label={i18n._(t`Delete`)}>
|
||||
<Tooltip
|
||||
message={i18n._(t`Delete`)}
|
||||
position="top"
|
||||
>
|
||||
<Button
|
||||
variant="plain"
|
||||
aria-label={i18n._(t`Delete`)}
|
||||
>
|
||||
<TrashAltIcon />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
{addUrl && (
|
||||
<Link to={addUrl}>
|
||||
<Button variant="primary" aria-label={i18n._(t`Add`)}>
|
||||
<Button
|
||||
variant="primary"
|
||||
aria-label={i18n._(t`Add`)}
|
||||
>
|
||||
<PlusIcon />
|
||||
</Button>
|
||||
</Link>
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import { Trans } from '@lingui/macro';
|
||||
import {
|
||||
Dropdown,
|
||||
DropdownItem,
|
||||
DropdownToggle,
|
||||
DropdownPosition,
|
||||
} from '@patternfly/react-core';
|
||||
import { QuestionCircleIcon } from '@patternfly/react-icons';
|
||||
import AboutModal from './About';
|
||||
|
||||
class HelpDropdown extends Component {
|
||||
state = {
|
||||
isOpen: false,
|
||||
showAboutModal: false
|
||||
};
|
||||
|
||||
render () {
|
||||
const { isOpen, showAboutModal } = this.state;
|
||||
const dropdownItems = [
|
||||
<DropdownItem
|
||||
href="https://docs.ansible.com/ansible-tower/latest/html/userguide/index.html"
|
||||
target="_blank"
|
||||
key="help"
|
||||
>
|
||||
<Trans>Help</Trans>
|
||||
</DropdownItem>,
|
||||
<DropdownItem
|
||||
onClick={() => this.setState({ showAboutModal: true })}
|
||||
key="about"
|
||||
>
|
||||
<Trans>About</Trans>
|
||||
</DropdownItem>
|
||||
];
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<Dropdown
|
||||
onSelect={() => this.setState({ isOpen: !isOpen })}
|
||||
toggle={(
|
||||
<DropdownToggle onToggle={(isToggleOpen) => this.setState({ isOpen: isToggleOpen })}>
|
||||
<QuestionCircleIcon />
|
||||
</DropdownToggle>
|
||||
)}
|
||||
isOpen={isOpen}
|
||||
dropdownItems={dropdownItems}
|
||||
position={DropdownPosition.right}
|
||||
/>
|
||||
{showAboutModal
|
||||
? (
|
||||
<AboutModal
|
||||
isOpen={showAboutModal}
|
||||
onAboutModalClose={() => this.setState({ showAboutModal: !showAboutModal })}
|
||||
/>
|
||||
)
|
||||
: null }
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default HelpDropdown;
|
||||
@@ -1,32 +0,0 @@
|
||||
import React from 'react';
|
||||
import { I18n } from '@lingui/react';
|
||||
import { t } from '@lingui/macro';
|
||||
|
||||
import {
|
||||
Button,
|
||||
ButtonVariant
|
||||
} from '@patternfly/react-core';
|
||||
|
||||
import { UserIcon } from '@patternfly/react-icons';
|
||||
|
||||
const LogoutButton = ({ onDevLogout }) => (
|
||||
<I18n>
|
||||
{({ i18n }) => (
|
||||
<Button
|
||||
id="button-logout"
|
||||
aria-label={i18n._(t`Logout`)}
|
||||
variant={ButtonVariant.plain}
|
||||
onClick={onDevLogout}
|
||||
onKeyDown={event => {
|
||||
if (event.keyCode === 13) {
|
||||
onDevLogout();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<UserIcon />
|
||||
</Button>
|
||||
)}
|
||||
</I18n>
|
||||
);
|
||||
|
||||
export default LogoutButton;
|
||||
@@ -14,18 +14,23 @@ class NavExpandableGroup extends Component {
|
||||
// Extract a list of paths from the route params and store them for later. This creates
|
||||
// an array of url paths associated with any NavItem component rendered by this component.
|
||||
this.navItemPaths = routes.map(({ path }) => path);
|
||||
|
||||
this.isActiveGroup = this.isActiveGroup.bind(this);
|
||||
this.isActivePath = this.isActivePath.bind(this);
|
||||
}
|
||||
|
||||
isActiveGroup = () => this.navItemPaths.some(this.isActivePath);
|
||||
isActiveGroup () {
|
||||
return this.navItemPaths.some(this.isActivePath);
|
||||
}
|
||||
|
||||
isActivePath = (path) => {
|
||||
isActivePath (path) {
|
||||
const { history } = this.props;
|
||||
|
||||
return history.location.pathname.startsWith(path);
|
||||
};
|
||||
}
|
||||
|
||||
render () {
|
||||
const { routes, groupId, staticContext, ...rest } = this.props;
|
||||
const { groupId, groupTitle, routes } = this.props;
|
||||
const isActive = this.isActiveGroup();
|
||||
|
||||
return (
|
||||
@@ -33,7 +38,7 @@ class NavExpandableGroup extends Component {
|
||||
isActive={isActive}
|
||||
isExpanded={isActive}
|
||||
groupId={groupId}
|
||||
{...rest}
|
||||
title={groupTitle}
|
||||
>
|
||||
{routes.map(({ path, title }) => (
|
||||
<NavItem
|
||||
|
||||
129
src/components/PageHeaderToolbar.jsx
Normal file
129
src/components/PageHeaderToolbar.jsx
Normal file
@@ -0,0 +1,129 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { t } from '@lingui/macro';
|
||||
import { I18n } from '@lingui/react';
|
||||
import {
|
||||
Dropdown,
|
||||
DropdownItem,
|
||||
DropdownToggle,
|
||||
DropdownPosition,
|
||||
Toolbar,
|
||||
ToolbarGroup,
|
||||
ToolbarItem,
|
||||
} from '@patternfly/react-core';
|
||||
import {
|
||||
QuestionCircleIcon,
|
||||
UserIcon,
|
||||
} from '@patternfly/react-icons';
|
||||
|
||||
const DOCLINK = 'https://docs.ansible.com/ansible-tower/latest/html/userguide/index.html';
|
||||
|
||||
class PageHeaderToolbar extends Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
this.state = { isHelpOpen: false, isUserOpen: false };
|
||||
|
||||
this.onHelpSelect = this.onHelpSelect.bind(this);
|
||||
this.onHelpToggle = this.onHelpToggle.bind(this);
|
||||
this.onUserSelect = this.onUserSelect.bind(this);
|
||||
this.onUserToggle = this.onUserToggle.bind(this);
|
||||
}
|
||||
|
||||
onHelpSelect () {
|
||||
const { isHelpOpen } = this.state;
|
||||
|
||||
this.setState({ isHelpOpen: !isHelpOpen });
|
||||
}
|
||||
|
||||
onUserSelect () {
|
||||
const { isUserOpen } = this.state;
|
||||
|
||||
this.setState({ isUserOpen: !isUserOpen });
|
||||
}
|
||||
|
||||
onHelpToggle (isOpen) {
|
||||
this.setState({ isHelpOpen: isOpen });
|
||||
}
|
||||
|
||||
onUserToggle (isOpen) {
|
||||
this.setState({ isUserOpen: isOpen });
|
||||
}
|
||||
|
||||
render () {
|
||||
const { isHelpOpen, isUserOpen } = this.state;
|
||||
const { isAboutDisabled, onAboutClick, onLogoutClick } = this.props;
|
||||
|
||||
return (
|
||||
<I18n>
|
||||
{({ i18n }) => (
|
||||
<Toolbar>
|
||||
<ToolbarGroup>
|
||||
<ToolbarItem>
|
||||
<Dropdown
|
||||
isOpen={isHelpOpen}
|
||||
position={DropdownPosition.right}
|
||||
onSelect={this.onHelpSelect}
|
||||
toggle={(
|
||||
<DropdownToggle
|
||||
onToggle={this.onHelpToggle}
|
||||
>
|
||||
<QuestionCircleIcon />
|
||||
</DropdownToggle>
|
||||
)}
|
||||
dropdownItems={[
|
||||
<DropdownItem
|
||||
key="help"
|
||||
target="_blank"
|
||||
href={DOCLINK}
|
||||
>
|
||||
{i18n._(t`Help`)}
|
||||
</DropdownItem>,
|
||||
<DropdownItem
|
||||
key="about"
|
||||
component="button"
|
||||
isDisabled={isAboutDisabled}
|
||||
onClick={onAboutClick}
|
||||
>
|
||||
{i18n._(t`About`)}
|
||||
</DropdownItem>
|
||||
]}
|
||||
/>
|
||||
</ToolbarItem>
|
||||
<ToolbarItem>
|
||||
<Dropdown
|
||||
isOpen={isUserOpen}
|
||||
position={DropdownPosition.right}
|
||||
onSelect={this.onUserSelect}
|
||||
toggle={(
|
||||
<DropdownToggle
|
||||
onToggle={this.onUserToggle}
|
||||
>
|
||||
<UserIcon />
|
||||
</DropdownToggle>
|
||||
)}
|
||||
dropdownItems={[
|
||||
<DropdownItem key="user">
|
||||
<Link to="/home">
|
||||
{i18n._(t`User Details`)}
|
||||
</Link>
|
||||
</DropdownItem>,
|
||||
<DropdownItem
|
||||
key="logout"
|
||||
component="button"
|
||||
onClick={onLogoutClick}
|
||||
>
|
||||
{i18n._(t`Logout`)}
|
||||
</DropdownItem>
|
||||
]}
|
||||
/>
|
||||
</ToolbarItem>
|
||||
</ToolbarGroup>
|
||||
</Toolbar>
|
||||
)}
|
||||
</I18n>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default PageHeaderToolbar;
|
||||
@@ -7,14 +7,9 @@ import {
|
||||
DropdownDirection,
|
||||
DropdownItem,
|
||||
DropdownToggle,
|
||||
Form,
|
||||
FormGroup,
|
||||
Level,
|
||||
LevelItem,
|
||||
TextInput,
|
||||
Toolbar,
|
||||
ToolbarGroup,
|
||||
ToolbarItem,
|
||||
Split,
|
||||
SplitItem,
|
||||
} from '@patternfly/react-core';
|
||||
@@ -23,24 +18,32 @@ class Pagination extends Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
|
||||
const { page } = this.props;
|
||||
|
||||
const { page } = props;
|
||||
this.state = { value: page, isOpen: false };
|
||||
|
||||
this.onPageChange = this.onPageChange.bind(this);
|
||||
this.onSubmit = this.onSubmit.bind(this);
|
||||
this.onFirst = this.onFirst.bind(this);
|
||||
this.onPrevious = this.onPrevious.bind(this);
|
||||
this.onNext = this.onNext.bind(this);
|
||||
this.onLast = this.onLast.bind(this);
|
||||
this.onTogglePageSize = this.onTogglePageSize.bind(this);
|
||||
this.onSelectPageSize = this.onSelectPageSize.bind(this);
|
||||
}
|
||||
|
||||
componentDidUpdate (prevProps) {
|
||||
const { page } = this.props;
|
||||
|
||||
if (prevProps.page !== page) {
|
||||
this.setState({ value: page });
|
||||
this.onPageChange(page);
|
||||
}
|
||||
}
|
||||
|
||||
onPageChange = value => {
|
||||
onPageChange (value) {
|
||||
this.setState({ value });
|
||||
};
|
||||
}
|
||||
|
||||
onSubmit = event => {
|
||||
onSubmit (event) {
|
||||
const { onSetPage, page, pageCount, page_size } = this.props;
|
||||
const { value } = this.state;
|
||||
|
||||
@@ -51,46 +54,42 @@ class Pagination extends Component {
|
||||
|
||||
if (isValid) {
|
||||
onSetPage(value, page_size);
|
||||
} else{
|
||||
} else {
|
||||
this.setState({ value: page });
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
onFirst = () => {
|
||||
const { onSetPage, page_size} = this.props;
|
||||
onFirst () {
|
||||
const { onSetPage, page_size } = this.props;
|
||||
|
||||
onSetPage(1, page_size);
|
||||
};
|
||||
}
|
||||
|
||||
onPrevious = () => {
|
||||
onPrevious () {
|
||||
const { onSetPage, page, page_size } = this.props;
|
||||
const previousPage = page - 1;
|
||||
|
||||
if (previousPage >= 1) {
|
||||
onSetPage(previousPage, page_size)
|
||||
}
|
||||
};
|
||||
onSetPage(previousPage, page_size);
|
||||
}
|
||||
|
||||
onNext = () => {
|
||||
onNext () {
|
||||
const { onSetPage, page, pageCount, page_size } = this.props;
|
||||
const nextPage = page + 1;
|
||||
|
||||
if (nextPage <= pageCount) {
|
||||
onSetPage(nextPage, page_size)
|
||||
}
|
||||
};
|
||||
onSetPage(nextPage, page_size);
|
||||
}
|
||||
|
||||
onLast = () => {
|
||||
onLast () {
|
||||
const { onSetPage, pageCount, page_size } = this.props;
|
||||
|
||||
onSetPage(pageCount, page_size)
|
||||
};
|
||||
}
|
||||
|
||||
onTogglePageSize = isOpen => {
|
||||
onTogglePageSize (isOpen) {
|
||||
this.setState({ isOpen });
|
||||
};
|
||||
}
|
||||
|
||||
onSelectPageSize = ({ target }) => {
|
||||
onSelectPageSize ({ target }) {
|
||||
const { onSetPage } = this.props;
|
||||
|
||||
const page = 1;
|
||||
@@ -99,7 +98,7 @@ class Pagination extends Component {
|
||||
this.setState({ isOpen: false });
|
||||
|
||||
onSetPage(page, page_size);
|
||||
};
|
||||
}
|
||||
|
||||
render () {
|
||||
const { up } = DropdownDirection;
|
||||
@@ -140,22 +139,28 @@ class Pagination extends Component {
|
||||
isOpen={isOpen}
|
||||
toggle={(
|
||||
<DropdownToggle
|
||||
onToggle={this.onTogglePageSize}>
|
||||
{ page_size }
|
||||
className="togglePageSize"
|
||||
onToggle={this.onTogglePageSize}
|
||||
>
|
||||
{page_size}
|
||||
</DropdownToggle>
|
||||
)}>
|
||||
)}
|
||||
>
|
||||
{opts.map(option => (
|
||||
<DropdownItem key={option} component="button">
|
||||
{ option }
|
||||
<DropdownItem
|
||||
key={option}
|
||||
component="button"
|
||||
>
|
||||
{option}
|
||||
</DropdownItem>
|
||||
))}
|
||||
</Dropdown>
|
||||
<Trans>Per Page</Trans>
|
||||
<Trans> Per Page</Trans>
|
||||
</LevelItem>
|
||||
<LevelItem>
|
||||
<Split gutter="md" className="pf-u-display-flex pf-u-align-items-center">
|
||||
<SplitItem>
|
||||
<Trans>{ itemMin } - { itemMax } of { count }</Trans>
|
||||
<Trans>{itemMin} - {itemMax} of {count}</Trans>
|
||||
</SplitItem>
|
||||
<SplitItem>
|
||||
<div className="pf-c-input-group">
|
||||
@@ -196,7 +201,7 @@ class Pagination extends Component {
|
||||
value={value}
|
||||
type="text"
|
||||
onChange={this.onPageChange}
|
||||
/> of { pageCount }
|
||||
/> of {pageCount}
|
||||
</Trans>
|
||||
</form>
|
||||
</SplitItem>
|
||||
|
||||
40
src/components/Tabs/Tab.jsx
Normal file
40
src/components/Tabs/Tab.jsx
Normal file
@@ -0,0 +1,40 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import './tabs.scss';
|
||||
|
||||
|
||||
const Tab = ({ location, match, tab, currentTab, children, breadcrumb }) => {
|
||||
const tabClasses = () => {
|
||||
let classes = 'pf-c-tabs__item';
|
||||
if (tab === currentTab) {
|
||||
classes += ' pf-m-current';
|
||||
}
|
||||
|
||||
return classes;
|
||||
};
|
||||
|
||||
const tabParams = () => {
|
||||
const params = new URLSearchParams(location.search);
|
||||
if (params.get('tab') !== undefined) {
|
||||
params.set('tab', tab);
|
||||
} else {
|
||||
params.append('tab', tab);
|
||||
}
|
||||
|
||||
return `?${params.toString()}`;
|
||||
};
|
||||
|
||||
return (
|
||||
<li className={tabClasses()}>
|
||||
<Link
|
||||
className={'pf-c-tabs__button'}
|
||||
to={{ pathname: `${match.url}`, search: tabParams(), state: { breadcrumb } }}
|
||||
replace={tab === currentTab}>
|
||||
{children}
|
||||
</Link>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
export default Tab;
|
||||
13
src/components/Tabs/Tabs.jsx
Normal file
13
src/components/Tabs/Tabs.jsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import React from 'react';
|
||||
import './tabs.scss';
|
||||
|
||||
|
||||
const Tabs = ({ children, labelText }) => (
|
||||
<div className="pf-c-tabs" aria-label={labelText}>
|
||||
<ul className="pf-c-tabs__list">
|
||||
{children}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default Tabs;
|
||||
50
src/components/Tabs/tabs.scss
Normal file
50
src/components/Tabs/tabs.scss
Normal file
@@ -0,0 +1,50 @@
|
||||
.at-c-orgPane {
|
||||
a {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.pf-c-card__header {
|
||||
--pf-c-card__header--PaddingBottom: 0;
|
||||
--pf-c-card__header--PaddingX: 0;
|
||||
--pf-c-card__header--PaddingTop: 0;
|
||||
}
|
||||
|
||||
.pf-c-tabs {
|
||||
--pf-global--link--Color: #484848;
|
||||
--pf-global--link--Color--hover: #484848;
|
||||
--pf-global--link--TextDecoration--hover: none;
|
||||
|
||||
&:before {
|
||||
border-bottom: 1px solid var(--pf-c-tabs__item--BorderColor);
|
||||
border-top: 1px solid var(--pf-c-tabs__item--BorderColor);
|
||||
bottom: 0;
|
||||
content: " ";
|
||||
left: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.pf-c-tabs__button {
|
||||
--pf-c-tabs__button--PaddingLeft: 20px;
|
||||
--pf-c-tabs__button--PaddingRight: 20px;
|
||||
}
|
||||
|
||||
.pf-c-tabs__item.pf-m-current
|
||||
.pf-c-tabs__button::after {
|
||||
border-bottom: 3px solid var(--pf-c-tabs__item--m-current--Color);
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.pf-c-tabs__item:not(.pf-m-current):hover
|
||||
.pf-c-tabs__button::after {
|
||||
border-bottom: 3px solid var(--pf-global--Color--dark-200);
|
||||
border-top: none;
|
||||
}
|
||||
}
|
||||
|
||||
.pf-c-breadcrumb__item.heading {
|
||||
flex: 100%;
|
||||
font-size: 20px;
|
||||
}
|
||||
@@ -12,31 +12,31 @@ class TowerLogo extends Component {
|
||||
super(props);
|
||||
|
||||
this.state = { hover: false };
|
||||
|
||||
this.onClick = this.onClick.bind(this);
|
||||
this.onHover = this.onHover.bind(this);
|
||||
}
|
||||
|
||||
onClick = () => {
|
||||
const { history, onClick: handleClick } = this.props;
|
||||
onClick () {
|
||||
const { history, linkTo } = this.props;
|
||||
|
||||
if (!handleClick) return;
|
||||
if (!linkTo) return;
|
||||
|
||||
history.push('/');
|
||||
history.push(linkTo);
|
||||
}
|
||||
|
||||
handleClick();
|
||||
};
|
||||
|
||||
onHover = () => {
|
||||
onHover () {
|
||||
const { hover } = this.state;
|
||||
|
||||
this.setState({ hover: !hover });
|
||||
};
|
||||
}
|
||||
|
||||
render () {
|
||||
const { hover } = this.state;
|
||||
const { onClick: handleClick } = this.props;
|
||||
|
||||
let src = TowerLogoHeader;
|
||||
|
||||
if (hover && handleClick) {
|
||||
if (hover) {
|
||||
src = TowerLogoHeaderHover;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user