assorted ui_next search phase 1 pr feedback updates

- remove unnecessary displayAll prop from ChipGroup
- update notification api fn to be 2 with no boolean param
- fix params passing to api functions
This commit is contained in:
John Mitchell
2019-07-30 13:57:16 -04:00
parent f0ff5b190a
commit 30253d21fc
12 changed files with 117 additions and 73 deletions

View File

@@ -108,8 +108,6 @@ Similar to the way the list grabs data based on changes to the react-router para
Currently the filter tags do not display the key, though that data is available and they could very easily do so. Currently the filter tags do not display the key, though that data is available and they could very easily do so.
Some small changes were made to our custom ChipGroup component to make this possible--a new displayAll bool prop which bypasses all chip hiding based on number of chips.
## QS Updates (and supporting duplicate keys) ## QS Updates (and supporting duplicate keys)
The logic that was updated to handle search tags can be found in the qs.js util file. The logic that was updated to handle search tags can be found in the qs.js util file.

View File

@@ -14,14 +14,14 @@ const NotificationsMixin = parent =>
readNotificationTemplatesSuccess(id, params) { readNotificationTemplatesSuccess(id, params) {
return this.http.get( return this.http.get(
`${this.baseUrl}${id}/notification_templates_success/`, `${this.baseUrl}${id}/notification_templates_success/`,
params { params }
); );
} }
readNotificationTemplatesError(id, params) { readNotificationTemplatesError(id, params) {
return this.http.get( return this.http.get(
`${this.baseUrl}${id}/notification_templates_error/`, `${this.baseUrl}${id}/notification_templates_error/`,
params { params }
); );
} }
@@ -54,43 +54,58 @@ const NotificationsMixin = parent =>
} }
/** /**
* This is a helper method meant to simplify setting the "on" or "off" status of * This is a helper method meant to simplify setting the "on" status of
* a related notification. * a related notification.
* *
* @param[resourceId] - id of the base resource * @param[resourceId] - id of the base resource
* @param[notificationId] - id of the notification * @param[notificationId] - id of the notification
* @param[notificationType] - the type of notification, options are "success" and "error" * @param[notificationType] - the type of notification, options are "success" and "error"
* @param[associationState] - Boolean for associating or disassociating, options are true or false
*/ */
// eslint-disable-next-line max-len associateNotificationTemplate(
updateNotificationTemplateAssociation(
resourceId, resourceId,
notificationId, notificationId,
notificationType, notificationType
associationState
) { ) {
if (notificationType === 'success' && associationState === true) { if (notificationType === 'success') {
return this.associateNotificationTemplatesSuccess( return this.associateNotificationTemplatesSuccess(
resourceId, resourceId,
notificationId notificationId
); );
} }
if (notificationType === 'success' && associationState === false) { if (notificationType === 'error') {
return this.disassociateNotificationTemplatesSuccess(
resourceId,
notificationId
);
}
if (notificationType === 'error' && associationState === true) {
return this.associateNotificationTemplatesError( return this.associateNotificationTemplatesError(
resourceId, resourceId,
notificationId notificationId
); );
} }
if (notificationType === 'error' && associationState === false) { throw new Error(
`Unsupported notificationType for association: ${notificationType}`
);
}
/**
* This is a helper method meant to simplify setting the "off" status of
* a related notification.
*
* @param[resourceId] - id of the base resource
* @param[notificationId] - id of the notification
* @param[notificationType] - the type of notification, options are "success" and "error"
*/
disassociateNotificationTemplate(
resourceId,
notificationId,
notificationType
) {
if (notificationType === 'success') {
return this.disassociateNotificationTemplatesSuccess(
resourceId,
notificationId
);
}
if (notificationType === 'error') {
return this.disassociateNotificationTemplatesError( return this.disassociateNotificationTemplatesError(
resourceId, resourceId,
notificationId notificationId
@@ -98,7 +113,7 @@ const NotificationsMixin = parent =>
} }
throw new Error( throw new Error(
`Unsupported notificationType, associationState combination: ${notificationType}, ${associationState}` `Unsupported notificationType for disassociation: ${notificationType}`
); );
} }
}; };

View File

