diff --git a/frontend/integration-tests/tests/operator-hub/operator-hub.scenario.ts b/frontend/integration-tests/tests/operator-hub/operator-hub.scenario.ts index 01e5e94f228..cc0aea5d81f 100644 --- a/frontend/integration-tests/tests/operator-hub/operator-hub.scenario.ts +++ b/frontend/integration-tests/tests/operator-hub/operator-hub.scenario.ts @@ -40,7 +40,7 @@ describe('Subscribing to an Operator from OperatorHub', () => { }); it('filters Operators by name', async() => { - await catalogPageView.filterSectionFor('provider').$$('.filter-panel-pf-category-item').get(1).$('input').click(); + await $('.co-catalog-page__filter input').click(); const filteredOperator = await catalogPageView.catalogTiles.first().$('.catalog-tile-pf-title').getText(); await catalogPageView.filterByKeyword(filteredOperator); diff --git a/frontend/integration-tests/views/catalog-page.view.ts b/frontend/integration-tests/views/catalog-page.view.ts index 163d539d7c9..707a90c0c2d 100644 --- a/frontend/integration-tests/views/catalog-page.view.ts +++ b/frontend/integration-tests/views/catalog-page.view.ts @@ -11,6 +11,6 @@ export const filterCheckboxFor = (id: string) => $(`input[data-test=${id}]`); export const clickFilterCheckbox = (id: string) => filterCheckboxFor(id).click(); export const filterCheckboxCount = (id: string) => filterCheckboxFor(id).$('.item-count').getText() .then(text => parseInt(text.substring(1, text.indexOf(')')), 10)); -export const filterTextbox = $$('.filter-panel-pf-category').first().$('input'); +export const filterTextbox = $('.co-catalog-page__filter input'); export const filterByKeyword = (filter: string) => filterTextbox.clear().then(() => filterTextbox.sendKeys(filter)); export const clearFiltersText = $('.co-catalog-page__no-filter-results').$('.blank-slate-pf-helpLink').$('button'); diff --git a/frontend/public/components/catalog/_catalog.scss b/frontend/public/components/catalog/_catalog.scss index 76e5517dca0..c49e34e9937 100644 --- a/frontend/public/components/catalog/_catalog.scss +++ b/frontend/public/components/catalog/_catalog.scss @@ -110,20 +110,33 @@ $co-modal-ignore-warning-icon-width: 30px; } } + &__filter { + display: flex; + justify-content: space-between; + } + &__filter-toggle { margin-top: 5px; } + &__header { + border-bottom: 1px solid $color-pf-black-200; + margin: 0 ($grid-gutter-width / 2) $grid-gutter-width 0; + } + &__heading { font-size: 16px; font-weight: 400; margin: 0 0 20px; } + &__input { + margin: 0 0 20px; + width: auto; + } + &__num-items { - border-bottom: 1px solid $color-pf-black-200; font-weight: 700; - margin: 0 ($grid-gutter-width / 2) $grid-gutter-width 0; padding: 0 0 20px; } diff --git a/frontend/public/components/operator-hub/operator-hub-items.tsx b/frontend/public/components/operator-hub/operator-hub-items.tsx index 993aa97a0ae..f9573736a54 100644 --- a/frontend/public/components/operator-hub/operator-hub-items.tsx +++ b/frontend/public/components/operator-hub/operator-hub-items.tsx @@ -23,11 +23,13 @@ const operatorHubFilterGroups = [ 'providerType', 'provider', 'installState', + 'capabilityLevel', ]; const operatorHubFilterMap = { providerType: 'Provider Type', installState: 'Install State', + capabilityLevel: 'Capability Level', }; const COMMUNITY_PROVIDER_TYPE: string = 'Community'; @@ -77,32 +79,66 @@ const providerSort = provider => { return provider.value; }; +enum ProviderType { + RedHat = 'Red Hat', + Certified = 'Certified', + Community = 'Community', + Custom = 'Custom', +} + +enum InstalledState { + Installed = 'Installed', + NotInstalled = 'Not Installed', +} + +enum CapabilityLevel { + BasicInstall = 'Basic Install', + SeamlessUpgrades = 'Seamless Upgrades', + FullLifecycle = 'Full Lifecycle', + DeepInsights = 'Deep Insights', +} + const providerTypeSort = provider => { switch (provider.value) { - case 'Red Hat': + case ProviderType.RedHat: return 0; - case 'Certified': + case ProviderType.Certified: return 1; - case 'Community': + case ProviderType.Community: return 2; - case 'Custom': + case ProviderType.Custom: return 4; default: return 5; } }; -const installedStateSort = provider =>{ +const installedStateSort = provider => { switch (provider.value) { - case 'Installed': + case InstalledState.Installed: return 0; - case 'Not Installed': + case InstalledState.NotInstalled: return 1; default: return 3; } }; +const capabilityLevelSort = provider => { + switch (provider.value) { + case CapabilityLevel.BasicInstall: + return 0; + case CapabilityLevel.SeamlessUpgrades: + return 1; + case CapabilityLevel.FullLifecycle: + return 2; + case CapabilityLevel.DeepInsights: + return 3; + default: + return 5; + } +}; + const sortFilterValues = (values, field) => { let sorter: any = ['value']; @@ -118,6 +154,10 @@ const sortFilterValues = (values, field) => { sorter = installedStateSort; } + if (field === 'capabilityLevel') { + sorter = capabilityLevelSort; + } + return _.sortBy(values, sorter); }; diff --git a/frontend/public/components/operator-hub/operator-hub-page.tsx b/frontend/public/components/operator-hub/operator-hub-page.tsx index ee245b991a6..d2e1ac04d04 100644 --- a/frontend/public/components/operator-hub/operator-hub-page.tsx +++ b/frontend/public/components/operator-hub/operator-hub-page.tsx @@ -52,6 +52,7 @@ export const OperatorHubList: React.SFC = (props) => { 'createdAt', 'support', ]), + capabilityLevel: currentCSVDesc.annotations.capabilities, } as OperatorHubItem; }); diff --git a/frontend/public/components/utils/tile-view-page.jsx b/frontend/public/components/utils/tile-view-page.jsx index 2ca65ee162a..ba6c100d0a3 100644 --- a/frontend/public/components/utils/tile-view-page.jsx +++ b/frontend/public/components/utils/tile-view-page.jsx @@ -627,16 +627,6 @@ export class TileViewPage extends React.Component { return ( - e.preventDefault()}> - this.filterByKeywordInput = ref} - placeholder="Filter by keyword..." - bsClass="form-control" - value={activeFilters.keyword.value} - onChange={e => this.onKeywordChange(e.target.value)} - /> - {_.map(activeFilters, (filterGroup, groupName) => { if (groupName === 'keyword') { return; @@ -666,7 +656,7 @@ export class TileViewPage extends React.Component { render() { const { renderTile } = this.props; - const { selectedCategoryId, categories } = this.state; + const { activeFilters, selectedCategoryId, categories } = this.state; let activeCategory = findActiveCategory(selectedCategoryId, categories); if (!activeCategory) { activeCategory = findActiveCategory('all', categories); @@ -679,9 +669,20 @@ export class TileViewPage extends React.Component { { this.renderSidePanel() }
-
+
{activeCategory.label}
-
{activeCategory.numItems} items
+
+ this.filterByKeywordInput = ref} + placeholder="Filter by keyword..." + bsClass="form-control" + value={activeFilters.keyword.value} + onChange={e => this.onKeywordChange(e.target.value)} + /> +
{activeCategory.numItems} items
+
{activeCategory.numItems > 0 && (