Skip to content

Commit

Permalink
chore: Brian ui fixes + fix service order (#3238)
Browse files Browse the repository at this point in the history
* UI feedback (#3218)

* UI enhancements per Jan

* Fix font sizes

* UI tweaks

* Design feedback UI

* change token

* remove token

* remove added code

---------

Co-authored-by: Brian Lee <brian-mf.lee@broadcom.com>

* fix conflicts

Signed-off-by: at670475 <andrea.tabone@broadcom.com>

* remove unused component

Signed-off-by: at670475 <andrea.tabone@broadcom.com>

* change

Signed-off-by: at670475 <andrea.tabone@broadcom.com>

* fix

Signed-off-by: at670475 <andrea.tabone@broadcom.com>

* sort tiles

Signed-off-by: at670475 <andrea.tabone@broadcom.com>

* revert

Signed-off-by: at670475 <andrea.tabone@broadcom.com>

* add tests

Signed-off-by: at670475 <andrea.tabone@broadcom.com>

* hide scrollbar

Signed-off-by: at670475 <andrea.tabone@broadcom.com>

* fix sonar

Signed-off-by: at670475 <andrea.tabone@broadcom.com>

* add action and reducers to be able to scroll after clicking on content icons

Signed-off-by: at670475 <andrea.tabone@broadcom.com>

* add tests

Signed-off-by: at670475 <andrea.tabone@broadcom.com>

* fix eslint

Signed-off-by: at670475 <andrea.tabone@broadcom.com>

* fix sonarcloud

Signed-off-by: at670475 <andrea.tabone@broadcom.com>

* set timeout to scrolling to avoid weird behaviour and address pr comments

Signed-off-by: at670475 <andrea.tabone@broadcom.com>

* fix test

Signed-off-by: at670475 <andrea.tabone@broadcom.com>

---------

Signed-off-by: at670475 <andrea.tabone@broadcom.com>
Co-authored-by: briandavid85 <blee1006@gmail.com>
Co-authored-by: Brian Lee <brian-mf.lee@broadcom.com>
  • Loading branch information
3 people authored Dec 21, 2023
1 parent e2f51b1 commit eff57b1
Show file tree
Hide file tree
Showing 22 changed files with 370 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* Copyright Contributors to the Zowe Project.
*/

import { CLEAR_SERVICE, SELECT_SERVICE } from '../constants/selected-service-constants';
import { CLEAR_SERVICE, SELECT_SERVICE, STORE_CONTENT_ANCHOR } from '../constants/selected-service-constants';

export function selectService(selectedService = {}, selectedTile = '') {
return {
Expand All @@ -25,3 +25,10 @@ export function clearService() {
selectedTile: '',
};
}

export function storeContentAnchor(id) {
return {
type: STORE_CONTENT_ANCHOR,
payload: id,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
* Copyright Contributors to the Zowe Project.
*/

import { CLEAR_SERVICE, SELECT_SERVICE } from '../constants/selected-service-constants';
import { selectService, clearService } from './selected-service-actions';
import { CLEAR_SERVICE, SELECT_SERVICE, STORE_CONTENT_ANCHOR } from '../constants/selected-service-constants';
import { selectService, clearService, storeContentAnchor } from './selected-service-actions';

describe('>>> Selected Service actions tests', () => {
it('should return selected service', () => {
Expand All @@ -31,4 +31,12 @@ describe('>>> Selected Service actions tests', () => {
};
expect(clearService()).toEqual(expectedAction);
});

it('should return store content anchor', () => {
const expectedAction = {
type: STORE_CONTENT_ANCHOR,
payload: '#id',
};
expect(storeContentAnchor('#id')).toEqual(expectedAction);
});
});
14 changes: 14 additions & 0 deletions api-catalog-ui/frontend/src/components/App/_app.scss
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,20 @@ body {
span {
font-size: var( --fontSmall );
}
.icon-img {
rect, path {
transition: fill .25s ease;
}
}

&:hover .icon-img {
rect {
fill: var( --criticalShade10 );
}
path {
fill: var( --surface05 );
}
}
}
}//end button-cta

Expand Down
77 changes: 64 additions & 13 deletions api-catalog-ui/frontend/src/components/Dashboard/Dashboard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import { Typography, IconButton, Snackbar } from '@material-ui/core';
import { Alert } from '@mui/material';
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Footer from '../Footer/Footer';
import SearchCriteria from '../Search/SearchCriteria';
import Shield from '../ErrorBoundary/Shield/Shield';
Expand All @@ -22,6 +23,7 @@ import DialogDropdown from '../Wizard/DialogDropdown';
import { enablerData } from '../Wizard/configs/wizard_onboarding_methods';
import ConfirmDialogContainer from '../Wizard/ConfirmDialogContainer';
import { customUIStyle, isAPIPortal } from '../../utils/utilFunctions';
import { sortServices } from '../../selectors/selectors';

const loadFeedbackButton = () => {
if (isAPIPortal()) {
Expand Down Expand Up @@ -71,6 +73,34 @@ export default class Dashboard extends Component {
closeAlert();
};

/**
* This method is used only in the portal in combination with CSS, to scroll the tiles.
* @param event
*/
dashboardTileScroll = (event) => {
if (isAPIPortal()) {
const gridHeader = document.querySelectorAll('.dashboard-grid-header')[0];
const getHeaderHeight = gridHeader?.offsetHeight;
const getFilterHeight = document.querySelectorAll('.filtering-container')[0]?.offsetHeight;

if (
gridHeader &&
getHeaderHeight &&
event.target &&
event.target.classList &&
event.target.scrollTop > getFilterHeight
) {
event.target.classList.add('fixed-header');
event.target.style.paddingTop = `${
getHeaderHeight + parseFloat(gridHeader.style.marginBottom) + parseFloat(gridHeader.style.marginTop)
}px`;
} else if (event.target?.classList) {
event.target.classList.remove('fixed-header');
event.target.style.paddingTop = 0;
}
}
};

render() {
const {
tiles,
Expand All @@ -83,6 +113,7 @@ export default class Dashboard extends Component {
clearError,
authentication,
storeCurrentTileId,
storeContentAnchor,
} = this.props;
const hasSearchCriteria =
typeof searchCriteria !== 'undefined' &&
Expand All @@ -100,6 +131,11 @@ export default class Dashboard extends Component {
if (hasTiles && 'customStyleConfig' in tiles[0] && tiles[0].customStyleConfig) {
customUIStyle(tiles[0].customStyleConfig);
}
let allServices;
if (hasTiles) {
allServices = sortServices(tiles);
}

return (
<div className="main-content dashboard-content">
{isAPIPortal() && <FeedbackButton />}
Expand Down Expand Up @@ -147,7 +183,12 @@ export default class Dashboard extends Component {
<ErrorDialog refreshedStaticApisError={refreshedStaticApisError} clearError={clearError} />
{!fetchTilesError && (
<div className="apis">
<div id="grid-container">
<div
id="grid-container"
onScroll={(e) => {
this.dashboardTileScroll(e);
}}
>
<div className="filtering-container">
{apiPortalEnabled && (
<div>
Expand All @@ -169,25 +210,28 @@ export default class Dashboard extends Component {
<div className="empty" />
<h4 className="description-header">Swagger</h4>
<h4 className="description-header">Use Cases</h4>
<h4 className="description-header">Tutorials</h4>
<h4 className="description-header">Videos</h4>
<h4 className="description-header">Getting Started</h4>
</div>
)}
<hr id="separator2" />

<div className="tile-container">
{isLoading && <div className="loadingDiv" />}

{hasTiles &&
tiles.map((tile) =>
tile.services.map((service) => (
<Tile
storeCurrentTileId={storeCurrentTileId}
service={service}
key={service}
tile={tile}
history={history}
/>
))
allServices.map((service) =>
tiles
.filter((tile) => tile.services.includes(service))
.map((tile) => (
<Tile
storeCurrentTileId={storeCurrentTileId}
storeContentAnchor={storeContentAnchor}
service={service}
key={service}
tile={tile}
history={history}
/>
))
)}
{!hasTiles && hasSearchCriteria && (
<Typography id="search_no_results" variant="subtitle2" className="no-content">
Expand All @@ -203,3 +247,10 @@ export default class Dashboard extends Component {
);
}
}

Dashboard.propTypes = {
tiles: PropTypes.shape({
filter: PropTypes.func.isRequired,
}).isRequired,
storeContentAnchor: PropTypes.func.isRequired,
};
Original file line number Diff line number Diff line change
Expand Up @@ -316,4 +316,98 @@ describe('>>> Dashboard component tests', () => {
expect(getByText('Feedback Button')).toBeInTheDocument();
});
});

it('should add fixed-header class and update padding when scrolled below filter height', () => {
process.env.REACT_APP_API_PORTAL = true;
const wrapper = shallow(
<Dashboard
tiles={null}
fetchTilesStart={jest.fn()}
fetchTilesStop={jest.fn()}
clearService={jest.fn()}
clear={jest.fn()}
assertAuthorization={jest.fn()}
authentication={jest.fn()}
/>
);
const instance = wrapper.instance();
const eventMock = {
target: {
scrollTop: 40,
classList: {
add: jest.fn(),
remove: jest.fn(),
},
style: {
paddingTop: '0px',
},
},
};
const getHeaderMock = {
offsetHeight: 50,
style: {
marginBottom: '10px',
marginTop: '5px',
},
};
const getFilterHeightMock = { offsetHeight: 30 };

jest.spyOn(document, 'querySelectorAll')
.mockReturnValueOnce([getHeaderMock])
.mockReturnValueOnce([getFilterHeightMock]);

instance.dashboardTileScroll(eventMock);

expect(eventMock.target.classList.add).toHaveBeenCalledWith('fixed-header');
expect(eventMock.target.classList.add).toHaveBeenCalledTimes(1);
expect(eventMock.target.style.paddingTop).toBe('65px');
});

it('should handle cases where elements are not found', () => {
process.env.REACT_APP_API_PORTAL = true;
const wrapper = shallow(
<Dashboard
tiles={null}
fetchTilesStart={jest.fn()}
fetchTilesStop={jest.fn()}
clearService={jest.fn()}
clear={jest.fn()}
assertAuthorization={jest.fn()}
authentication={jest.fn()}
/>
);
const instance = wrapper.instance();

const eventMock = {
target: {
scrollTop: 40,
classList: {
add: jest.fn(),
remove: jest.fn(),
},
style: {
paddingTop: '0px',
},
},
};

const getHeaderMock = {
style: {
marginBottom: '10px',
marginTop: '5px',
},
};
const getFilterHeightMock = { offsetHeight: 30 };

jest.spyOn(document, 'querySelectorAll')
.mockReturnValueOnce([getHeaderMock])
.mockReturnValueOnce([getFilterHeightMock]);

instance.dashboardTileScroll(eventMock);

expect(eventMock.target.classList.add).toHaveBeenCalledTimes(0);
expect(eventMock.target.classList.remove).toHaveBeenCalledWith('fixed-header');
expect(eventMock.target.classList.remove).toHaveBeenCalledTimes(1);
expect(eventMock.target.style.paddingTop).toBe(0);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
fetchTilesStop,
storeCurrentTileId,
} from '../../actions/catalog-tile-actions';
import { clearService } from '../../actions/selected-service-actions';
import { clearService, storeContentAnchor } from '../../actions/selected-service-actions';
import { filterText, clear } from '../../actions/filter-actions';
import { createLoadingSelector, getFilteredServices } from '../../selectors/selectors';
import { clearError, refreshedStaticApi } from '../../actions/refresh-static-apis-actions';
Expand Down Expand Up @@ -51,6 +51,7 @@ const mapDispatchToProps = {
selectEnabler,
closeAlert: () => userActions.closeAlert(),
storeCurrentTileId: (id) => storeCurrentTileId(id),
storeContentAnchor: (id) => storeContentAnchor(id),
};

export default connect(mapStateToProps, mapDispatchToProps)(Dashboard);
Loading

0 comments on commit eff57b1

Please sign in to comment.