diff --git a/smart-frontend/app/src/components/pages/ActionsPage/ActionsToolbar/ActionsToolbar.tsx b/smart-frontend/app/src/components/pages/ActionsPage/ActionsToolbar/ActionsToolbar.tsx index 2d4a451306..72ae92157c 100644 --- a/smart-frontend/app/src/components/pages/ActionsPage/ActionsToolbar/ActionsToolbar.tsx +++ b/smart-frontend/app/src/components/pages/ActionsPage/ActionsToolbar/ActionsToolbar.tsx @@ -27,7 +27,8 @@ import FrequencySelect from '@uikit/Table/FrequencySelect/FrequencySelect'; const ActionsToolbar: React.FC = () => { const dispatch = useDispatch(); - const actions = useStore(({ adh }) => adh.actions.actions); + + const totalCount = useStore(({ adh }) => adh.actions.totalCount); const paginationParams = useStore(({ adh }) => adh.actionsTable.paginationParams); const requestFrequency = useStore(({ adh }) => adh.actionsTable.requestFrequency); @@ -43,8 +44,8 @@ const ActionsToolbar: React.FC = () => { } /> diff --git a/smart-frontend/app/src/components/pages/AuditPage/AuditEventsToolbar/AuditEventsToolbar.tsx b/smart-frontend/app/src/components/pages/AuditPage/AuditEventsToolbar/AuditEventsToolbar.tsx index bda7c7cb3a..40b10d3944 100644 --- a/smart-frontend/app/src/components/pages/AuditPage/AuditEventsToolbar/AuditEventsToolbar.tsx +++ b/smart-frontend/app/src/components/pages/AuditPage/AuditEventsToolbar/AuditEventsToolbar.tsx @@ -27,7 +27,8 @@ import FrequencySelect from '@uikit/Table/FrequencySelect/FrequencySelect'; const AuditEventsToolbar: React.FC = () => { const dispatch = useDispatch(); - const auditEvents = useStore(({ adh }) => adh.auditEvents.auditEvents); + + const totalCount = useStore(({ adh }) => adh.auditEvents.totalCount); const paginationParams = useStore(({ adh }) => adh.auditEventsTable.paginationParams); const requestFrequency = useStore(({ adh }) => adh.auditEventsTable.requestFrequency); @@ -43,8 +44,8 @@ const AuditEventsToolbar: React.FC = () => { } /> diff --git a/smart-frontend/app/src/components/uikit/Icon/icons/chevron-double.svg b/smart-frontend/app/src/components/uikit/Icon/icons/chevron-double.svg index 8e23e71614..135fa8179c 100644 --- a/smart-frontend/app/src/components/uikit/Icon/icons/chevron-double.svg +++ b/smart-frontend/app/src/components/uikit/Icon/icons/chevron-double.svg @@ -1,4 +1,4 @@ - - - + + + diff --git a/smart-frontend/app/src/components/uikit/Pagination/Pagination.tsx b/smart-frontend/app/src/components/uikit/Pagination/Pagination.tsx index b40ec6feea..ad3da1d41b 100644 --- a/smart-frontend/app/src/components/uikit/Pagination/Pagination.tsx +++ b/smart-frontend/app/src/components/uikit/Pagination/Pagination.tsx @@ -26,7 +26,7 @@ import { usePagination } from './usePagination'; import type { PaginationDataItem, PaginationProps } from '@uikit/Pagination/Pagination.types'; import { defaultPerPagesList } from '@constants'; -const MAX_VISIBLE_ITEMS = 9; +const MAX_VISIBLE_ITEMS = 7; const DEFAULT_PER_PAGE_ITEMS = 14; interface renderButtonsProps { @@ -49,10 +49,7 @@ const RenderNumberButtons = ({ items, setPageNumber, currentPageNumber }: render {item.label} ) : ( - setPageNumber(currentPageNumber - item.pageNumber)} - > + setPageNumber(item.pageNumber)}> {item.label} ), @@ -109,32 +106,32 @@ const Pagination = ({
- {totalPages === 0 && ( + {totalPages !== 0 && ( setPageNumber(0)} disabled={!hasPrev} /> )} setPageNumber(pageNumber - 1)} disabled={!hasPrev} dataTest="pagination-prev-page" /> setPageNumber(pageNumber + 1)} - variant={'next'} + variant="next" disabled={!hasNext} dataTest="pagination-next-page" /> {totalItems !== 0 && ( setPageNumber(totalItems)} - variant={'next'} + variant="next" disabled={!hasNext} dataTest="pagination-last-page" /> diff --git a/smart-frontend/app/src/components/uikit/Pagination/usePagination.ts b/smart-frontend/app/src/components/uikit/Pagination/usePagination.ts index 49c7907c5e..48e41e479c 100644 --- a/smart-frontend/app/src/components/uikit/Pagination/usePagination.ts +++ b/smart-frontend/app/src/components/uikit/Pagination/usePagination.ts @@ -59,134 +59,122 @@ interface Results { } export function usePagination(params: UsePaginationParams): Results { - const { pageNumber, totalItems, maxItems, isNextBtn, perPage } = params; - const totalPages = Math.ceil(totalItems / perPage); + const { pageNumber, totalItems, perPage, maxItems, isNextBtn } = params; + + const totalPages = Math.max(1, Math.ceil(totalItems / perPage)); const hasNext = typeof isNextBtn === 'boolean' ? isNextBtn : pageNumber < totalPages - 1; const hasPrev = pageNumber > 0; - const decorationPrev = pageNumber - DECORATION_STEP >= 0 ? DECORATION_STEP : 0; - const decorationNext = pageNumber + DECORATION_STEP <= totalPages - 1 ? -DECORATION_STEP : totalPages - 1; - const linksAt = useMemo(() => { - // 0 1 2 3 4 + // All pages fits, no need for dots + // 1 2 3 4 5 6 7 if (totalPages <= maxItems) { return 'none'; } - // 0 1 2 3 4 ... 99 + const boundary = maxItems - TRAILING_ITEMS_AMOUNT - 1; + + // 1 2 3 4 5 … 100 // ^ - if (pageNumber < maxItems - TRAILING_ITEMS_AMOUNT - 1) { + if (pageNumber <= boundary) { return 'start'; } - // 0 ... 49 50 60 ... 99 + // 1 … 96 97 98 99 100 // ^ - if ( - pageNumber >= maxItems - TRAILING_ITEMS_AMOUNT - 1 && - pageNumber < totalPages - (maxItems - TRAILING_ITEMS_AMOUNT * 2) - ) { - return 'center'; + if (pageNumber >= totalPages - 1 - boundary) { + return 'end'; } - // 0 ... 95 96 97 98 99 + // 1 … 48 49 50 51 52 … 100 // ^ - return 'end'; - }, [pageNumber, maxItems, totalPages]); + return 'center'; + }, [pageNumber, totalPages, maxItems]); + + // Dots lead on these pages (±5 from current) + const prevDecorationPage = Math.max(0, pageNumber - DECORATION_STEP); + const nextDecorationPage = Math.min(totalPages - 1, pageNumber + DECORATION_STEP); const pageItems = useMemo(() => { + const items: PaginationDataItem[] = []; + + const addPage = (num: number) => { + items.push({ + key: `page-${num}`, + type: 'page', + label: (num + 1).toString(), + pageNumber: num, + }); + }; + + const addDots = (targetPage: number) => { + items.push({ + key: `dots-${items.length}`, + type: 'decoration', + label: '...', + pageNumber: targetPage, + }); + }; + switch (linksAt) { case 'none': { - const from = 0; - const to = totalPages - 1; - const index = 0; - - return getLinks(from, to, index); + for (let i = 0; i < totalPages; i++) { + addPage(i); + } + break; } + case 'start': { - const from = 0; - const to = maxItems - TRAILING_ITEMS_AMOUNT - 1; - const index = 0; - const startLinks = getLinks(from, to, index); - - return [ - ...startLinks, - { - key: startLinks.length.toString(), - type: 'decoration', - label: '...', - pageNumber: decorationNext, - }, - { - key: (startLinks.length + 1).toString(), - type: 'page', - label: totalPages.toString(), - pageNumber: totalPages - 1, - }, - ]; + for (let i = 0; i < maxItems - 1; i++) { + addPage(i); + } + addDots(nextDecorationPage); + addPage(totalPages - 1); + break; } + case 'center': { - const iterations = maxItems - TRAILING_ITEMS_AMOUNT * 2; - const spot = Math.ceil(iterations / 2); - const from = pageNumber - spot + 1; - const to = from + iterations - 1; - const index = 2; - const midLinks = getLinks(from, to, index); - - return [ - { - key: '0', - type: 'page', - label: '1', - pageNumber: 0, - }, - { - key: '1', - type: 'decoration', - label: '...', - pageNumber: decorationPrev, - }, - ...midLinks, - { - key: (index + midLinks.length).toString(), - type: 'decoration', - label: '...', - pageNumber: decorationNext, - }, - { - key: (index + midLinks.length + 1).toString(), - type: 'page', - label: totalPages.toString(), - pageNumber: totalPages - 1, - }, - ]; + addPage(0); + addDots(prevDecorationPage); + + const middleCount = maxItems - 4; + const half = Math.floor(middleCount / 2); + let start = pageNumber - half; + let end = pageNumber + half + (middleCount % 2); + + if (start < 2) { + end += 2 - start; + start = 2; + } + if (end > totalPages - 3) { + start -= end - (totalPages - 3); + end = totalPages - 3; + } + start = Math.max(start, 2); + end = Math.min(end, totalPages - 3); + + for (let i = start; i <= end; i++) { + addPage(i); + } + + addDots(nextDecorationPage); + addPage(totalPages - 1); + break; } + case 'end': { - const from = totalPages - (maxItems - TRAILING_ITEMS_AMOUNT); - const to = totalPages - 1; - const index = 2; - const endLinks = getLinks(from, to, index); - - return [ - { - key: '0', - type: 'page', - label: '1', - pageNumber: 0, - }, - { - key: '1', - type: 'decoration', - label: '...', - pageNumber: decorationPrev, - }, - ...endLinks, - ]; - } - default: { - return []; + addPage(0); + addDots(prevDecorationPage); + + for (let i = totalPages - (maxItems - 1); i < totalPages; i++) { + addPage(i); + } + break; } } - }, [linksAt, totalPages, maxItems, decorationNext, pageNumber, decorationPrev]); + + return items; + }, [linksAt, pageNumber, totalPages, maxItems, prevDecorationPage, nextDecorationPage]); return { hasNext, @@ -195,17 +183,3 @@ export function usePagination(params: UsePaginationParams): Results { totalPages, }; } - -function getLinks(from: number, to: number, index: number): PaginationDataItem[] { - const links: PaginationDataItem[] = []; - for (let pageNumber = from; pageNumber <= to; pageNumber += 1) { - links.push({ - key: (pageNumber - from + index).toString(), - type: 'page', - label: (pageNumber + 1).toString(), - pageNumber, - }); - } - - return links; -} diff --git a/smart-tests/src/test/java/org/smartdata/test/element/PaginationElement.java b/smart-tests/src/test/java/org/smartdata/test/element/PaginationElement.java index 645d1860fc..465e3019b2 100644 --- a/smart-tests/src/test/java/org/smartdata/test/element/PaginationElement.java +++ b/smart-tests/src/test/java/org/smartdata/test/element/PaginationElement.java @@ -31,18 +31,23 @@ public interface PaginationElement { String NEXT_PAGE_BUTTON = ".//*[@data-qa='pagination-next-page']"; String PREV_PAGE_BUTTON = ".//*[@data-qa='pagination-prev-page']"; String LAST_PAGE_BUTTON = ".//*[@data-qa='pagination-last-page']"; + String PAGE_BUTTONS = ".//*[contains(@class, 'paginationButton') and not(contains(@class, 'Arrow'))]"; String EXTEND_PAGES_BUTTON = ".//*[contains(@class, 'paginationButton') and .='...']"; String SHOW_PER_PAGE_INPUT = ".//*[contains(@class, 'pagination') and contains(@class, 'commonSelectField')]//input"; ElementsCollection SHOW_PER_PAGE_OPTIONS = $$x(".//*[@data-qa='pagination-per-page-popover']//li"); - static SelenideElement getNumberedButtonByPageNum(int pageNum, SelenideElement baseElement) { - return getNumberedButtons(baseElement).find(exactText(String.valueOf(pageNum))); + static ElementsCollection getPageButtons(SelenideElement baseElement) { + return findAllFromBaseElement(baseElement, PAGE_BUTTONS); } static ElementsCollection getNumberedButtons(SelenideElement baseElement) { return findAllFromBaseElement(baseElement, PAGINATION_NUMBERED_BUTTONS); } + static SelenideElement getNumberedButtonByPageNum(int pageNum, SelenideElement baseElement) { + return getNumberedButtons(baseElement).find(exactText(String.valueOf(pageNum))); + } + static SelenideElement getPerPageInput(SelenideElement baseElement) { return findFromBaseElement(baseElement, SHOW_PER_PAGE_INPUT); } diff --git a/smart-tests/src/test/java/org/smartdata/test/step/ApiStep.java b/smart-tests/src/test/java/org/smartdata/test/step/ApiStep.java index 447556eff0..6788b1d9cd 100644 --- a/smart-tests/src/test/java/org/smartdata/test/step/ApiStep.java +++ b/smart-tests/src/test/java/org/smartdata/test/step/ApiStep.java @@ -22,6 +22,7 @@ import io.restassured.response.Response; import lombok.extern.slf4j.Slf4j; import org.smartdata.client.generated.invoker.ApiClient; +import org.smartdata.client.generated.model.ActionInfoDto; import org.smartdata.client.generated.model.SubmitActionRequestDto; import org.smartdata.client.generated.model.SubmitRuleRequestDto; import org.springframework.beans.factory.annotation.Autowired; @@ -42,12 +43,11 @@ public ApiStep createRule(String ruleText) { return this; } - public ApiStep createAction(String actionText) { - apiClient.actions().submitAction() + public ActionInfoDto createAction(String actionText) { + return apiClient.actions().submitAction() .body(new SubmitActionRequestDto().action(actionText)) .respSpec(response -> response.expectStatusCode(200)) .executeAs(Response::andReturn); - return this; } public ApiClient getRawClient() { diff --git a/smart-tests/src/test/java/org/smartdata/test/step/PaginationStep.java b/smart-tests/src/test/java/org/smartdata/test/step/PaginationStep.java index ea3f2e2bd6..87fa3044ce 100644 --- a/smart-tests/src/test/java/org/smartdata/test/step/PaginationStep.java +++ b/smart-tests/src/test/java/org/smartdata/test/step/PaginationStep.java @@ -47,6 +47,7 @@ import static org.smartdata.test.element.PaginationElement.getNextPageButton; import static org.smartdata.test.element.PaginationElement.getNumberedButtonByPageNum; import static org.smartdata.test.element.PaginationElement.getNumberedButtons; +import static org.smartdata.test.element.PaginationElement.getPageButtons; import static org.smartdata.test.element.PaginationElement.getPerPageInput; import static org.smartdata.test.element.PaginationElement.getPreviousPageButton; import static org.smartdata.test.element.TableElement.TableType; @@ -75,9 +76,9 @@ public PaginationStep checkAllNumberedButtonsIsEnabled(SelenideElement baseEleme return this; } - @Step("Check that there are {expectedAmount} numbered pagination buttons on the page") - public PaginationStep checkNumberedButtonsAmount(int expectedAmount, SelenideElement baseElement) { - checkSize(getNumberedButtons(baseElement), expectedAmount); + @Step("Check that there are {expectedAmount} page buttons on the page") + public PaginationStep checkPageButtonsAmount(int expectedAmount, SelenideElement baseElement) { + checkSize(getPageButtons(baseElement), expectedAmount); return this; } @@ -126,7 +127,7 @@ public PaginationStep checkPagination(int pageNum, PaginationElement.PageSize pa TableType tableType, TableColumn tableColumn, List testColumnValues, SelenideElement baseElement) { checkNumberedButtonIsSelected(pageNum, baseElement) - .checkNumberedButtonsAmount(getNumberedButtonsQuantity(testColumnValues.size(), pageSize.getSize()), + .checkPageButtonsAmount(getPageButtonsQuantity(testColumnValues.size(), pageSize.getSize()), baseElement) .checkShowPerPageValue(pageSize, baseElement); tableStep.checkColumnValues(tableType, tableColumn, @@ -164,8 +165,8 @@ public void checkPaginationFixture(TableType tableType, TableColumn tableColumn, waitAndClick(getExtendPageButton(baseElement)); checkPagination(6, TEN, tableType, tableColumn, testColumnValues, baseElement); - waitAndClick(getNumberedButtonByPageNum(4, baseElement)); - checkPagination(4, TEN, tableType, tableColumn, testColumnValues, baseElement); + waitAndClick(getNumberedButtonByPageNum(8, baseElement)); + checkPagination(8, TEN, tableType, tableColumn, testColumnValues, baseElement); setShowPerPageOption(THIRTY, baseElement) .checkPagination(1, THIRTY, tableType, tableColumn, testColumnValues, baseElement); @@ -187,7 +188,7 @@ private List getExpectedValues(List testColumnValues, int pageNu return testColumnValues.subList(fromIndex, toIndex); } - private int getNumberedButtonsQuantity(int testColumnValuesSize, int pageSize) { + private int getPageButtonsQuantity(int testColumnValuesSize, int pageSize) { int pages = (testColumnValuesSize + pageSize - 1) / pageSize; return Math.min(pages, 8); } diff --git a/smart-tests/src/test/java/org/smartdata/test/suite/web/ActionsSuite.java b/smart-tests/src/test/java/org/smartdata/test/suite/web/ActionsSuite.java index 29f4bf830e..b8284f277a 100644 --- a/smart-tests/src/test/java/org/smartdata/test/suite/web/ActionsSuite.java +++ b/smart-tests/src/test/java/org/smartdata/test/suite/web/ActionsSuite.java @@ -29,6 +29,7 @@ import org.smartdata.test.step.DataBaseStep; import org.smartdata.test.step.LoginStep; import org.smartdata.test.step.MenuStep; +import org.smartdata.test.step.PaginationStep; import org.smartdata.test.step.TableStep; import org.smartdata.test.suite.SsmWebBaseSuite; import org.smartdata.test.util.comparator.ActionStatusComparator; @@ -38,6 +39,10 @@ import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + import static org.smartdata.test.element.ActionsDetailsPageElement.HEADER_SUCCESSFUL_ICON; import static org.smartdata.test.element.ActionsPageElement.ActionsTableColumn.ACTION; import static org.smartdata.test.element.ActionsPageElement.ActionsTableColumn.CREATE_TIME; @@ -49,6 +54,7 @@ import static org.smartdata.test.model.ActionStatus.SUCCESSFUL; import static org.smartdata.test.model.SortOrder.ASC; import static org.smartdata.test.model.SortOrder.DESC; +import static org.smartdata.test.util.constant.CommonConstants.PAGINATION_QUANTITY; @Feature("Actions page") public class ActionsSuite extends SsmWebBaseSuite { @@ -75,6 +81,9 @@ public class ActionsSuite extends SsmWebBaseSuite { @Autowired private ActionsDetailsStep actionsDetailsStep; + @Autowired + private PaginationStep paginationStep; + @BeforeMethod public void testPrepare() { loginStep.loginAs(UserRole.OWNER); @@ -196,6 +205,14 @@ public void testHostAssignment() { actionsStep.checkHostAssignment(); } + @TmsLink("90209") + @Story("Actions") + @Test(description = "Check pagination") + public void testPagination() { + List actionIds = prepareDataForPaginationTest(); + paginationStep.checkPaginationFixture(ID, actionIds); + } + @Step("Create actions for sorting test") private void prepareDataForSortingTest() { dataBaseStep.insertDataForActionSortTest(); @@ -217,4 +234,16 @@ private void prepareDataForActionDetailsPageTest() { actionsStep.refreshPage(); tableStep.checkTableRowsCountIs(1); } + + @Step("Create actions for pagination test") + private List prepareDataForPaginationTest() { + List actionIds = new ArrayList<>(); + tableStep.checkTableIsEmpty(); + for (int i = 1; i <= PAGINATION_QUANTITY; i++) { + actionIds.add(apiStep.createAction(TEST_ACTION_TEXT).getId().toString()); + } + actionsStep.refreshPage(); + Collections.reverse(actionIds); + return actionIds; + } } diff --git a/smart-tests/src/test/java/org/smartdata/test/suite/web/AuditSuite.java b/smart-tests/src/test/java/org/smartdata/test/suite/web/AuditSuite.java index be14b5e6d8..9ab2724268 100644 --- a/smart-tests/src/test/java/org/smartdata/test/suite/web/AuditSuite.java +++ b/smart-tests/src/test/java/org/smartdata/test/suite/web/AuditSuite.java @@ -30,6 +30,7 @@ import org.smartdata.test.step.DataBaseStep; import org.smartdata.test.step.LoginStep; import org.smartdata.test.step.MenuStep; +import org.smartdata.test.step.PaginationStep; import org.smartdata.test.step.TableStep; import org.smartdata.test.suite.SsmWebBaseSuite; import org.smartdata.test.util.comparator.UiDateTimeComparator; @@ -37,6 +38,9 @@ import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; +import java.util.ArrayList; +import java.util.List; + import static org.smartdata.test.element.AuditPageElement.AuditTableColumn.DATE; import static org.smartdata.test.element.AuditPageElement.AuditTableColumn.ID; import static org.smartdata.test.element.AuditPageElement.AuditTableColumn.OBJECT_ID; @@ -45,6 +49,7 @@ import static org.smartdata.test.element.AuditPageElement.AuditTableColumn.USER; import static org.smartdata.test.model.SortOrder.ASC; import static org.smartdata.test.model.SortOrder.DESC; +import static org.smartdata.test.util.constant.CommonConstants.PAGINATION_QUANTITY; @Feature("Audit page") public class AuditSuite extends SsmWebBaseSuite { @@ -67,6 +72,9 @@ public class AuditSuite extends SsmWebBaseSuite { @Autowired private ApiStep apiStep; + @Autowired + private PaginationStep paginationStep; + @BeforeMethod public void testPrepare() { loginStep.loginAs(UserRole.OWNER); @@ -116,6 +124,16 @@ public void testFrequency() { } } + @TmsLink("90592") + @Story("Audit") + @Test(description = "Check pagination") + public void testPagination() { + List objectIds = prepareDataForPaginationTest(); + tableStep.clickOnSortingColumn(OBJECT_ID) + .checkSelectedSorting(OBJECT_ID, ASC); + paginationStep.checkPaginationFixture(OBJECT_ID, objectIds); + } + @Step("Create audit events for sorting test") private void prepareDataForSortingTest() { dataBaseStep.insertDataForAuditSortTest(); @@ -133,4 +151,15 @@ private void prepareDataForFiltrationTest() { tableStep.refreshPage(); tableStep.checkTableRowsCountIs(2); } + + @Step("Create actions for audit pagination test") + private List prepareDataForPaginationTest() { + List cmdletIds = new ArrayList<>(); + tableStep.checkTableIsEmpty(); + for (int i = 1; i <= PAGINATION_QUANTITY; i++) { + cmdletIds.add(apiStep.createAction("sleep -ms 10").getCmdletId().toString()); + } + auditStep.refreshPage(); + return cmdletIds; + } }