Skip to content

Commit 21723e5

Browse files
authored
Fix: Add filter support in learning resources component (#25946)
1 parent c150ce0 commit 21723e5

File tree

3 files changed

+214
-4
lines changed

3 files changed

+214
-4
lines changed

openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/LearningResources.spec.ts

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,216 @@ test.describe(
357357
}
358358
);
359359

360+
async function applyLearningResourceFilter(
361+
page: Page,
362+
filterLabel: string,
363+
optionKey: string
364+
) {
365+
await page.getByTestId(`search-dropdown-${filterLabel}`).click();
366+
await expect(page.getByTestId('drop-down-menu')).toBeVisible();
367+
const option = page.getByTestId(optionKey);
368+
await expect(option).toBeVisible();
369+
await option.click();
370+
371+
const filterResponse = page.waitForResponse(
372+
(r) =>
373+
r.url().includes('/api/v1/learning/resources') &&
374+
r.request().method() === 'GET'
375+
);
376+
const updateBtn = page.getByTestId('update-btn');
377+
await expect(updateBtn).toBeVisible();
378+
await expect(updateBtn).toBeEnabled();
379+
await updateBtn.click();
380+
381+
const response = await filterResponse;
382+
await waitForAllLoadersToDisappear(page);
383+
384+
return response;
385+
}
386+
387+
test.describe('Learning Resources - Search and Filters', () => {
388+
const videoResource = new LearningResourceClass({
389+
resourceType: 'Video',
390+
categories: ['Discovery'],
391+
contexts: [{ pageId: 'glossary' }],
392+
status: 'Active',
393+
});
394+
const storylaneResource = new LearningResourceClass({
395+
resourceType: 'Storylane',
396+
categories: ['DataGovernance'],
397+
contexts: [{ pageId: 'lineage' }],
398+
status: 'Draft',
399+
});
400+
401+
test.beforeAll(async ({ browser }) => {
402+
const { apiContext, afterAction } = await createNewPage(browser);
403+
await videoResource.create(apiContext);
404+
await storylaneResource.create(apiContext);
405+
await afterAction();
406+
});
407+
408+
test.afterAll(async ({ browser }) => {
409+
const { apiContext, afterAction } = await createNewPage(browser);
410+
await videoResource.delete(apiContext);
411+
await storylaneResource.delete(apiContext);
412+
await afterAction();
413+
});
414+
415+
test.beforeEach(async ({ page }) => {
416+
await goToLearningResourcesAdmin(page);
417+
});
418+
419+
test('should send correct search param to API when searching', async ({
420+
page,
421+
}) => {
422+
const searchTerm = videoResource.data.name;
423+
424+
await test.step(
425+
'Type search term and verify API receives search param',
426+
async () => {
427+
const searchResponse = page.waitForResponse(
428+
(r) =>
429+
r.url().includes('/api/v1/learning/resources') &&
430+
r.url().includes('search=') &&
431+
r.request().method() === 'GET'
432+
);
433+
434+
await page
435+
.getByRole('textbox', { name: 'Search Resource' })
436+
.fill(searchTerm);
437+
const response = await searchResponse;
438+
await waitForAllLoadersToDisappear(page);
439+
440+
expect(response.url()).toContain(
441+
`search=${encodeURIComponent(searchTerm)}`
442+
);
443+
}
444+
);
445+
446+
await test.step('Verify search result is shown in table', async () => {
447+
await expect(
448+
page.getByText(
449+
videoResource.data.displayName ?? videoResource.data.name
450+
)
451+
).toBeVisible();
452+
});
453+
});
454+
455+
test('should send correct resourceType param when filtering by type', async ({
456+
page,
457+
}) => {
458+
await test.step(
459+
'Apply Video type filter and verify API param',
460+
async () => {
461+
const response = await applyLearningResourceFilter(
462+
page,
463+
'Type',
464+
'Video'
465+
);
466+
expect(response.url()).toContain('resourceType=Video');
467+
}
468+
);
469+
470+
await test.step('Verify filter chip is shown', async () => {
471+
await expect(
472+
page.locator('.filter-selection-chip').filter({ hasText: 'Video' })
473+
).toBeVisible();
474+
});
475+
});
476+
477+
test('should send correct category param when filtering by category', async ({
478+
page,
479+
}) => {
480+
await test.step(
481+
'Apply Discovery category filter and verify API param',
482+
async () => {
483+
const response = await applyLearningResourceFilter(
484+
page,
485+
'Categories',
486+
'Discovery'
487+
);
488+
expect(response.url()).toContain('category=Discovery');
489+
}
490+
);
491+
492+
await test.step('Verify filter chip is shown', async () => {
493+
await expect(page.getByTitle('Discovery')).toBeVisible();
494+
});
495+
});
496+
497+
test('should send correct pageId param when filtering by context', async ({
498+
page,
499+
}) => {
500+
await test.step(
501+
'Apply Glossary context filter and verify API param',
502+
async () => {
503+
const response = await applyLearningResourceFilter(
504+
page,
505+
'Context',
506+
'glossary'
507+
);
508+
expect(response.url()).toContain('pageId=glossary');
509+
}
510+
);
511+
512+
await test.step('Verify filter chip is shown', async () => {
513+
await expect(page.getByTitle('Glossary')).toBeVisible();
514+
});
515+
});
516+
517+
test('should send correct status param when filtering by status', async ({
518+
page,
519+
}) => {
520+
await test.step(
521+
'Apply Active status filter and verify API param',
522+
async () => {
523+
const response = await applyLearningResourceFilter(
524+
page,
525+
'Status',
526+
'Active'
527+
);
528+
expect(response.url()).toContain('status=Active');
529+
}
530+
);
531+
532+
await test.step('Verify filter chip is shown', async () => {
533+
await expect(page.getByTitle('Active')).toBeVisible();
534+
});
535+
});
536+
537+
test('should clear all filters and reload without filter params', async ({
538+
page,
539+
}) => {
540+
await test.step('Apply a filter first', async () => {
541+
await applyLearningResourceFilter(page, 'Type', 'Video');
542+
await expect(page.getByTitle('Video')).toBeVisible();
543+
});
544+
545+
await test.step('Clear all filters and verify clean API call', async () => {
546+
const clearResponse = page.waitForResponse(
547+
(r) =>
548+
r.url().includes('/api/v1/learning/resources') &&
549+
r.request().method() === 'GET'
550+
);
551+
await page.getByRole('button', { name: /clear all/i }).click();
552+
const response = await clearResponse;
553+
await waitForAllLoadersToDisappear(page);
554+
555+
expect(response.url()).not.toContain('resourceType=');
556+
expect(response.url()).not.toContain('category=');
557+
expect(response.url()).not.toContain('pageId=');
558+
expect(response.url()).not.toContain('status=');
559+
expect(response.url()).not.toContain('search=');
560+
});
561+
562+
await test.step('Verify filter chips are gone', async () => {
563+
await expect(
564+
page.locator('.filter-selection-container')
565+
).not.toBeVisible();
566+
});
567+
});
568+
});
569+
360570
test.describe(
361571
'Learning Resources E2E Flow',
362572
{ tag: ['@Flow', '@Platform'] },

openmetadata-ui/src/main/resources/ui/src/pages/LearningResourcesPage/hooks/useLearningResources.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,10 @@ function buildListParams(
8585
return {
8686
limit: pageSize,
8787
fields: FIELDS,
88-
q: searchText || undefined,
88+
search: searchText || undefined,
8989
category: filterState.category?.length ? filterState.category : undefined,
9090
pageId: filterState.context?.length ? filterState.context : undefined,
91-
type: filterState.type?.length ? filterState.type : undefined,
91+
resourceType: filterState.type?.length ? filterState.type : undefined,
9292
status: filterState.status?.length ? filterState.status : undefined,
9393
...(hasCursor ? cursor : undefined),
9494
};

openmetadata-ui/src/main/resources/ui/src/rest/learningResourceAPI.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,11 +87,11 @@ export interface CreateLearningResource {
8787
}
8888

8989
export type ListLearningResourcesParams = ListParams & {
90-
q?: string;
90+
search?: string;
9191
pageId?: string | string[];
9292
componentId?: string | string[];
9393
category?: string | string[];
94-
type?: string | string[];
94+
resourceType?: string | string[];
9595
difficulty?: string;
9696
status?: string | string[];
9797
};

0 commit comments

Comments
 (0)