Skip to content

Commit 88ff2e1

Browse files
Merge pull request #651 from mbaldessari/ui-tiers
Add support for custom tiers
2 parents b36f4fd + 0c15eeb commit 88ff2e1

5 files changed

Lines changed: 69 additions & 33 deletions

File tree

console/integration-tests/tests/pattern-catalog-page.cy.ts

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -60,32 +60,36 @@ describe('Pattern Catalog Page', () => {
6060
});
6161
});
6262

63-
it('tier filter dropdown shows all tier options', () => {
63+
it('tier filter dropdown shows tier options', () => {
6464
visitCatalog();
65-
// Default filter shows "Maintained"; click the toggle button
66-
cy.contains('button', 'Maintained').click();
67-
// Options are capitalized ("Tested", "Sandbox") and unique to the dropdown
68-
cy.contains('Tested').should('be.visible');
69-
cy.contains('Sandbox').should('be.visible');
70-
// Close dropdown by clicking the toggle again
71-
cy.contains('button', 'Maintained').click();
65+
// Open the tier filter dropdown
66+
cy.get('#tier-filter').closest('.pf-v6-c-select').find('button').first().click();
67+
// At least one selectable option should be visible
68+
cy.get('[role="option"]').should('have.length.greaterThan', 0);
69+
// Close dropdown
70+
cy.get('#tier-filter').closest('.pf-v6-c-select').find('button').first().click();
7271
});
7372

74-
it('selecting all tiers shows at least as many cards as maintained only', () => {
73+
it('selecting all tiers shows at least as many cards as the default selection', () => {
7574
visitCatalog();
7675
cy.get('.patterns-operator__card')
7776
.its('length')
78-
.then((maintainedCount) => {
79-
// Open filter and add Tested
80-
cy.contains('button', 'Maintained').click();
81-
cy.contains('Tested').click();
82-
// Dropdown may close after selection; re-open to add Sandbox
83-
cy.contains('button', /Maintained/).click();
84-
cy.contains('Sandbox').click();
77+
.then((defaultCount) => {
78+
// Open filter dropdown
79+
cy.get('#tier-filter').closest('.pf-v6-c-select').find('button').first().click();
80+
// Select every unchecked tier option
81+
cy.get('[role="option"]').each(($option) => {
82+
const checkbox = $option.find('input[type="checkbox"]');
83+
if (checkbox.length && !checkbox.is(':checked')) {
84+
cy.wrap($option).click();
85+
// Re-open dropdown if it closed
86+
cy.get('#tier-filter').closest('.pf-v6-c-select').find('button').first().click();
87+
}
88+
});
8589
// Close dropdown
8690
cy.get('body').click(0, 0);
87-
// With more tiers selected, card count should be >= maintained only
88-
cy.get('.patterns-operator__card').should('have.length.gte', maintainedCount);
91+
// With all tiers selected, card count should be >= default selection
92+
cy.get('.patterns-operator__card').should('have.length.gte', defaultCount);
8993
});
9094
});
9195

console/src/components/PatternCatalogPage.tsx

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -115,11 +115,18 @@ const TIER_SVG_COLORS: Record<string, { filled: string; outline: string }> = {
115115
sandbox: { filled: '#f0ab00', outline: '#f0ab00' },
116116
};
117117

118-
function TierIcon({ tier }: { tier: string }) {
119-
const colors = TIER_SVG_COLORS[tier] || TIER_SVG_COLORS.sandbox;
120-
// 3 horizontal bars: bottom=bar1, middle=bar2, top=bar3
121-
// maintained: all 3 filled; tested: 2 filled + 1 outline; sandbox: 1 filled + 2 outline
122-
const filledCount = tier === 'maintained' ? 3 : tier === 'tested' ? 2 : 1;
118+
const KNOWN_TIER_ORDER = ['maintained', 'tested', 'sandbox'];
119+
120+
const TIER_FILLED_BARS: Record<string, number> = {
121+
maintained: 3,
122+
tested: 2,
123+
sandbox: 1,
124+
};
125+
126+
function TierIcon({ tier }: { tier: string }): React.ReactElement | null {
127+
const colors = TIER_SVG_COLORS[tier];
128+
if (!colors) return null;
129+
const filledCount = TIER_FILLED_BARS[tier] ?? 1;
123130
return (
124131
<svg
125132
width="16"
@@ -167,7 +174,7 @@ export default function PatternCatalogPage() {
167174
const [catalogImage, setCatalogImage] = React.useState<string | null>(null);
168175
const [catalogDescription, setCatalogDescription] = React.useState<string | undefined>();
169176
const [catalogLogo, setCatalogLogo] = React.useState<string | undefined>();
170-
const [selectedTiers, setSelectedTiers] = React.useState<Set<string>>(new Set(['maintained']));
177+
const [selectedTiers, setSelectedTiers] = React.useState<Set<string>>(new Set());
171178
const [tierSelectOpen, setTierSelectOpen] = React.useState(false);
172179

173180
const loadData = React.useCallback(() => {
@@ -191,6 +198,29 @@ export default function PatternCatalogPage() {
191198
loadData();
192199
}, [loadData]);
193200

201+
const availableTiers = React.useMemo(() => {
202+
const tierSet = new Set(patterns.map((p) => p.tier));
203+
return Array.from(tierSet).sort((a, b) => {
204+
const ai = KNOWN_TIER_ORDER.indexOf(a);
205+
const bi = KNOWN_TIER_ORDER.indexOf(b);
206+
if (ai !== -1 && bi !== -1) return ai - bi;
207+
if (ai !== -1) return -1;
208+
if (bi !== -1) return 1;
209+
return a.localeCompare(b, undefined, { sensitivity: 'base' });
210+
});
211+
}, [patterns]);
212+
213+
const defaultsApplied = React.useRef(false);
214+
React.useEffect(() => {
215+
if (defaultsApplied.current || availableTiers.length === 0) return;
216+
defaultsApplied.current = true;
217+
if (availableTiers.includes('maintained')) {
218+
setSelectedTiers(new Set(['maintained']));
219+
} else {
220+
setSelectedTiers(new Set(availableTiers));
221+
}
222+
}, [availableTiers]);
223+
194224
const filteredPatterns = React.useMemo(
195225
() => (selectedTiers.size === 0 ? patterns : patterns.filter((p) => selectedTiers.has(p.tier))),
196226
[patterns, selectedTiers],
@@ -284,7 +314,7 @@ export default function PatternCatalogPage() {
284314
)}
285315
>
286316
<SelectList>
287-
{['maintained', 'tested', 'sandbox'].map((tier) => (
317+
{availableTiers.map((tier) => (
288318
<SelectOption
289319
key={tier}
290320
value={tier}
@@ -315,7 +345,11 @@ export default function PatternCatalogPage() {
315345
<Tooltip content={TIER_DESCRIPTIONS[pattern.tier] || pattern.tier}>
316346
<Label
317347
color={TIER_COLORS[pattern.tier] || 'grey'}
318-
icon={<TierIcon tier={pattern.tier} />}
348+
icon={
349+
TIER_SVG_COLORS[pattern.tier] ? (
350+
<TierIcon tier={pattern.tier} />
351+
) : undefined
352+
}
319353
>
320354
{pattern.tier}
321355
</Label>

console/src/components/SecretForm/FileField.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,9 @@ export const FileField: React.FC<FileFieldProps> = ({ field, value, onChange, fi
6868
</HelperTextItem>
6969
)}
7070
{field.description && (
71-
<HelperTextItem id={`file-${field.name}-helpertext`}>{field.description}</HelperTextItem>
71+
<HelperTextItem id={`file-${field.name}-helpertext`}>
72+
{field.description}
73+
</HelperTextItem>
7274
)}
7375
</HelperText>
7476
</FileUploadHelperText>

console/src/components/SecretForm/SecretFormExpandableSections.tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,7 @@ export function SecretFormExpandableSections({
3232
}: SecretFormExpandableSectionsProps) {
3333
const { t } = useTranslation('plugin__patterns-operator-console-plugin');
3434

35-
const renderField = (
36-
secret: SecretDefinition,
37-
field: SecretField,
38-
uploadFieldError?: string,
39-
) => {
35+
const renderField = (secret: SecretDefinition, field: SecretField, uploadFieldError?: string) => {
4036
const fieldType = getSecretFieldKind(field);
4137
const value = secretFormData[secret.name]?.[field.name] || '';
4238

console/src/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export interface Pattern {
3838
issues_url: string;
3939
docs_url: string;
4040
ci_url: string;
41-
tier: 'maintained' | 'tested' | 'sandbox';
41+
tier: string;
4242
owners: string[];
4343
org: string;
4444
clustergroupname: string;

0 commit comments

Comments
 (0)