From 1add8d50c0faa17f108fe04cb1e379b44e9d48ee Mon Sep 17 00:00:00 2001 From: Tomohisa Igarashi Date: Tue, 30 Apr 2024 11:59:55 -0400 Subject: [PATCH] fix: Catalog: Make "type" buttons to be toggle and allow to show multiple types at the same time 2nd shot - instead of collapsible grouped section, added a header tag of "Component", "Processor" or "Kamelet" to indicate the type, so it could be distinguished at a first glance Fixes: #1039 Fixes: #1040 --- .../cypress/e2e/componentCatalog.cy.ts | 14 ++++++++---- .../cypress/support/next-commands/design.ts | 3 +-- .../camel-utils/camel-to-tile.adapter.test.ts | 6 ++--- .../src/camel-utils/camel-to-tile.adapter.ts | 5 +++-- .../ui/src/components/Catalog/Catalog.tsx | 15 +++++++++---- .../src/components/Catalog/CatalogFilter.tsx | 22 +++++++++++++------ .../Catalog/Tags/tag-color-resolver.ts | 4 ++++ 7 files changed, 47 insertions(+), 22 deletions(-) diff --git a/packages/ui-tests/cypress/e2e/componentCatalog.cy.ts b/packages/ui-tests/cypress/e2e/componentCatalog.cy.ts index 668ae80c4..506b2380f 100644 --- a/packages/ui-tests/cypress/e2e/componentCatalog.cy.ts +++ b/packages/ui-tests/cypress/e2e/componentCatalog.cy.ts @@ -5,19 +5,22 @@ describe('Catalog related tests', () => { it('Catalog search', () => { cy.openCatalog(); - cy.get('[data-testid="component-catalog-tab"]').click(); + cy.get('[data-testid="processor-catalog-tab"]').click(); + cy.get('[data-testid="kamelet-catalog-tab"]').click(); cy.get('.pf-v5-c-text-input-group__text-input').click(); cy.get('.pf-v5-c-text-input-group__text-input').type('timer'); cy.get('div[id="timer"]').should('be.visible'); cy.get('button[aria-label="Reset"]').click(); cy.get('.pf-v5-c-text-input-group__text-input').should('have.value', ''); + cy.get('[data-testid="component-catalog-tab"]').click(); cy.get('[data-testid="processor-catalog-tab"]').click(); cy.get('.pf-v5-c-text-input-group__text-input').type('choice'); cy.get('div[id="choice"]').should('be.visible'); cy.get('button[aria-label="Reset"]').click(); cy.get('.pf-v5-c-text-input-group__text-input').should('have.value', ''); + cy.get('[data-testid="processor-catalog-tab"]').click(); cy.get('[data-testid="kamelet-catalog-tab"]').click(); cy.get('.pf-v5-c-text-input-group__text-input').type('google'); cy.get('div[id="google-storage-source"]').should('be.visible'); @@ -27,7 +30,8 @@ describe('Catalog related tests', () => { it('Catalog filtering using tags', () => { cy.openCatalog(); - cy.get('[data-testid="component-catalog-tab"]').click(); + cy.get('[data-testid="processor-catalog-tab"]').click(); + cy.get('[data-testid="kamelet-catalog-tab"]').click(); cy.get('[data-testid="tag-cloud"]').first().click(); cy.get('[data-testid="tag-database"]').first().click(); cy.get('[data-testid="tag-serverless"]').first().click(); @@ -43,13 +47,15 @@ describe('Catalog related tests', () => { it('Catalog list view switch check', () => { cy.openCatalog(); cy.get('#toggle-layout-button-List').click(); - cy.get('[data-testid="kamelet-catalog-tab"]').click(); + cy.get('[data-testid="component-catalog-tab"]').click(); + cy.get('[data-testid="processor-catalog-tab"]').click(); cy.get('#toggle-layout-button-Gallery').should('have.attr', 'aria-pressed', 'false'); cy.openSourceCode(); cy.openCatalog(); cy.get('#toggle-layout-button-Gallery').should('have.attr', 'aria-pressed', 'false'); cy.get('#toggle-layout-button-Gallery').click(); - cy.get('[data-testid="component-catalog-tab"]').click(); + cy.get('[data-testid="processor-catalog-tab"]').click(); + cy.get('[data-testid="kamelet-catalog-tab"]').click(); cy.get('#toggle-layout-button-Gallery').should('have.attr', 'aria-pressed', 'true'); }); }); diff --git a/packages/ui-tests/cypress/support/next-commands/design.ts b/packages/ui-tests/cypress/support/next-commands/design.ts index 5eabd28cc..d67c61acc 100644 --- a/packages/ui-tests/cypress/support/next-commands/design.ts +++ b/packages/ui-tests/cypress/support/next-commands/design.ts @@ -69,8 +69,7 @@ Cypress.Commands.add('selectPrependNode', (nodeName: string, nodeIndex?: number) cy.performNodeAction(nodeName, 'prepend', nodeIndex); }); -Cypress.Commands.add('chooseFromCatalog', (nodeType: string, name: string) => { - cy.get(`[data-testid="${nodeType}-catalog-tab"]`).click(); +Cypress.Commands.add('chooseFromCatalog', (_nodeType: string, name: string) => { cy.get('.pf-v5-c-text-input-group__text-input').click(); cy.get('.pf-v5-c-text-input-group__text-input').type(name); cy.get(`#${name}`).should('be.visible').click(); diff --git a/packages/ui/src/camel-utils/camel-to-tile.adapter.test.ts b/packages/ui/src/camel-utils/camel-to-tile.adapter.test.ts index b3ee8930d..a47a30654 100644 --- a/packages/ui/src/camel-utils/camel-to-tile.adapter.test.ts +++ b/packages/ui/src/camel-utils/camel-to-tile.adapter.test.ts @@ -27,7 +27,7 @@ describe('camelComponentToTile', () => { const tile = camelComponentToTile(componentDef); - expect(tile.headerTags).toEqual(['Preview']); + expect(tile.headerTags).toEqual(['Component', 'Preview']); }); it('should populate the tags and version', () => { @@ -67,7 +67,7 @@ describe('camelComponentToTile', () => { const tile = camelComponentToTile(componentDef); - expect(tile.headerTags).toEqual(['Deprecated']); + expect(tile.headerTags).toEqual(['Component', 'Deprecated']); }); }); @@ -210,6 +210,6 @@ describe('kameletToTile', () => { const tile = kameletToTile(kameletDef); - expect(tile.headerTags).toEqual(['Preview']); + expect(tile.headerTags).toEqual(['Kamelet', 'Preview']); }); }); diff --git a/packages/ui/src/camel-utils/camel-to-tile.adapter.ts b/packages/ui/src/camel-utils/camel-to-tile.adapter.ts index c99eb34e0..99d51077e 100644 --- a/packages/ui/src/camel-utils/camel-to-tile.adapter.ts +++ b/packages/ui/src/camel-utils/camel-to-tile.adapter.ts @@ -3,7 +3,7 @@ import { CatalogKind, ICamelComponentDefinition, ICamelProcessorDefinition, IKam export const camelComponentToTile = (componentDef: ICamelComponentDefinition): ITile => { const { name, title, description, supportLevel, label, version } = componentDef.component; - const headerTags: string[] = []; + const headerTags: string[] = ['Component']; const tags: string[] = []; if (supportLevel && !componentDef.component.deprecated) { @@ -42,13 +42,14 @@ export const camelProcessorToTile = (processorDef: ICamelProcessorDefinition): I name, title, description, + headerTags: ['Processor'], tags, rawObject: processorDef, }; }; export const kameletToTile = (kameletDef: IKameletDefinition): ITile => { - const headerTags: string[] = []; + const headerTags: string[] = ['Kamelet']; if (kameletDef.metadata.annotations['camel.apache.org/kamelet.support.level']) { headerTags.push(kameletDef.metadata.annotations['camel.apache.org/kamelet.support.level']); } diff --git a/packages/ui/src/components/Catalog/Catalog.tsx b/packages/ui/src/components/Catalog/Catalog.tsx index c271ab021..528b6b01a 100644 --- a/packages/ui/src/components/Catalog/Catalog.tsx +++ b/packages/ui/src/components/Catalog/Catalog.tsx @@ -31,9 +31,16 @@ export const Catalog: FunctionComponent> = (prop return Object.entries(filteredTilesByGroup).map(([group, tiles]) => ({ name: group, count: tiles.length })); }, [filteredTilesByGroup]); - const [activeGroup, setActiveGroup] = useState(tilesGroups[0].name); + const [activeGroups, setActiveGroups] = useState(tilesGroups.map((g) => g.name)); const [activeLayout, setActiveLayout] = useLocalStorage(LocalStorageKeys.CatalogLayout, CatalogLayout.Gallery); - const filteredTiles = useMemo(() => filteredTilesByGroup[activeGroup] ?? [], [activeGroup, filteredTilesByGroup]); + const filteredTiles = useMemo(() => { + return Object.entries(filteredTilesByGroup).reduce((acc, [group, tiles]) => { + if (activeGroups.includes(group)) { + acc.push(...tiles); + } + return acc; + }, [] as ITile[]); + }, [activeGroups, filteredTilesByGroup]); const onFilterChange = useCallback( (_event: unknown, value = '') => { @@ -62,11 +69,11 @@ export const Catalog: FunctionComponent> = (prop searchTerm={searchTerm} groups={tilesGroups} layouts={[CatalogLayout.Gallery, CatalogLayout.List]} - activeGroup={activeGroup} + activeGroups={activeGroups} activeLayout={activeLayout} filterTags={filterTags} onChange={onFilterChange} - setActiveGroup={setActiveGroup} + setActiveGroups={setActiveGroups} setActiveLayout={setActiveLayout} setFilterTags={setFilterTags} /> diff --git a/packages/ui/src/components/Catalog/CatalogFilter.tsx b/packages/ui/src/components/Catalog/CatalogFilter.tsx index 705473fc9..ef01a074f 100644 --- a/packages/ui/src/components/Catalog/CatalogFilter.tsx +++ b/packages/ui/src/components/Catalog/CatalogFilter.tsx @@ -20,11 +20,11 @@ interface CatalogFilterProps { searchTerm: string; groups: { name: string; count: number }[]; layouts: CatalogLayout[]; - activeGroup: string; + activeGroups: string[]; activeLayout: CatalogLayout; filterTags: string[]; onChange: (event: unknown, value?: string) => void; - setActiveGroup: (group: string) => void; + setActiveGroups: (groups: string[]) => void; setActiveLayout: (layout: CatalogLayout) => void; setFilterTags: (tags: string[]) => void; } @@ -40,6 +40,17 @@ export const CatalogFilter: FunctionComponent = (props) => { props.setFilterTags(props.filterTags.filter((savedTag) => savedTag !== tag)); }; + const toggleActiveGroup = (selected: boolean, group: string) => { + if (selected && !props.activeGroups.includes(group)) { + props.activeGroups.push(group); + props.setActiveGroups([...props.activeGroups]); + } else if (!selected && props.activeGroups.includes(group)) { + props.activeGroups.splice(props.activeGroups.indexOf(group), 1); + props.setActiveGroups([...props.activeGroups]); + } + inputRef.current?.focus(); + }; + return (
@@ -69,11 +80,8 @@ export const CatalogFilter: FunctionComponent = (props) => { key={tileGroup.name} data-testid={`${tileGroup.name}-catalog-tab`} buttonId={`toggle-group-button-${tileGroup.name}`} - isSelected={props.activeGroup === tileGroup.name} - onChange={() => { - props.setActiveGroup(tileGroup.name); - inputRef.current?.focus(); - }} + isSelected={props.activeGroups.includes(tileGroup.name)} + onChange={(_event, selected: boolean) => toggleActiveGroup(selected, tileGroup.name)} /> ))} diff --git a/packages/ui/src/components/Catalog/Tags/tag-color-resolver.ts b/packages/ui/src/components/Catalog/Tags/tag-color-resolver.ts index 18fac6379..d2192bc6d 100644 --- a/packages/ui/src/components/Catalog/Tags/tag-color-resolver.ts +++ b/packages/ui/src/components/Catalog/Tags/tag-color-resolver.ts @@ -8,6 +8,10 @@ export const getTagColor = (tag: string): COLOR => { return 'orange'; case 'deprecated': return 'red'; + case 'component': + case 'processor': + case 'kamelet': + return 'blue'; default: return 'grey'; }