Refactor breadcrumbs, tabs, routing.

* Cleanup previous params logic from Notificaitons list
* Add shared breadcrumbs and tabs components
* Structure Organization screens to render only cardBody content
* Add styles
* Fetch organization when location changes
This commit is contained in:
Marliana Lara
2019-01-24 23:53:10 -05:00
parent dd522c240e
commit b820e411d3
18 changed files with 363 additions and 356 deletions

View File

@@ -0,0 +1,70 @@
import React, { Fragment } from 'react';
import {
PageSection,
PageSectionVariants,
Breadcrumb,
BreadcrumbItem,
BreadcrumbHeading
} from '@patternfly/react-core';
import {
Link,
Route,
withRouter
} from 'react-router-dom';
import './breadcrumbs.scss';
const Breadcrumbs = ({ breadcrumbConfig }) => {
const { light } = PageSectionVariants;
return (
<PageSection
variant={light}
className="pf-m-condensed"
>
<Breadcrumb>
<Route
render={(props) => <Crumb breadcrumbConfig={breadcrumbConfig} {...props} />}
/>
</Breadcrumb>
</PageSection>
);
};
const Crumb = ({ breadcrumbConfig, match }) => {
const crumb = breadcrumbConfig[match.url];
let crumbElement = (
<BreadcrumbItem key={match.url}>
<Link to={match.url}>
{crumb}
</Link>
</BreadcrumbItem>
);
if (match.isExact) {
crumbElement = (
<BreadcrumbHeading
key="breadcrumb-heading"
className="heading"
>
{crumb}
</BreadcrumbHeading>
);
}
if (!crumb) {
crumbElement = null;
}
return (
<Fragment>
{crumbElement}
<Route
path={`${match.url}/:path`}
render={(props) => <Crumb breadcrumbConfig={breadcrumbConfig} {...props} />}
/>
</Fragment>
);
};
export default withRouter(Breadcrumbs);

View File

@@ -0,0 +1,15 @@
.pf-c-breadcrumb__item {
text-transform: capitalize;
}
.pf-c-breadcrumb__item.heading {
--pf-c-breadcrumb__heading--FontSize: 20px;
line-height: 24px;
flex: 100%;
}
.pf-c-breadcrumb__item:nth-last-child(2) {
.pf-c-breadcrumb__item-divider {
display: none;
}
}

View File

@@ -3,7 +3,7 @@
--awx-toolbar--BorderColor: var(--pf-global--Color--light-200);
--awx-toolbar--BorderWidth: var(--pf-global--BorderWidth--sm);
border: var(--awx-toolbar--BorderWidth) solid var(--awx-toolbar--BorderColor);
border-bottom: var(--awx-toolbar--BorderWidth) solid var(--awx-toolbar--BorderColor);
background-color: var(--awx-toolbar--BackgroundColor);
height: 70px;
padding-top: 5px;

View File

@@ -65,9 +65,7 @@ class Notifications extends Component {
componentDidMount () {
const queryParams = this.getQueryParams();
// TODO: remove this hack once tab query param is gone
const { tab, ...queryParamsWithoutTab } = queryParams;
this.fetchNotifications(queryParamsWithoutTab);
this.fetchNotifications(queryParams);
}
onSearch () {
@@ -81,10 +79,8 @@ class Notifications extends Component {
const { search } = location;
const searchParams = parseQueryString(search.substring(1));
// TODO: remove this hack once tab query param is gone
const { tab, ...queryParamsWithoutTab } = searchParams;
return Object.assign({}, this.defaultParams, queryParamsWithoutTab, overrides);
return Object.assign({}, this.defaultParams, searchParams, overrides);
}
onSort = (sortedColumnKey, sortOrder) => {
@@ -240,8 +236,6 @@ class Notifications extends Component {
}
this.setState(stateToUpdate);
// TODO: remove this hack once tab query param is gone
this.updateUrl({ ...queryParams, tab: 'notifications' });
const notificationTemplateIds = results
.map(notificationTemplate => notificationTemplate.id)
@@ -299,7 +293,7 @@ class Notifications extends Component {
<EmptyState>
<EmptyStateIcon icon={CubesIcon} />
<Title size="lg">
<Trans>No Notifictions Found</Trans>
<Trans>No Notifications Found</Trans>
</Title>
<EmptyStateBody>
<Trans>Please add a notification template to populate this list</Trans>
@@ -331,7 +325,7 @@ class Notifications extends Component {
itemId={o.id}
name={o.name}
notificationType={o.notification_type}
detailUrl={`notifications/${o.id}`}
detailUrl={`/notifications/${o.id}`}
isSelected={selected.includes(o.id)}
onSelect={() => this.onSelect(o.id)}
toggleNotification={this.toggleNotification}

View File

@@ -1,40 +1,18 @@
import React from 'react';
import { Link } from 'react-router-dom';
import { NavLink } 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>
);
};
const Tab = ({ children, link, replace }) => (
<li className="pf-c-tabs__item">
<NavLink
to={link}
replace={replace}
className="pf-c-tabs__button"
activeClassName="pf-m-current"
>
{children}
</NavLink>
</li>
);
export default Tab;

View File

@@ -1,11 +1,37 @@
import React from 'react';
import { Link } from 'react-router-dom';
import { Button } from '@patternfly/react-core';
import { TimesIcon } from '@patternfly/react-icons';
import Tooltip from '../Tooltip';
import './tabs.scss';
const Tabs = ({ children, labelText }) => (
<div className="pf-c-tabs" aria-label={labelText}>
const Tabs = ({ children, labelText, closeButton }) => (
<div
aria-label={labelText}
className="pf-c-tabs pf-u-flex-direction-row pf-u-justify-content-space-between"
>
<ul className="pf-c-tabs__list">
{children}
</ul>
{closeButton
&& (
<div className="pf-u-align-self-center">
<Tooltip
message={closeButton.text}
position="top"
>
<Link to={closeButton.link}>
<Button
variant="plain"
aria-label={closeButton.text}
>
<TimesIcon />
</Button>
</Link>
</Tooltip>
</div>
)
}
</div>
);

View File

@@ -1,9 +1,3 @@
.at-c-orgPane {
a {
display: block;
}
}
.pf-c-card__header {
--pf-c-card__header--PaddingBottom: 0;
--pf-c-card__header--PaddingX: 0;
@@ -29,22 +23,34 @@
.pf-c-tabs__button {
--pf-c-tabs__button--PaddingLeft: 20px;
--pf-c-tabs__button--PaddingRight: 20px;
}
display: block;
.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;
&:after {
content: '';
bottom: 0;
left: 0;
position: absolute;
right: 0;
top: 0;
}
}
.pf-c-tabs__item:not(.pf-m-current):hover
.pf-c-tabs__button::after {
border-top: none;
}
.pf-c-tabs__item:hover
.pf-c-tabs__button:not(.pf-m-current)::after {
border-bottom: 3px solid var(--pf-global--Color--dark-200);
border-top: none;
}
.pf-c-tabs__button.pf-m-current::after {
content: '';
border-bottom: 3px solid var(--pf-c-tabs__item--m-current--Color);
border-top: none;
margin-left: 1px;
}
}
.pf-c-breadcrumb__item.heading {
flex: 100%;
font-size: 20px;
}