@@ -1,15 +1,9 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { number, bool } from 'prop-types'; import { number } from 'prop-types';
import styled from 'styled-components'; import styled from 'styled-components';
import Chip from './Chip'; import Chip from './Chip';
const ChipGroup = ({ const ChipGroup = ({ children, className, showOverflowAfter, ...props }) => {
children,
className,
showOverflowAfter,
displayAll,
...props
}) => {
const [isExpanded, setIsExpanded] = useState(!showOverflowAfter); const [isExpanded, setIsExpanded] = useState(!showOverflowAfter);
const toggleIsOpen = () => setIsExpanded(!isExpanded); const toggleIsOpen = () => setIsExpanded(!isExpanded);
@@ -26,8 +20,8 @@ const ChipGroup = ({
return ( return (
<ul className={`pf-c-chip-group ${className}`} {...props}> <ul className={`pf-c-chip-group ${className}`} {...props}>
{displayAll ? mappedChildren : mappedChildren.slice(0, numToShow)} {!showOverflowAfter ? mappedChildren : mappedChildren.slice(0, numToShow)}
{!displayAll && showOverflowToggle && ( {showOverflowAfter && showOverflowToggle && (
<Chip isOverflowChip onClick={toggleIsOpen} component="li"> <Chip isOverflowChip onClick={toggleIsOpen} component="li">
{isExpanded ? expandedText : collapsedText} {isExpanded ? expandedText : collapsedText}
</Chip> </Chip>
@@ -37,11 +31,9 @@ const ChipGroup = ({
}; };
ChipGroup.propTypes = { ChipGroup.propTypes = {
showOverflowAfter: number, showOverflowAfter: number,
displayAll: bool,
}; };
ChipGroup.defaultProps = { ChipGroup.defaultProps = {
showOverflowAfter: null, showOverflowAfter: null,
displayAll: false,
}; };
export default styled(ChipGroup)` export default styled(ChipGroup)`

View File

@@ -40,7 +40,6 @@ const FilterTags = ({
}) => { }) => {
const queryParams = parseQueryString(qsConfig, location.search); const queryParams = parseQueryString(qsConfig, location.search);
const queryParamsArr = []; const queryParamsArr = [];
const displayAll = true;
const nonDefaultParams = filterDefaultParams( const nonDefaultParams = filterDefaultParams(
Object.keys(queryParams), Object.keys(queryParams),
qsConfig qsConfig
@@ -59,7 +58,7 @@ const FilterTags = ({
<ResultCount>{`${itemCount} results`}</ResultCount> <ResultCount>{`${itemCount} results`}</ResultCount>
<VerticalSeparator /> <VerticalSeparator />
<FilterLabel>{i18n._(t`Active Filters:`)}</FilterLabel> <FilterLabel>{i18n._(t`Active Filters:`)}</FilterLabel>
<ChipGroup displayAll={displayAll}> <ChipGroup>
{queryParamsArr.map(({ key, value }) => ( {queryParamsArr.map(({ key, value }) => (
<Chip <Chip
className="searchTagChip" className="searchTagChip"

View File

@@ -41,7 +41,9 @@ class PaginatedDataList extends React.Component {
const { history, qsConfig } = this.props; const { history, qsConfig } = this.props;
const { search } = history.location; const { search } = history.location;
const oldParams = parseQueryString(qsConfig, search); const oldParams = parseQueryString(qsConfig, search);
this.pushHistoryState(addParams(qsConfig, oldParams, { page_size: pageSize })); this.pushHistoryState(
addParams(qsConfig, oldParams, { page_size: pageSize })
);
} }
pushHistoryState(params) { pushHistoryState(params) {

View File

@@ -75,7 +75,7 @@ class OrganizationAccess extends React.Component {
} = await OrganizationsAPI.readAccessList(organization.id, params); } = await OrganizationsAPI.readAccessList(organization.id, params);
this.setState({ itemCount, accessRecords }); this.setState({ itemCount, accessRecords });
} catch (err) { } catch (err) {
this.setState({ cotentError: err }); this.setState({ contentError: err });
} finally { } finally {
this.setState({ hasContentLoading: false }); this.setState({ hasContentLoading: false });
} }

View File

@@ -714,7 +714,6 @@ exports[`<OrganizationAccessItem /> initially renders succesfully 1`] = `
> >
<ChipGroup <ChipGroup
className="ChipGroup-sc-10zu8t0-0 ldAQsx" className="ChipGroup-sc-10zu8t0-0 ldAQsx"
displayAll={false}
showOverflowAfter={null} showOverflowAfter={null}
> >
<ul <ul

View File

@@ -151,12 +151,19 @@ class OrganizationNotifications extends Component {
this.setState({ toggleLoading: true }); this.setState({ toggleLoading: true });
try { try {
await OrganizationsAPI.updateNotificationTemplateAssociation( if (isCurrentlyOn) {
id, await OrganizationsAPI.disassociateNotificationTemplate(
notificationId, id,
status, notificationId,
!isCurrentlyOn status
); );
} else {
await OrganizationsAPI.associateNotificationTemplate(
id,
notificationId,
status
);
}
this.setState(stateUpdateFunction); this.setState(stateUpdateFunction);
} catch (err) { } catch (err) {
this.setState({ toggleError: true }); this.setState({ toggleError: true });

View File

@@ -96,9 +96,11 @@ describe('<OrganizationNotifications />', () => {
.find('Switch') .find('Switch')
.at(0) .at(0)
.prop('onChange')(); .prop('onChange')();
expect( expect(OrganizationsAPI.associateNotificationTemplate).toHaveBeenCalledWith(
OrganizationsAPI.updateNotificationTemplateAssociation 1,
).toHaveBeenCalledWith(1, 2, 'success', true); 2,
'success'
);
await sleep(0); await sleep(0);
wrapper.update(); wrapper.update();
expect( expect(
@@ -122,9 +124,11 @@ describe('<OrganizationNotifications />', () => {
.find('Switch') .find('Switch')
.at(1) .at(1)
.prop('onChange')(); .prop('onChange')();
expect( expect(OrganizationsAPI.associateNotificationTemplate).toHaveBeenCalledWith(
OrganizationsAPI.updateNotificationTemplateAssociation 1,
).toHaveBeenCalledWith(1, 1, 'error', true); 1,
'error'
);
await sleep(0); await sleep(0);
wrapper.update(); wrapper.update();
expect( expect(
@@ -149,8 +153,8 @@ describe('<OrganizationNotifications />', () => {
.at(0) .at(0)
.prop('onChange')(); .prop('onChange')();
expect( expect(
OrganizationsAPI.updateNotificationTemplateAssociation OrganizationsAPI.disassociateNotificationTemplate
).toHaveBeenCalledWith(1, 1, 'success', false); ).toHaveBeenCalledWith(1, 1, 'success');
await sleep(0); await sleep(0);
wrapper.update(); wrapper.update();
expect( expect(
@@ -175,8 +179,8 @@ describe('<OrganizationNotifications />', () => {
.at(1) .at(1)
.prop('onChange')(); .prop('onChange')();
expect( expect(
OrganizationsAPI.updateNotificationTemplateAssociation OrganizationsAPI.disassociateNotificationTemplate
).toHaveBeenCalledWith(1, 2, 'error', false); ).toHaveBeenCalledWith(1, 2, 'error');
await sleep(0); await sleep(0);
wrapper.update(); wrapper.update();
expect( expect(

View File

@@ -320,10 +320,7 @@ const getRemainingNewParams = (mergedParams, newParams) =>
* @return {object} query param object * @return {object} query param object
*/ */
export function addParams(config, oldParams, paramsToAdd) { export function addParams(config, oldParams, paramsToAdd) {
const namespacedOldParams = namespaceParams( const namespacedOldParams = namespaceParams(config.namespace, oldParams);
config.namespace,
oldParams
);
const namespacedParamsToAdd = namespaceParams(config.namespace, paramsToAdd); const namespacedParamsToAdd = namespaceParams(config.namespace, paramsToAdd);
const namespacedDefaultParams = namespaceParams( const namespacedDefaultParams = namespaceParams(
config.namespace, config.namespace,

View File

@@ -403,7 +403,11 @@ describe('qs (qs.js)', () => {
defaultParams: { page: 1, page_size: 15 }, defaultParams: { page: 1, page_size: 15 },
integerFields: ['page', 'page_size'], integerFields: ['page', 'page_size'],
}; };
const oldParams = { baz: ['bar', 'bang', 'bust'], page: 3, page_size: 15 }; const oldParams = {
baz: ['bar', 'bang', 'bust'],
page: 3,
page_size: 15,
};
const newParams = { baz: 'bar' }; const newParams = { baz: 'bar' };
expect(removeParams(config, oldParams, newParams)).toEqual({ expect(removeParams(config, oldParams, newParams)).toEqual({
baz: ['bang', 'bust'], baz: ['bang', 'bust'],
@@ -433,7 +437,12 @@ describe('qs (qs.js)', () => {
defaultParams: { page: 1, page_size: 15 }, defaultParams: { page: 1, page_size: 15 },
integerFields: ['page', 'page_size'], integerFields: ['page', 'page_size'],
}; };
const oldParams = { baz: ['bar', 'bang', 'bust'], pat: 'pal', page: 3, page_size: 15 }; const oldParams = {
baz: ['bar', 'bang', 'bust'],
pat: 'pal',
page: 3,
page_size: 15,
};
const newParams = { baz: 'bust', pat: 'pal' }; const newParams = { baz: 'bust', pat: 'pal' };
expect(removeParams(config, oldParams, newParams)).toEqual({ expect(removeParams(config, oldParams, newParams)).toEqual({
baz: ['bar', 'bang'], baz: ['bar', 'bang'],
@@ -491,7 +500,11 @@ describe('qs (qs.js)', () => {
defaultParams: { page: 1, page_size: 15 }, defaultParams: { page: 1, page_size: 15 },
integerFields: ['page', 'page_size'], integerFields: ['page', 'page_size'],
}; };
const oldParams = { baz: ['bar', 'bang', 'bust'], page: 3, page_size: 15 }; const oldParams = {
baz: ['bar', 'bang', 'bust'],
page: 3,
page_size: 15,
};
const newParams = { baz: 'bar' }; const newParams = { baz: 'bar' };
expect(removeParams(config, oldParams, newParams)).toEqual({ expect(removeParams(config, oldParams, newParams)).toEqual({
baz: ['bang', 'bust'], baz: ['bang', 'bust'],
@@ -521,7 +534,12 @@ describe('qs (qs.js)', () => {
defaultParams: { page: 1, page_size: 15 }, defaultParams: { page: 1, page_size: 15 },
integerFields: ['page', 'page_size'], integerFields: ['page', 'page_size'],
}; };
const oldParams = { baz: ['bar', 'bang', 'bust'], pat: 'pal', page: 3, page_size: 15 }; const oldParams = {
baz: ['bar', 'bang', 'bust'],
pat: 'pal',
page: 3,
page_size: 15,
};
const newParams = { baz: 'bust', pat: 'pal' }; const newParams = { baz: 'bust', pat: 'pal' };
expect(removeParams(config, oldParams, newParams)).toEqual({ expect(removeParams(config, oldParams, newParams)).toEqual({
baz: ['bar', 'bang'], baz: ['bar', 'bang'],

View File

@@ -8,23 +8,36 @@ export function describeNotificationMixin (Model, name) {
jest.clearAllMocks(); jest.clearAllMocks();
}); });
const parameters = [ const parameters = ['success', 'error'];
['success', true],
['success', false], parameters.forEach((type) => {
['error', true], const label = `[notificationType=${type}, associationState=true`;
['error', false], const testName = `associateNotificationTemplate ${label} makes expected http calls`;
];
parameters.forEach(([type, state]) => {
const label = `[notificationType=${type}, associationState=${state}]`;
const testName = `updateNotificationTemplateAssociation ${label} makes expected http calls`;
test(testName, async (done) => { test(testName, async (done) => {
await ModelAPI.updateNotificationTemplateAssociation(1, 21, type, state); await ModelAPI.associateNotificationTemplate(1, 21, type);
const expectedPath = `${ModelAPI.baseUrl}1/notification_templates_${type}/`; const expectedPath = `${ModelAPI.baseUrl}1/notification_templates_${type}/`;
expect(mockHttp.post).toHaveBeenCalledTimes(1); expect(mockHttp.post).toHaveBeenCalledTimes(1);
const expectedParams = state ? { id: 21 } : { id: 21, disassociate: true }; const expectedParams = { id: 21 };
expect(mockHttp.post.mock.calls.pop()).toEqual([expectedPath, expectedParams]);
done();
});
});
parameters.forEach((type) => {
const label = `[notificationType=${type}, associationState=false`;
const testName = `disassociateNotificationTemplate ${label} makes expected http calls`;
test(testName, async (done) => {
await ModelAPI.disassociateNotificationTemplate(1, 21, type);
const expectedPath = `${ModelAPI.baseUrl}1/notification_templates_${type}/`;
expect(mockHttp.post).toHaveBeenCalledTimes(1);
const expectedParams = { id: 21, disassociate: true };
expect(mockHttp.post.mock.calls.pop()).toEqual([expectedPath, expectedParams]); expect(mockHttp.post.mock.calls.pop()).toEqual([expectedPath, expectedParams]);
done(); done();