mirror of
https://github.com/ansible/awx.git
synced 2026-01-11 01:57:35 -03:30
Convert last class components to functional components
Convert last class components to functional components
This commit is contained in:
parent
19b4849345
commit
1994eaa406
@ -1,4 +1,4 @@
|
||||
import React, { Component } from 'react';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Checkbox as PFCheckbox } from '@patternfly/react-core';
|
||||
import styled from 'styled-components';
|
||||
@ -17,27 +17,25 @@ const Checkbox = styled(PFCheckbox)`
|
||||
}
|
||||
`;
|
||||
|
||||
class CheckboxCard extends Component {
|
||||
render() {
|
||||
const { name, description, isSelected, onSelect, itemId } = this.props;
|
||||
return (
|
||||
<CheckboxWrapper>
|
||||
<Checkbox
|
||||
isChecked={isSelected}
|
||||
onChange={onSelect}
|
||||
aria-label={name}
|
||||
id={`checkbox-card-${itemId}`}
|
||||
label={
|
||||
<>
|
||||
<div style={{ fontWeight: 'bold' }}>{name}</div>
|
||||
<div>{description}</div>
|
||||
</>
|
||||
}
|
||||
value={itemId}
|
||||
/>
|
||||
</CheckboxWrapper>
|
||||
);
|
||||
}
|
||||
function CheckboxCard(props) {
|
||||
const { name, description, isSelected, onSelect, itemId } = props;
|
||||
return (
|
||||
<CheckboxWrapper>
|
||||
<Checkbox
|
||||
isChecked={isSelected}
|
||||
onChange={onSelect}
|
||||
aria-label={name}
|
||||
id={`checkbox-card-${itemId}`}
|
||||
label={
|
||||
<>
|
||||
<div style={{ fontWeight: 'bold' }}>{name}</div>
|
||||
<div>{description}</div>
|
||||
</>
|
||||
}
|
||||
value={itemId}
|
||||
/>
|
||||
</CheckboxWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
CheckboxCard.propTypes = {
|
||||
|
||||
@ -1,60 +1,45 @@
|
||||
import React, { Component } from 'react';
|
||||
import React from 'react';
|
||||
import PropTypes, { oneOfType, string, arrayOf } from 'prop-types';
|
||||
import { matchPath, Link, withRouter } from 'react-router-dom';
|
||||
import { matchPath, Link, useHistory } from 'react-router-dom';
|
||||
import { NavExpandable, NavItem } from '@patternfly/react-core';
|
||||
|
||||
class NavExpandableGroup extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const { routes } = this.props;
|
||||
function NavExpandableGroup(props) {
|
||||
const history = useHistory();
|
||||
const { groupId, groupTitle, routes } = props;
|
||||
|
||||
// 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);
|
||||
}
|
||||
// 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.
|
||||
const navItemPaths = routes.map(({ path }) => path);
|
||||
|
||||
isActiveGroup() {
|
||||
return this.navItemPaths.some(this.isActivePath);
|
||||
}
|
||||
const isActive = navItemPaths.some(isActivePath);
|
||||
|
||||
isActivePath(path) {
|
||||
const { history } = this.props;
|
||||
function isActivePath(path) {
|
||||
return Boolean(matchPath(history.location.pathname, { path }));
|
||||
}
|
||||
|
||||
render() {
|
||||
const { groupId, groupTitle, routes } = this.props;
|
||||
|
||||
if (routes.length === 1 && groupId === 'settings') {
|
||||
const [{ path }] = routes;
|
||||
return (
|
||||
<NavItem itemId={groupId} isActive={this.isActivePath(path)} key={path}>
|
||||
<Link to={path}>{groupTitle}</Link>
|
||||
</NavItem>
|
||||
);
|
||||
}
|
||||
|
||||
if (routes.length === 1 && groupId === 'settings') {
|
||||
const [{ path }] = routes;
|
||||
return (
|
||||
<NavExpandable
|
||||
isActive={this.isActiveGroup()}
|
||||
isExpanded
|
||||
groupId={groupId}
|
||||
title={groupTitle}
|
||||
>
|
||||
{routes.map(({ path, title }) => (
|
||||
<NavItem
|
||||
groupId={groupId}
|
||||
isActive={this.isActivePath(path)}
|
||||
key={path}
|
||||
>
|
||||
<Link to={path}>{title}</Link>
|
||||
</NavItem>
|
||||
))}
|
||||
</NavExpandable>
|
||||
<NavItem itemId={groupId} isActive={isActivePath(path)} key={path}>
|
||||
<Link to={path}>{groupTitle}</Link>
|
||||
</NavItem>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<NavExpandable
|
||||
isActive={isActive}
|
||||
isExpanded
|
||||
groupId={groupId}
|
||||
title={groupTitle}
|
||||
>
|
||||
{routes.map(({ path, title }) => (
|
||||
<NavItem groupId={groupId} isActive={isActivePath(path)} key={path}>
|
||||
<Link to={path}>{title}</Link>
|
||||
</NavItem>
|
||||
))}
|
||||
</NavExpandable>
|
||||
);
|
||||
}
|
||||
|
||||
NavExpandableGroup.propTypes = {
|
||||
@ -63,4 +48,4 @@ NavExpandableGroup.propTypes = {
|
||||
routes: arrayOf(PropTypes.object).isRequired,
|
||||
};
|
||||
|
||||
export default withRouter(NavExpandableGroup);
|
||||
export default NavExpandableGroup;
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
import React from 'react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { MemoryRouter, withRouter } from 'react-router-dom';
|
||||
import { mount } from 'enzyme';
|
||||
|
||||
import { Nav } from '@patternfly/react-core';
|
||||
import NavExpandableGroup from './NavExpandableGroup';
|
||||
import _NavExpandableGroup from './NavExpandableGroup';
|
||||
|
||||
const NavExpandableGroup = withRouter(_NavExpandableGroup);
|
||||
|
||||
describe('NavExpandableGroup', () => {
|
||||
test('initialization and render', () => {
|
||||
@ -21,47 +23,88 @@ describe('NavExpandableGroup', () => {
|
||||
/>
|
||||
</Nav>
|
||||
</MemoryRouter>
|
||||
)
|
||||
.find('NavExpandableGroup')
|
||||
.instance();
|
||||
).find('NavExpandableGroup');
|
||||
|
||||
expect(component.navItemPaths).toEqual(['/foo', '/bar', '/fiz']);
|
||||
expect(component.isActiveGroup()).toEqual(true);
|
||||
expect(component.find('NavItem').length).toEqual(3);
|
||||
let link = component.find('NavItem').at(0);
|
||||
expect(component.find('NavItem').at(0).prop('isActive')).toBeTruthy();
|
||||
expect(link.find('Link').prop('to')).toBe('/foo');
|
||||
|
||||
link = component.find('NavItem').at(1);
|
||||
expect(link.prop('isActive')).toBeFalsy();
|
||||
expect(link.find('Link').prop('to')).toBe('/bar');
|
||||
|
||||
link = component.find('NavItem').at(2);
|
||||
expect(link.prop('isActive')).toBeFalsy();
|
||||
expect(link.find('Link').prop('to')).toBe('/fiz');
|
||||
});
|
||||
|
||||
describe('isActivePath', () => {
|
||||
const params = [
|
||||
['/fo', '/foo', false],
|
||||
['/foo', '/foo', true],
|
||||
['/foo/1/bar/fiz', '/foo', true],
|
||||
['/foo/1/bar/fiz', 'foo', false],
|
||||
['/foo/1/bar/fiz', 'foo/', false],
|
||||
['/foo/1/bar/fiz', '/bar', false],
|
||||
['/foo/1/bar/fiz', '/fiz', false],
|
||||
];
|
||||
test('when location is /foo/1/bar/fiz isActive returns false', () => {
|
||||
const component = mount(
|
||||
<MemoryRouter initialEntries={['/foo/1/bar/fiz']}>
|
||||
<Nav aria-label="Test Navigation">
|
||||
<NavExpandableGroup
|
||||
groupId="test"
|
||||
groupTitle="Test"
|
||||
routes={[
|
||||
{ path: '/foo', title: 'Foo' },
|
||||
{ path: '/bar', title: 'Bar' },
|
||||
{ path: '/fiz', title: 'Fiz' },
|
||||
]}
|
||||
/>
|
||||
</Nav>
|
||||
</MemoryRouter>
|
||||
).find('NavExpandableGroup');
|
||||
|
||||
params.forEach(([location, path, expected]) => {
|
||||
test(`when location is ${location}, isActivePath('${path}') returns ${expected} `, () => {
|
||||
const component = mount(
|
||||
<MemoryRouter initialEntries={[location]}>
|
||||
<Nav aria-label="Test Navigation">
|
||||
<NavExpandableGroup
|
||||
groupId="test"
|
||||
groupTitle="Test"
|
||||
routes={[
|
||||
{ path: '/foo', title: 'Foo' },
|
||||
{ path: '/bar', title: 'Bar' },
|
||||
{ path: '/fiz', title: 'Fiz' },
|
||||
]}
|
||||
/>
|
||||
</Nav>
|
||||
</MemoryRouter>
|
||||
)
|
||||
.find('NavExpandableGroup')
|
||||
.instance();
|
||||
expect(component.find('NavItem').length).toEqual(3);
|
||||
const link = component.find('NavItem').at(0);
|
||||
expect(component.find('NavItem').at(0).prop('isActive')).toBeTruthy();
|
||||
expect(link.find('Link').prop('to')).toBe('/foo');
|
||||
});
|
||||
|
||||
expect(component.isActivePath(path)).toEqual(expected);
|
||||
});
|
||||
});
|
||||
test('when location is /fo isActive returns false', () => {
|
||||
const component = mount(
|
||||
<MemoryRouter initialEntries={['/fo']}>
|
||||
<Nav aria-label="Test Navigation">
|
||||
<NavExpandableGroup
|
||||
groupId="test"
|
||||
groupTitle="Test"
|
||||
routes={[
|
||||
{ path: '/foo', title: 'Foo' },
|
||||
{ path: '/bar', title: 'Bar' },
|
||||
{ path: '/fiz', title: 'Fiz' },
|
||||
]}
|
||||
/>
|
||||
</Nav>
|
||||
</MemoryRouter>
|
||||
).find('NavExpandableGroup');
|
||||
|
||||
expect(component.find('NavItem').length).toEqual(3);
|
||||
const link = component.find('NavItem').at(0);
|
||||
expect(component.find('NavItem').at(0).prop('isActive')).toBeFalsy();
|
||||
expect(link.find('Link').prop('to')).toBe('/foo');
|
||||
});
|
||||
|
||||
test('when location is /foo isActive returns true', () => {
|
||||
const component = mount(
|
||||
<MemoryRouter initialEntries={['/foo']}>
|
||||
<Nav aria-label="Test Navigation">
|
||||
<NavExpandableGroup
|
||||
groupId="test"
|
||||
groupTitle="Test"
|
||||
routes={[
|
||||
{ path: '/foo', title: 'Foo' },
|
||||
{ path: '/bar', title: 'Bar' },
|
||||
{ path: '/fiz', title: 'Fiz' },
|
||||
]}
|
||||
/>
|
||||
</Nav>
|
||||
</MemoryRouter>
|
||||
).find('NavExpandableGroup');
|
||||
|
||||
expect(component.find('NavItem').length).toEqual(3);
|
||||
const link = component.find('NavItem').at(0);
|
||||
expect(component.find('NavItem').at(0).prop('isActive')).toBeTruthy();
|
||||
expect(link.find('Link').prop('to')).toBe('/foo');
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,24 +1,17 @@
|
||||
import { Component } from 'react';
|
||||
import { useEffect } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
class AppendBody extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.el = document.createElement('div');
|
||||
}
|
||||
function AppendBody({ children }) {
|
||||
const el = document.createElement('div');
|
||||
|
||||
componentDidMount() {
|
||||
document.body.appendChild(this.el);
|
||||
}
|
||||
useEffect(() => {
|
||||
document.body.appendChild(el);
|
||||
return () => {
|
||||
document.body.removeChild(el);
|
||||
};
|
||||
}, [el]);
|
||||
|
||||
componentWillUnmount() {
|
||||
document.body.removeChild(this.el);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { children } = this.props;
|
||||
return ReactDOM.createPortal(children, this.el);
|
||||
}
|
||||
return ReactDOM.createPortal(children, el);
|
||||
}
|
||||
|
||||
export default AppendBody;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import { useHistory, useLocation } from 'react-router-dom';
|
||||
import styled from 'styled-components';
|
||||
import { Toolbar, ToolbarContent } from '@patternfly/react-core';
|
||||
|
||||
@ -24,122 +24,105 @@ const EmptyStateControlsWrapper = styled.div`
|
||||
margin-left: 20px;
|
||||
}
|
||||
`;
|
||||
class ListHeader extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
function ListHeader(props) {
|
||||
const { search, pathname } = useLocation();
|
||||
const history = useHistory();
|
||||
const {
|
||||
emptyStateControls,
|
||||
itemCount,
|
||||
pagination,
|
||||
qsConfig,
|
||||
relatedSearchableKeys,
|
||||
renderToolbar,
|
||||
searchColumns,
|
||||
searchableKeys,
|
||||
sortColumns,
|
||||
} = props;
|
||||
|
||||
this.handleSearch = this.handleSearch.bind(this);
|
||||
this.handleReplaceSearch = this.handleReplaceSearch.bind(this);
|
||||
this.handleSort = this.handleSort.bind(this);
|
||||
this.handleRemove = this.handleRemove.bind(this);
|
||||
this.handleRemoveAll = this.handleRemoveAll.bind(this);
|
||||
}
|
||||
|
||||
handleSearch(key, value) {
|
||||
const { location, qsConfig } = this.props;
|
||||
const params = parseQueryString(qsConfig, location.search);
|
||||
const qs = updateQueryString(qsConfig, location.search, {
|
||||
const handleSearch = (key, value) => {
|
||||
const params = parseQueryString(qsConfig, search);
|
||||
const qs = updateQueryString(qsConfig, search, {
|
||||
...mergeParams(params, { [key]: value }),
|
||||
page: 1,
|
||||
});
|
||||
this.pushHistoryState(qs);
|
||||
}
|
||||
pushHistoryState(qs);
|
||||
};
|
||||
|
||||
handleReplaceSearch(key, value) {
|
||||
const { location, qsConfig } = this.props;
|
||||
const qs = updateQueryString(qsConfig, location.search, {
|
||||
const handleReplaceSearch = (key, value) => {
|
||||
const qs = updateQueryString(qsConfig, search, {
|
||||
[key]: value,
|
||||
});
|
||||
this.pushHistoryState(qs);
|
||||
}
|
||||
pushHistoryState(qs);
|
||||
};
|
||||
|
||||
handleRemove(key, value) {
|
||||
const { location, qsConfig } = this.props;
|
||||
const oldParams = parseQueryString(qsConfig, location.search);
|
||||
const handleRemove = (key, value) => {
|
||||
const oldParams = parseQueryString(qsConfig, search);
|
||||
const updatedParams = removeParams(qsConfig, oldParams, {
|
||||
[key]: value,
|
||||
});
|
||||
const qs = updateQueryString(qsConfig, location.search, updatedParams);
|
||||
this.pushHistoryState(qs);
|
||||
}
|
||||
const qs = updateQueryString(qsConfig, search, updatedParams);
|
||||
pushHistoryState(qs);
|
||||
};
|
||||
|
||||
handleRemoveAll() {
|
||||
const { location, qsConfig } = this.props;
|
||||
const oldParams = parseQueryString(qsConfig, location.search);
|
||||
const handleRemoveAll = () => {
|
||||
const oldParams = parseQueryString(qsConfig, search);
|
||||
Object.keys(oldParams).forEach((key) => {
|
||||
oldParams[key] = null;
|
||||
});
|
||||
delete oldParams.page_size;
|
||||
delete oldParams.order_by;
|
||||
const qs = updateQueryString(qsConfig, location.search, oldParams);
|
||||
this.pushHistoryState(qs);
|
||||
}
|
||||
const qs = updateQueryString(qsConfig, search, oldParams);
|
||||
pushHistoryState(qs);
|
||||
};
|
||||
|
||||
handleSort(key, order) {
|
||||
const { location, qsConfig } = this.props;
|
||||
const qs = updateQueryString(qsConfig, location.search, {
|
||||
const handleSort = (key, order) => {
|
||||
const qs = updateQueryString(qsConfig, search, {
|
||||
order_by: order === 'ascending' ? key : `-${key}`,
|
||||
page: null,
|
||||
});
|
||||
this.pushHistoryState(qs);
|
||||
}
|
||||
pushHistoryState(qs);
|
||||
};
|
||||
|
||||
pushHistoryState(queryString) {
|
||||
const { history } = this.props;
|
||||
const { pathname } = history.location;
|
||||
const pushHistoryState = (queryString) => {
|
||||
history.push(queryString ? `${pathname}?${queryString}` : pathname);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
emptyStateControls,
|
||||
itemCount,
|
||||
searchColumns,
|
||||
searchableKeys,
|
||||
relatedSearchableKeys,
|
||||
sortColumns,
|
||||
renderToolbar,
|
||||
qsConfig,
|
||||
location,
|
||||
pagination,
|
||||
} = this.props;
|
||||
const params = parseQueryString(qsConfig, location.search);
|
||||
const isEmpty = itemCount === 0 && Object.keys(params).length === 0;
|
||||
return (
|
||||
<>
|
||||
{isEmpty ? (
|
||||
<Toolbar
|
||||
id={`${qsConfig.namespace}-list-toolbar`}
|
||||
clearAllFilters={this.handleRemoveAll}
|
||||
collapseListedFiltersBreakpoint="lg"
|
||||
>
|
||||
<ToolbarContent>
|
||||
<EmptyStateControlsWrapper>
|
||||
{emptyStateControls}
|
||||
</EmptyStateControlsWrapper>
|
||||
</ToolbarContent>
|
||||
</Toolbar>
|
||||
) : (
|
||||
<>
|
||||
{renderToolbar({
|
||||
itemCount,
|
||||
searchColumns,
|
||||
sortColumns,
|
||||
searchableKeys,
|
||||
relatedSearchableKeys,
|
||||
onSearch: this.handleSearch,
|
||||
onReplaceSearch: this.handleReplaceSearch,
|
||||
onSort: this.handleSort,
|
||||
onRemove: this.handleRemove,
|
||||
clearAllFilters: this.handleRemoveAll,
|
||||
qsConfig,
|
||||
pagination,
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
const params = parseQueryString(qsConfig, search);
|
||||
const isEmpty = itemCount === 0 && Object.keys(params).length === 0;
|
||||
return (
|
||||
<>
|
||||
{isEmpty ? (
|
||||
<Toolbar
|
||||
id={`${qsConfig.namespace}-list-toolbar`}
|
||||
clearAllFilters={handleRemoveAll}
|
||||
collapseListedFiltersBreakpoint="lg"
|
||||
>
|
||||
<ToolbarContent>
|
||||
<EmptyStateControlsWrapper>
|
||||
{emptyStateControls}
|
||||
</EmptyStateControlsWrapper>
|
||||
</ToolbarContent>
|
||||
</Toolbar>
|
||||
) : (
|
||||
<>
|
||||
{renderToolbar({
|
||||
itemCount,
|
||||
searchColumns,
|
||||
sortColumns,
|
||||
searchableKeys,
|
||||
relatedSearchableKeys,
|
||||
onSearch: handleSearch,
|
||||
onReplaceSearch: handleReplaceSearch,
|
||||
onSort: handleSort,
|
||||
onRemove: handleRemove,
|
||||
clearAllFilters: handleRemoveAll,
|
||||
qsConfig,
|
||||
pagination,
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
ListHeader.propTypes = {
|
||||
@ -159,4 +142,4 @@ ListHeader.defaultProps = {
|
||||
relatedSearchableKeys: [],
|
||||
};
|
||||
|
||||
export default withRouter(ListHeader);
|
||||
export default ListHeader;
|
||||
|
||||
@ -7,11 +7,15 @@ describe('ListHeader', () => {
|
||||
const qsConfig = {
|
||||
namespace: 'item',
|
||||
defaultParams: { page: 1, page_size: 5, order_by: 'foo' },
|
||||
integerFields: [],
|
||||
integerFields: ['id', 'page', 'page_size'],
|
||||
dateFields: ['modified', 'created'],
|
||||
};
|
||||
const renderToolbarFn = jest.fn();
|
||||
|
||||
test('initially renders without crashing', () => {
|
||||
const history = createMemoryHistory({
|
||||
initialEntries: ['/organizations/1/teams'],
|
||||
});
|
||||
const wrapper = mountWithContexts(
|
||||
<ListHeader
|
||||
itemCount={50}
|
||||
@ -21,7 +25,8 @@ describe('ListHeader', () => {
|
||||
]}
|
||||
sortColumns={[{ name: 'foo', key: 'foo' }]}
|
||||
renderToolbar={renderToolbarFn}
|
||||
/>
|
||||
/>,
|
||||
{ context: { router: { history } } }
|
||||
);
|
||||
expect(wrapper.length).toBe(1);
|
||||
});
|
||||
|
||||
@ -137,14 +137,14 @@ function PaginatedTable({
|
||||
return (
|
||||
<>
|
||||
<ListHeader
|
||||
itemCount={itemCount}
|
||||
renderToolbar={renderToolbar}
|
||||
emptyStateControls={emptyStateControls}
|
||||
itemCount={itemCount}
|
||||
pagination={ToolbarPagination}
|
||||
qsConfig={qsConfig}
|
||||
relatedSearchableKeys={toolbarRelatedSearchableKeys}
|
||||
renderToolbar={renderToolbar}
|
||||
searchColumns={searchColumns}
|
||||
searchableKeys={toolbarSearchableKeys}
|
||||
relatedSearchableKeys={toolbarRelatedSearchableKeys}
|
||||
qsConfig={qsConfig}
|
||||
pagination={ToolbarPagination}
|
||||
/>
|
||||
{Content}
|
||||
{items.length ? (
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { Component } from 'react';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Chip, Split as PFSplit, SplitItem } from '@patternfly/react-core';
|
||||
|
||||
@ -16,42 +16,34 @@ const SplitLabelItem = styled(SplitItem)`
|
||||
word-break: initial;
|
||||
`;
|
||||
|
||||
class SelectedList extends Component {
|
||||
render() {
|
||||
const {
|
||||
label,
|
||||
selected,
|
||||
onRemove,
|
||||
displayKey,
|
||||
isReadOnly,
|
||||
renderItemChip,
|
||||
} = this.props;
|
||||
function SelectedList(props) {
|
||||
const { label, selected, onRemove, displayKey, isReadOnly, renderItemChip } =
|
||||
props;
|
||||
|
||||
const renderChip =
|
||||
renderItemChip ||
|
||||
(({ item, removeItem }) => (
|
||||
<Chip key={item.id} onClick={removeItem} isReadOnly={isReadOnly}>
|
||||
{item[displayKey]}
|
||||
</Chip>
|
||||
));
|
||||
const renderChip =
|
||||
renderItemChip ||
|
||||
(({ item, removeItem }) => (
|
||||
<Chip key={item.id} onClick={removeItem} isReadOnly={isReadOnly}>
|
||||
{item[displayKey]}
|
||||
</Chip>
|
||||
));
|
||||
|
||||
return (
|
||||
<Split>
|
||||
<SplitLabelItem>{label}</SplitLabelItem>
|
||||
<SplitItem>
|
||||
<ChipGroup numChips={5} totalChips={selected.length}>
|
||||
{selected.map((item) =>
|
||||
renderChip({
|
||||
item,
|
||||
removeItem: () => onRemove(item),
|
||||
canDelete: !isReadOnly,
|
||||
})
|
||||
)}
|
||||
</ChipGroup>
|
||||
</SplitItem>
|
||||
</Split>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Split>
|
||||
<SplitLabelItem>{label}</SplitLabelItem>
|
||||
<SplitItem>
|
||||
<ChipGroup numChips={5} totalChips={selected.length}>
|
||||
{selected.map((item) =>
|
||||
renderChip({
|
||||
item,
|
||||
removeItem: () => onRemove(item),
|
||||
canDelete: !isReadOnly,
|
||||
})
|
||||
)}
|
||||
</ChipGroup>
|
||||
</SplitItem>
|
||||
</Split>
|
||||
);
|
||||
}
|
||||
|
||||
SelectedList.propTypes = {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import React, { useState } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { PageSection, Card } from '@patternfly/react-core';
|
||||
|
||||
import { TeamsAPI } from 'api';
|
||||
@ -7,54 +7,48 @@ import { Config } from 'contexts/Config';
|
||||
import { CardBody } from 'components/Card';
|
||||
import TeamForm from '../shared/TeamForm';
|
||||
|
||||
class TeamAdd extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.handleSubmit = this.handleSubmit.bind(this);
|
||||
this.handleCancel = this.handleCancel.bind(this);
|
||||
this.state = { error: null };
|
||||
}
|
||||
function TeamAdd() {
|
||||
const [submitError, setSubmitError] = useState(null);
|
||||
const history = useHistory();
|
||||
|
||||
async handleSubmit(values) {
|
||||
const { history } = this.props;
|
||||
const handleSubmit = async (values) => {
|
||||
try {
|
||||
const valuesToSend = { ...values };
|
||||
valuesToSend.organization = valuesToSend.organization.id;
|
||||
const {
|
||||
name,
|
||||
description,
|
||||
organization: { id },
|
||||
} = values;
|
||||
const valuesToSend = { name, description, organization: id };
|
||||
const { data: response } = await TeamsAPI.create(valuesToSend);
|
||||
history.push(`/teams/${response.id}`);
|
||||
} catch (error) {
|
||||
this.setState({ error });
|
||||
setSubmitError(error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleCancel() {
|
||||
const { history } = this.props;
|
||||
const handleCancel = () => {
|
||||
history.push('/teams');
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { error } = this.state;
|
||||
|
||||
return (
|
||||
<PageSection>
|
||||
<Card>
|
||||
<CardBody>
|
||||
<Config>
|
||||
{({ me }) => (
|
||||
<TeamForm
|
||||
handleSubmit={this.handleSubmit}
|
||||
handleCancel={this.handleCancel}
|
||||
me={me || {}}
|
||||
submitError={error}
|
||||
/>
|
||||
)}
|
||||
</Config>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</PageSection>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<PageSection>
|
||||
<Card>
|
||||
<CardBody>
|
||||
<Config>
|
||||
{({ me }) => (
|
||||
<TeamForm
|
||||
handleSubmit={handleSubmit}
|
||||
handleCancel={handleCancel}
|
||||
me={me || {}}
|
||||
submitError={submitError}
|
||||
/>
|
||||
)}
|
||||
</Config>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</PageSection>
|
||||
);
|
||||
}
|
||||
|
||||
export { TeamAdd as _TeamAdd };
|
||||
export default withRouter(TeamAdd);
|
||||
export default TeamAdd;
|
||||
|
||||
@ -12,8 +12,7 @@ jest.mock('../../../api');
|
||||
|
||||
describe('<TeamAdd />', () => {
|
||||
test('handleSubmit should post to api', async () => {
|
||||
TeamsAPI.create.mockResolvedValueOnce({ data: {} });
|
||||
const wrapper = mountWithContexts(<TeamAdd />);
|
||||
const history = createMemoryHistory({});
|
||||
const updatedTeamData = {
|
||||
name: 'new name',
|
||||
description: 'new description',
|
||||
@ -22,6 +21,10 @@ describe('<TeamAdd />', () => {
|
||||
name: 'Default',
|
||||
},
|
||||
};
|
||||
TeamsAPI.create.mockResolvedValueOnce({ data: {} });
|
||||
const wrapper = mountWithContexts(<TeamAdd />, {
|
||||
context: { router: { history } },
|
||||
});
|
||||
await act(async () => {
|
||||
wrapper.find('TeamForm').invoke('handleSubmit')(updatedTeamData);
|
||||
});
|
||||
|
||||
@ -228,7 +228,7 @@ export function updateQueryString(config, queryString, newParams) {
|
||||
return encodeQueryString(allParams);
|
||||
}
|
||||
|
||||
function parseFullQueryString(queryString) {
|
||||
function parseFullQueryString(queryString = '') {
|
||||
const allParams = {};
|
||||
queryString
|
||||
.replace(/^\?/, '')
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user