mirror of
https://github.com/inventree/InvenTree.git
synced 2026-07-06 07:00:39 +00:00
[UI] Table column filters (#12103)
* Expose properties for column based filtering * Adjust renderers * Hide filter if name does not match * Allow multiple filters on same column * Better formatting * Add filtering support for multiple tables * Revert yarn.lock changes * Fix date input props * Updated column * Add filter to PartTable * Add playwright tests for new column filters * Update CHANGELOG * Updated docs * Reduce padding * Update more table filters * More filter columns * Adjust playwright test * Simplify playwright test * Robustify playwright tests * Add some delay * Add some buffer time
This commit is contained in:
@@ -210,3 +210,23 @@ export const expectTableColumnCount = async (page: Page, count: number) => {
|
||||
const columns = page.locator('table thead tr th');
|
||||
await expect(columns).toHaveCount(count);
|
||||
};
|
||||
|
||||
// Open an "action" associated with the detail view of a page
|
||||
export const openDetailAction = async (
|
||||
page: Page,
|
||||
menuName: string,
|
||||
actionName: string
|
||||
) => {
|
||||
// Ensure idle state first
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
// Click on the "actions" menu
|
||||
await page.getByRole('button', { name: `action-menu-${menuName}` }).click();
|
||||
|
||||
// Click on the specified action
|
||||
await page
|
||||
.getByRole('menuitem', {
|
||||
name: `action-menu-${menuName}-actions-${actionName}`
|
||||
})
|
||||
.click();
|
||||
};
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
getRowFromCell,
|
||||
loadTab,
|
||||
navigate,
|
||||
openDetailAction,
|
||||
setTableChoiceFilter,
|
||||
showCalendarView,
|
||||
showParametricView,
|
||||
@@ -40,7 +41,6 @@ test('Build Order - Basic Tests', async ({ browser }) => {
|
||||
// We have now loaded the "Build Order" table. Check for some expected texts
|
||||
await page.getByPlaceholder('Search').fill('7');
|
||||
await page.getByText('On Hold').first().waitFor();
|
||||
await page.getByText('Pending').first().waitFor();
|
||||
|
||||
// Load a particular build order
|
||||
await page.getByRole('cell', { name: 'BO0017' }).click();
|
||||
@@ -756,8 +756,8 @@ test('Build Order - Duplicate', async ({ browser }) => {
|
||||
const page = await doCachedLogin(browser);
|
||||
|
||||
await navigate(page, 'manufacturing/build-order/24/details');
|
||||
await page.getByLabel('action-menu-build-order-').click();
|
||||
await page.getByLabel('action-menu-build-order-actions-duplicate').click();
|
||||
|
||||
await openDetailAction(page, 'build-order', 'duplicate');
|
||||
|
||||
// Ensure a new reference is suggested
|
||||
await expect(
|
||||
|
||||
@@ -5,12 +5,13 @@ import {
|
||||
clickOnParamFilter,
|
||||
loadTab,
|
||||
navigate,
|
||||
openDetailAction,
|
||||
setTableChoiceFilter,
|
||||
showParametricView
|
||||
} from '../helpers.js';
|
||||
import { doCachedLogin } from '../login.js';
|
||||
|
||||
test('Company', async ({ browser }) => {
|
||||
test('Company - Basic Tests', async ({ browser }) => {
|
||||
const page = await doCachedLogin(browser);
|
||||
|
||||
await navigate(page, 'company/1/details');
|
||||
@@ -42,8 +43,7 @@ test('Company', async ({ browser }) => {
|
||||
await loadTab(page, 'Notes');
|
||||
|
||||
// Let's edit the company details
|
||||
await page.getByLabel('action-menu-company-actions').click();
|
||||
await page.getByLabel('action-menu-company-actions-edit').click();
|
||||
await openDetailAction(page, 'company', 'edit');
|
||||
|
||||
await page.getByLabel('text-field-name', { exact: true }).fill('');
|
||||
await page
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
getRowFromCell,
|
||||
loadTab,
|
||||
navigate,
|
||||
openDetailAction,
|
||||
setTableChoiceFilter,
|
||||
showParametricView,
|
||||
showTableView
|
||||
@@ -69,6 +70,9 @@ test('Parts - Tabs', async ({ browser }) => {
|
||||
test('Parts - Image Selection', async ({ browser }) => {
|
||||
const page = await doCachedLogin(browser, { url: 'part/911/details' });
|
||||
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForTimeout(250);
|
||||
|
||||
// Select a new image from the available images
|
||||
await page
|
||||
.getByRole('tabpanel', { name: 'Part Details' })
|
||||
@@ -1015,8 +1019,8 @@ test('Parts - Bulk Edit', async ({ browser }) => {
|
||||
// Edit the category for multiple parts
|
||||
await page.getByLabel('Select record 1', { exact: true }).click();
|
||||
await page.getByLabel('Select record 2', { exact: true }).click();
|
||||
await page.getByLabel('action-menu-part-actions').click();
|
||||
await page.getByLabel('action-menu-part-actions-set-category').click();
|
||||
|
||||
await openDetailAction(page, 'part', 'set-category');
|
||||
|
||||
await page.getByLabel('related-field-category').fill('rnitu');
|
||||
await page.waitForTimeout(250);
|
||||
@@ -1032,9 +1036,7 @@ test('Parts - Duplicate', async ({ browser }) => {
|
||||
});
|
||||
|
||||
// Open "duplicate part" dialog
|
||||
await page.getByLabel('action-menu-part-actions').click();
|
||||
|
||||
await page.getByLabel('action-menu-part-actions-duplicate').click();
|
||||
await openDetailAction(page, 'part', 'duplicate');
|
||||
|
||||
// Check for expected fields
|
||||
await page.getByText('Copy Image', { exact: true }).waitFor();
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
clickOnRowMenu,
|
||||
loadTab,
|
||||
navigate,
|
||||
openDetailAction,
|
||||
openFilterDrawer,
|
||||
setTableChoiceFilter,
|
||||
showCalendarView,
|
||||
@@ -292,8 +293,7 @@ test('Purchase Orders - Barcodes', async ({ browser }) => {
|
||||
await page.getByRole('button', { name: 'Issue Order' }).waitFor();
|
||||
|
||||
// Display QR code
|
||||
await page.getByLabel('action-menu-barcode-actions').click();
|
||||
await page.getByLabel('action-menu-barcode-actions-view').click();
|
||||
await openDetailAction(page, 'barcode', 'view');
|
||||
await page.getByRole('img', { name: 'QR Code' }).waitFor();
|
||||
await page.getByRole('banner').getByRole('button').click();
|
||||
|
||||
@@ -314,8 +314,7 @@ test('Purchase Orders - Barcodes', async ({ browser }) => {
|
||||
}
|
||||
|
||||
// Link to barcode
|
||||
await page.getByLabel('action-menu-barcode-actions', { exact: true }).click();
|
||||
await page.getByLabel('action-menu-barcode-actions-link-barcode').click();
|
||||
await openDetailAction(page, 'barcode', 'link-barcode');
|
||||
|
||||
await page.getByLabel('barcode-input-scanner').click();
|
||||
|
||||
@@ -338,8 +337,7 @@ test('Purchase Orders - Barcodes', async ({ browser }) => {
|
||||
await page.getByText('Purchase Order: PO0013', { exact: true }).waitFor();
|
||||
|
||||
// Unlink barcode
|
||||
await page.getByLabel('action-menu-barcode-actions').click();
|
||||
await page.getByLabel('action-menu-barcode-actions-unlink-barcode').click();
|
||||
await openDetailAction(page, 'barcode', 'unlink-barcode');
|
||||
await page.getByRole('heading', { name: 'Unlink Barcode' }).waitFor();
|
||||
await page.getByText('This will remove the link to').waitFor();
|
||||
await page.getByRole('button', { name: 'Unlink Barcode' }).click();
|
||||
@@ -431,8 +429,8 @@ test('Purchase Orders - Order Parts', async ({ browser }) => {
|
||||
await page.getByLabel(`Select record ${ii}`, { exact: true }).click();
|
||||
}
|
||||
|
||||
await page.getByLabel('action-menu-part-actions').click();
|
||||
await page.getByLabel('action-menu-part-actions-order-parts').click();
|
||||
await openDetailAction(page, 'part', 'order-parts');
|
||||
|
||||
await page
|
||||
.getByRole('heading', { name: 'Order Parts' })
|
||||
.locator('div')
|
||||
@@ -456,8 +454,7 @@ test('Purchase Orders - Order Parts', async ({ browser }) => {
|
||||
await navigate(page, 'part/69/');
|
||||
await page.waitForURL('**/part/69/**');
|
||||
|
||||
await page.getByLabel('action-menu-stock-actions').click();
|
||||
await page.getByLabel('action-menu-stock-actions-order').click();
|
||||
await openDetailAction(page, 'stock', 'order');
|
||||
|
||||
// Select supplier part
|
||||
await page.getByLabel('related-field-supplier_part').click();
|
||||
@@ -630,8 +627,7 @@ test('Purchase Orders - Duplicate', async ({ browser }) => {
|
||||
url: 'purchasing/purchase-order/13/detail'
|
||||
});
|
||||
|
||||
await page.getByLabel('action-menu-order-actions').click();
|
||||
await page.getByLabel('action-menu-order-actions-duplicate').click();
|
||||
await openDetailAction(page, 'order', 'duplicate');
|
||||
|
||||
// Ensure a new reference is suggested
|
||||
await expect(
|
||||
|
||||
@@ -625,9 +625,9 @@ test('Transfer Order - Reference', async ({ browser }) => {
|
||||
.click();
|
||||
|
||||
// Ensure a new reference is suggested
|
||||
await expect(
|
||||
page.getByLabel('text-field-reference', { exact: true })
|
||||
).not.toBeEmpty();
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForTimeout(250);
|
||||
|
||||
// Grab the Transfer Order reference
|
||||
const reference: string = await page
|
||||
.getByRole('textbox', { name: 'text-field-reference' })
|
||||
|
||||
@@ -2,7 +2,7 @@ import { createApi } from './api';
|
||||
/** Unit tests for form validation, rendering, etc */
|
||||
import { expect, test } from './baseFixtures';
|
||||
import { stevenuser } from './defaults';
|
||||
import { navigate } from './helpers';
|
||||
import { navigate, openDetailAction } from './helpers';
|
||||
import { doCachedLogin } from './login';
|
||||
|
||||
// Test hover form action in related fields
|
||||
@@ -84,8 +84,7 @@ test('Forms - Stock Item Validation', async ({ browser }) => {
|
||||
await page.getByRole('button', { name: 'Submit' }).click();
|
||||
|
||||
// Edit the resulting stock item
|
||||
await page.getByLabel('action-menu-stock-item-actions').click();
|
||||
await page.getByLabel('action-menu-stock-item-actions-edit').click();
|
||||
await openDetailAction(page, 'stock-item', 'edit');
|
||||
|
||||
await page.getByLabel('number-field-purchase_price').fill('-1');
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { expect, test } from './baseFixtures.js';
|
||||
import { logoutUrl, noaccessuser } from './defaults.js';
|
||||
import { navigate } from './helpers.js';
|
||||
import { navigate, openDetailAction } from './helpers.js';
|
||||
import { doLogin } from './login.js';
|
||||
|
||||
import { TOTP } from 'otpauth';
|
||||
@@ -54,8 +54,8 @@ test('Login - Change Password', async ({ page }) => {
|
||||
|
||||
// Navigate to the 'change password' page
|
||||
await navigate(page, 'settings/user/account', { waitUntil: 'networkidle' });
|
||||
await page.getByLabel('action-menu-account-actions').click();
|
||||
await page.getByLabel('action-menu-account-actions-change-password').click();
|
||||
|
||||
await openDetailAction(page, 'account', 'change-password');
|
||||
|
||||
// First attempt with some errors
|
||||
await page.getByLabel('password', { exact: true }).fill('youshallnotpass');
|
||||
|
||||
@@ -9,6 +9,41 @@ import {
|
||||
} from './helpers.js';
|
||||
import { doCachedLogin } from './login.js';
|
||||
|
||||
// Test filtering by "quick filter" actions (against table columns)
|
||||
test('Tables - Quick Filters', async ({ browser }) => {
|
||||
const page = await doCachedLogin(browser, {
|
||||
url: 'part/category/index/parts/'
|
||||
});
|
||||
|
||||
await clearTableFilters(page);
|
||||
|
||||
await page
|
||||
.getByRole('button', { name: 'Part Not sorted' })
|
||||
.getByRole('button')
|
||||
.first()
|
||||
.click();
|
||||
await page.getByRole('combobox', { name: 'choice-filter-active' }).click();
|
||||
await page.getByRole('option', { name: 'Yes' }).click();
|
||||
|
||||
await page
|
||||
.getByRole('button', { name: 'Part Not sorted' })
|
||||
.getByRole('button')
|
||||
.first()
|
||||
.click();
|
||||
await page.getByRole('combobox', { name: 'choice-filter-locked' }).click();
|
||||
await page.getByRole('option', { name: 'No' }).click();
|
||||
|
||||
await page
|
||||
.getByRole('button', { name: 'IPN Not sorted' })
|
||||
.getByRole('button')
|
||||
.first()
|
||||
.click();
|
||||
await page.getByRole('combobox', { name: 'choice-filter-has_ipn' }).click();
|
||||
await page.getByRole('option', { name: 'Yes' }).click();
|
||||
|
||||
await page.getByRole('cell', { name: 'ENCAB' }).first().waitFor();
|
||||
});
|
||||
|
||||
test('Tables - Filters', async ({ browser }) => {
|
||||
// Head to the "build order list" page
|
||||
const page = await doCachedLogin(browser, { url: 'manufacturing/index/' });
|
||||
@@ -46,7 +81,10 @@ test('Tables - Filters', async ({ browser }) => {
|
||||
await page.getByRole('button', { name: 'Add Filter' }).click();
|
||||
await page.getByRole('combobox', { name: 'Filter' }).click();
|
||||
await page.getByRole('option', { name: 'Outstanding' }).click();
|
||||
await page.getByRole('combobox', { name: 'Value' }).click();
|
||||
|
||||
await page
|
||||
.getByRole('combobox', { name: 'choice-filter-outstanding' })
|
||||
.click();
|
||||
await page.getByRole('option', { name: 'Yes' }).click();
|
||||
|
||||
// Save the filter group
|
||||
|
||||
Reference in New Issue
Block a user