Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

task/WP-273: Category icon #874

Merged
merged 13 commits into from
Oct 25, 2023
32 changes: 27 additions & 5 deletions client/src/components/_common/AppIcon/AppIcon.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,32 @@ import PropTypes from 'prop-types';
import Icon from '_common/Icon';
import './AppIcon.scss';

const doesClassExist = (className) => {
// Check if the CSS class exists in the stylesheets
for (let sheet of document.styleSheets) {
try {
for (let rule of sheet.cssRules) {
if (
rule.selectorText &&
rule.selectorText.includes(`.${className}::before`)
) {
return true;
}
}
} catch (e) {
// Handle cross-origin stylesheet errors
continue;
}
rstijerina marked this conversation as resolved.
Show resolved Hide resolved
}
return false;
};

const AppIcon = ({ appId, category }) => {
const appIcons = useSelector((state) => state.apps.appIcons);
const findAppIcon = (id) => {
if (!category) {
console.error('Category is undefined for appId:', id);
return 'applications';
}
let appIcon = category.replace(' ', '-').toLowerCase();
let appIcon = category
? category.replace(' ', '-').toLowerCase()
: 'applications';
Object.keys(appIcons).forEach((appName) => {
if (id.includes(appName)) {
appIcon = appIcons[appName].toLowerCase();
Expand All @@ -23,6 +41,10 @@ const AppIcon = ({ appId, category }) => {
} else if (id.includes('extract')) {
appIcon = 'extract';
}
// Check if the CSS class exists, if not default to 'icon-applications'
if (!doesClassExist(`icon-${appIcon}`)) {
appIcon = 'applications';
}
return appIcon;
};
const iconName = findAppIcon(appId);
Expand Down
52 changes: 35 additions & 17 deletions client/src/components/_common/AppIcon/AppIcon.test.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import React from 'react';
import { render } from '@testing-library/react';
import {
toHaveAttribute,
toHaveTextContent,
} from '@testing-library/jest-dom/dist/matchers';
import '@testing-library/jest-dom/extend-expect';
import { Provider } from 'react-redux';
import configureStore from 'redux-mock-store';
import AppIcon from './AppIcon';
Expand All @@ -16,46 +13,67 @@ const store = mockStore({
},
},
categories: {
'test-apps': ['vasp'],
visualization: ['vasp'],
'data-processing': ['jupyter'],
},
});

expect.extend({ toHaveAttribute });
// Mock document.styleSheets to simulate the existence of the CSS classes we're testing for
Object.defineProperty(document, 'styleSheets', {
value: [
{
cssRules: [
{ selectorText: '.icon-jupyter::before' },
{ selectorText: '.icon-visualization::before' },
{ selectorText: '.icon-compress::before' },
{ selectorText: '.icon-extract::before' },
],
},
],
writable: true,
});

function renderAppIcon(appId, category = 'default') {
return render(
<Provider store={store}>
<AppIcon appId={appId} category={category} />
<AppIcon
appId={appId}
category={category}
appIcons={store.getState().apps.appIcons}
/>
</Provider>
);
}

describe('AppIcon', () => {
it('should render icons for known app IDs', () => {
const { getByRole } = renderAppIcon('jupyter', 'data-processing');
expect(getByRole('img')).toHaveAttribute('class', 'icon icon-jupyter');
const { container } = renderAppIcon('jupyter', 'data-processing');
expect(container.firstChild).toHaveClass('icon-jupyter');
});

it('should show category icons for apps with no appIcon', () => {
const { getByRole } = renderAppIcon('vasp', 'test-apps');
expect(getByRole('img')).toHaveAttribute('class', 'icon icon-test-apps');
const { container } = renderAppIcon('vasp', 'visualization');
expect(container.firstChild).toHaveClass('icon-visualization');
});

it('should render icons for prtl.clone apps', () => {
const { getByRole } = renderAppIcon(
const { container } = renderAppIcon(
'prtl.clone.username.allocation.jupyter'
);
expect(getByRole('img')).toHaveAttribute('class', 'icon icon-jupyter');
expect(container.firstChild).toHaveClass('icon-jupyter');
});

it('should render icon for zippy toolbar app', () => {
const { getByRole } = renderAppIcon(
const { container } = renderAppIcon(
'prtl.clone.username.FORK.zippy-0.2u2-2.0'
);
expect(getByRole('img')).toHaveAttribute('class', 'icon icon-compress');
expect(container.firstChild).toHaveClass('icon-compress');
});

it('should render icon for extract toolbar app', () => {
const { getByRole } = renderAppIcon(
const { container } = renderAppIcon(
'prtl.clone.username.FORK.extract-0.1u7-7.0'
);
expect(getByRole('img')).toHaveAttribute('class', 'icon icon-extract');
expect(container.firstChild).toHaveClass('icon-extract');
});
});
4 changes: 0 additions & 4 deletions client/src/styles/trumps/icon.fonts.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.