From e5183e3b88765a4031e603803b81bf52900b956f Mon Sep 17 00:00:00 2001 From: Alan Greene Date: Fri, 18 Mar 2022 13:49:37 +0000 Subject: [PATCH] Add client-side pagination Add client-side pagination to all list pages via the `ListPageLayout` container. Update the `children` prop to expect a function so we can use the render props pattern to allow the `ListPageLayout` component to handle all of the pagination logic required. Consuming pages pass their raw resources to `ListPageLayout` and it passes back the correct slice for the current pagination settings which the consumer then formats / renders as needed. Although we're still loading and storing the full list of resources from the API this does have significant performance benefits in the client, especially with larger number of resources. Notably, this addresses freezing reported by a number of users on the PipelineRuns and TaskRuns pages with busy clusters. --- .../ClusterInterceptors.js | 89 +++--- src/containers/ClusterTasks/ClusterTasks.js | 243 +++++++++-------- .../ClusterTriggerBindings.js | 79 +++--- src/containers/Conditions/Conditions.js | 95 ++++--- .../EventListener/EventListener.stories.js | 2 +- .../EventListeners/EventListeners.js | 91 ++++--- .../ListPageLayout/ListPageLayout.js | 57 +++- .../ListPageLayout/ListPageLayout.stories.js | 34 ++- .../ListPageLayout/ListPageLayout.test.js | 37 +-- .../PipelineResources/PipelineResources.js | 39 +-- src/containers/PipelineRuns/PipelineRuns.js | 59 ++-- src/containers/Pipelines/Pipelines.js | 251 +++++++++-------- src/containers/ResourceList/ResourceList.js | 133 ++++----- src/containers/TaskRuns/TaskRuns.js | 51 ++-- src/containers/Tasks/Tasks.js | 253 ++++++++++-------- .../TriggerBindings/TriggerBindings.js | 85 +++--- .../TriggerTemplates/TriggerTemplates.js | 85 +++--- src/containers/Triggers/Triggers.js | 95 ++++--- src/nls/messages_de.json | 3 + src/nls/messages_en.json | 3 + src/nls/messages_es.json | 3 + src/nls/messages_fr.json | 3 + src/nls/messages_it.json | 3 + src/nls/messages_ja.json | 3 + src/nls/messages_ko.json | 3 + src/nls/messages_pt.json | 3 + src/nls/messages_zh-Hans.json | 3 + src/nls/messages_zh-Hant.json | 3 + src/scss/_carbon.scss | 1 + 29 files changed, 1015 insertions(+), 794 deletions(-) diff --git a/src/containers/ClusterInterceptors/ClusterInterceptors.js b/src/containers/ClusterInterceptors/ClusterInterceptors.js index 86deac0ae..ed7049706 100644 --- a/src/containers/ClusterInterceptors/ClusterInterceptors.js +++ b/src/containers/ClusterInterceptors/ClusterInterceptors.js @@ -21,6 +21,30 @@ import { Link as CarbonLink } from 'carbon-components-react'; import { ListPageLayout } from '..'; import { useClusterInterceptors } from '../../api'; +function getFormattedResources(resources) { + return resources.map(clusterInterceptor => ({ + id: clusterInterceptor.metadata.uid, + name: ( + + {clusterInterceptor.metadata.name} + + ), + createdTime: ( + + ) + })); +} + function ClusterInterceptors({ intl }) { const location = useLocation(); const filters = getFilters(location); @@ -67,56 +91,35 @@ function ClusterInterceptors({ intl }) { } ]; - const clusterInterceptorsFormatted = clusterInterceptors.map( - clusterInterceptor => ({ - id: clusterInterceptor.metadata.uid, - name: ( - - {clusterInterceptor.metadata.name} - - ), - createdTime: ( - - ) - }) - ); - return ( - + {({ resources }) => ( +
+ )} ); } diff --git a/src/containers/ClusterTasks/ClusterTasks.js b/src/containers/ClusterTasks/ClusterTasks.js index 97bdaf6ef..e1e3171e0 100644 --- a/src/containers/ClusterTasks/ClusterTasks.js +++ b/src/containers/ClusterTasks/ClusterTasks.js @@ -1,5 +1,5 @@ /* -Copyright 2019-2021 The Tekton Authors +Copyright 2019-2022 The Tekton Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at @@ -32,6 +32,95 @@ import { getFilters, urls, useTitleSync } from '@tektoncd/dashboard-utils'; import { ListPageLayout } from '..'; import { deleteClusterTask, useClusterTasks, useIsReadOnly } from '../../api'; +function getFormattedResources({ + intl, + isReadOnly, + openDeleteModal, + resources +}) { + return resources.map(clusterTask => ({ + id: clusterTask.metadata.uid, + name: ( + + {clusterTask.metadata.name} + + ), + createdTime: ( + + ), + actions: ( + <> + {!isReadOnly ? ( +
- {showDeleteModal ? ( - - ) : null} + {({ resources }) => ( + <> +
+ {showDeleteModal ? ( + + ) : null} + + )} ); } diff --git a/src/containers/ClusterTriggerBindings/ClusterTriggerBindings.js b/src/containers/ClusterTriggerBindings/ClusterTriggerBindings.js index 88b299416..6114fe475 100644 --- a/src/containers/ClusterTriggerBindings/ClusterTriggerBindings.js +++ b/src/containers/ClusterTriggerBindings/ClusterTriggerBindings.js @@ -1,5 +1,5 @@ /* -Copyright 2020-2021 The Tekton Authors +Copyright 2020-2022 The Tekton Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at @@ -21,6 +21,24 @@ import { Link as CarbonLink } from 'carbon-components-react'; import { ListPageLayout } from '..'; import { useClusterTriggerBindings } from '../../api'; +function getFormattedResources(resources) { + return resources.map(binding => ({ + id: `${binding.metadata.name}`, + name: ( + + {binding.metadata.name} + + ), + date: + })); +} + function ClusterTriggerBindings({ intl }) { const location = useLocation(); const filters = getFilters(location); @@ -58,50 +76,35 @@ function ClusterTriggerBindings({ intl }) { } ]; - const clusterTriggerBindingsFormatted = clusterTriggerBindings.map( - binding => ({ - id: `${binding.metadata.name}`, - name: ( - - {binding.metadata.name} - - ), - date: - }) - ); - return ( -
+ {({ resources }) => ( +
+ )} ); } diff --git a/src/containers/Conditions/Conditions.js b/src/containers/Conditions/Conditions.js index 1aa636798..a2156adb9 100644 --- a/src/containers/Conditions/Conditions.js +++ b/src/containers/Conditions/Conditions.js @@ -1,5 +1,5 @@ /* -Copyright 2020-2021 The Tekton Authors +Copyright 2020-2022 The Tekton Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at @@ -21,6 +21,28 @@ import { Link as CarbonLink } from 'carbon-components-react'; import { useConditions, useSelectedNamespace } from '../../api'; import { ListPageLayout } from '..'; +function getFormattedResources(resources) { + return resources.map(condition => ({ + id: condition.metadata.uid, + name: ( + + {condition.metadata.name} + + ), + namespace: condition.metadata.namespace, + createdTime: ( + + ) + })); +} + function Conditions({ intl }) { const location = useLocation(); const params = useParams(); @@ -74,49 +96,36 @@ function Conditions({ intl }) { } ]; - const conditionsFormatted = conditions.map(condition => ({ - id: condition.metadata.uid, - name: ( - - {condition.metadata.name} - - ), - namespace: condition.metadata.namespace, - createdTime: ( - - ) - })); - return ( - -
+ + {({ resources }) => ( +
+ )} ); } diff --git a/src/containers/EventListener/EventListener.stories.js b/src/containers/EventListener/EventListener.stories.js index d8547dcf2..cde69eedf 100644 --- a/src/containers/EventListener/EventListener.stories.js +++ b/src/containers/EventListener/EventListener.stories.js @@ -146,7 +146,7 @@ export default { route: `/namespaces/${namespace}/eventlisteners/${name}` }, component: EventListenerContainer, - title: 'Containers/EventListenerContainer' + title: 'Containers/EventListener' }; export const Base = () => ; diff --git a/src/containers/EventListeners/EventListeners.js b/src/containers/EventListeners/EventListeners.js index 1839379c5..895aad0b0 100644 --- a/src/containers/EventListeners/EventListeners.js +++ b/src/containers/EventListeners/EventListeners.js @@ -1,5 +1,5 @@ /* -Copyright 2019-2021 The Tekton Authors +Copyright 2019-2022 The Tekton Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at @@ -21,6 +21,26 @@ import { Link as CarbonLink } from 'carbon-components-react'; import { ListPageLayout } from '..'; import { useEventListeners, useSelectedNamespace } from '../../api'; +function getFormattedResources(resources) { + return resources.map(listener => ({ + id: `${listener.metadata.namespace}:${listener.metadata.name}`, + name: ( + + {listener.metadata.name} + + ), + namespace: listener.metadata.namespace, + date: + })); +} + function EventListeners({ intl }) { const location = useLocation(); const params = useParams(); @@ -69,47 +89,36 @@ function EventListeners({ intl }) { } ]; - const eventListenersFormatted = eventListeners.map(listener => ({ - id: `${listener.metadata.namespace}:${listener.metadata.name}`, - name: ( - - {listener.metadata.name} - - ), - namespace: listener.metadata.namespace, - date: - })); - return ( - -
+ + {({ resources }) => ( +
+ )} ); } diff --git a/src/containers/ListPageLayout/ListPageLayout.js b/src/containers/ListPageLayout/ListPageLayout.js index bbcf2b127..cb32c7f11 100644 --- a/src/containers/ListPageLayout/ListPageLayout.js +++ b/src/containers/ListPageLayout/ListPageLayout.js @@ -1,5 +1,5 @@ /* -Copyright 2020-2021 The Tekton Authors +Copyright 2020-2022 The Tekton Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at @@ -11,7 +11,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React, { useEffect, useState } from 'react'; +import PropTypes from 'prop-types'; import { generatePath, useHistory, @@ -20,7 +21,7 @@ import { useRouteMatch } from 'react-router-dom'; import { injectIntl } from 'react-intl'; -import { InlineNotification } from 'carbon-components-react'; +import { InlineNotification, Pagination } from 'carbon-components-react'; import { ALL_NAMESPACES, getErrorMessage, @@ -36,6 +37,7 @@ export const ListPageLayout = ({ filters, hideNamespacesDropdown, intl, + resources = [], title }) => { const history = useHistory(); @@ -46,6 +48,20 @@ export const ListPageLayout = ({ const { selectedNamespace: namespace, selectNamespace } = useSelectedNamespace(); + const [pageSize, setPageSize] = useState(100); + const [page, setPage] = useState(1); // pagination component counts from 1 + + useEffect(() => { + const savedPageSize = localStorage.getItem('tkn-page-size'); + if (savedPageSize) { + setPageSize(parseInt(savedPageSize, 10)); + } + }, []); + + useEffect(() => { + localStorage.setItem('tkn-page-size', pageSize); + }, [pageSize]); + function setPath(path) { history.push(`${path}${location.search}`); } @@ -72,6 +88,11 @@ export const ListPageLayout = ({ setPath(newURL); } + const resourcesForCurrentPage = resources.slice( + (page - 1) * pageSize, + page * pageSize + ); + return ( <>
@@ -109,9 +130,37 @@ export const ListPageLayout = ({ } /> )} - {children} + {children({ resources: resourcesForCurrentPage })} + {resources.length > 10 && ( + { + setPage(newPage); + setPageSize(newPageSize); + }} + page={page} + pageSize={pageSize} + pageSizes={[10, 25, 50, 100]} + totalItems={resources.length} + /> + )} ); }; export default injectIntl(ListPageLayout); + +ListPageLayout.propTypes = { + children: PropTypes.func.isRequired +}; diff --git a/src/containers/ListPageLayout/ListPageLayout.stories.js b/src/containers/ListPageLayout/ListPageLayout.stories.js index e682ec6b4..b62e67e14 100644 --- a/src/containers/ListPageLayout/ListPageLayout.stories.js +++ b/src/containers/ListPageLayout/ListPageLayout.stories.js @@ -1,5 +1,5 @@ /* -Copyright 2020-2021 The Tekton Authors +Copyright 2020-2022 The Tekton Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at @@ -13,6 +13,7 @@ limitations under the License. import React from 'react'; import { QueryClient, QueryClientProvider } from 'react-query'; +import { Table } from '@tektoncd/dashboard-components'; import { ALL_NAMESPACES } from '@tektoncd/dashboard-utils'; import ListPageLayoutContainer from './ListPageLayout'; @@ -42,6 +43,10 @@ const namespaceContext = { }; export default { + args: { + hideNamespacesDropdown: false, + title: 'PipelineRuns' + }, component: ListPageLayoutContainer, decorators: [ Story => { @@ -64,20 +69,27 @@ export default { export const Base = args => ( - page content goes here + {() => page content goes here} ); -Base.args = { - hideNamespacesDropdown: false, - title: 'PipelineRuns' -}; export const WithFilters = args => ( - page content goes here + {() => page content goes here} + +); + +export const WithPagination = args => ( + ({ + id: index, + value: `Row ${index + 1}` + }))} + {...args} + > + {({ resources }) => ( +
+ )} ); -WithFilters.args = { - hideNamespacesDropdown: false, - title: 'PipelineRuns' -}; diff --git a/src/containers/ListPageLayout/ListPageLayout.test.js b/src/containers/ListPageLayout/ListPageLayout.test.js index 8bf281b6f..f342e5ab5 100644 --- a/src/containers/ListPageLayout/ListPageLayout.test.js +++ b/src/containers/ListPageLayout/ListPageLayout.test.js @@ -1,5 +1,5 @@ /* -Copyright 2020-2021 The Tekton Authors +Copyright 2020-2022 The Tekton Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at @@ -36,7 +36,9 @@ describe('ListPageLayout', () => { , + > + {() => {}} + , { path, route: path } ); fireEvent.click(getByDisplayValue(/All Namespaces/i)); @@ -65,10 +67,9 @@ describe('ListPageLayout', () => { const selectNamespace = jest.fn(); jest.spyOn(window.history, 'pushState'); const { getByText, getByDisplayValue } = renderWithRouter( - , + + {() => {}} + , { path, route: `/namespaces/${namespace}/fake/path` } ); fireEvent.click(getByDisplayValue(namespace)); @@ -94,10 +95,9 @@ describe('ListPageLayout', () => { jest.spyOn(window.history, 'pushState'); const path = '/namespaces/:namespace/fake/path'; const { getByText, getByDisplayValue } = renderWithRouter( - , + + {() => {}} + , { path, route: `/namespaces/${namespace}/fake/path` @@ -126,10 +126,9 @@ describe('ListPageLayout', () => { jest.spyOn(window.history, 'pushState'); const path = '/namespaces/:namespace/fake/path'; const { getByTitle } = renderWithRouter( - , + + {() => {}} + , { path, route: `/namespaces/${namespace}/fake/path` } ); fireEvent.click(getByTitle(/clear selected item/i)); @@ -144,26 +143,28 @@ describe('ListPageLayout', () => { it('does not render namespaces dropdown in single namespace visibility mode', () => { jest.spyOn(API, 'useTenantNamespace').mockImplementation(() => 'fake'); const { queryByPlaceholderText } = renderWithRouter( - + {() => {}} ); expect(queryByPlaceholderText(/select namespace/i)).toBeFalsy(); }); it('does not render namespaces dropdown when hideNamespacesDropdown is true', () => { const { queryByPlaceholderText } = renderWithRouter( - + {() => {}} ); expect(queryByPlaceholderText(/select namespace/i)).toBeFalsy(); }); it('does not render LabelFilter input by default', () => { - const { queryByLabelText } = renderWithRouter(); + const { queryByLabelText } = renderWithRouter( + {() => {}} + ); expect(queryByLabelText(/Input a label filter/i)).toBeFalsy(); }); it('renders LabelFilter input when filters prop is provided', () => { const { getAllByLabelText } = renderWithRouter( - + {() => {}} ); expect(getAllByLabelText(/Input a label filter/i)[0]).toBeTruthy(); }); diff --git a/src/containers/PipelineResources/PipelineResources.js b/src/containers/PipelineResources/PipelineResources.js index 47466090c..872547c6e 100644 --- a/src/containers/PipelineResources/PipelineResources.js +++ b/src/containers/PipelineResources/PipelineResources.js @@ -1,5 +1,5 @@ /* -Copyright 2019-2021 The Tekton Authors +Copyright 2019-2022 The Tekton Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at @@ -150,24 +150,29 @@ export function PipelineResources({ intl }) { - - {isDeleteModalOpen ? ( - - ) : null} + {({ resources }) => ( + <> + + {isDeleteModalOpen ? ( + + ) : null} + + )} ); } diff --git a/src/containers/PipelineRuns/PipelineRuns.js b/src/containers/PipelineRuns/PipelineRuns.js index 54d5b0a57..28e9db9f1 100644 --- a/src/containers/PipelineRuns/PipelineRuns.js +++ b/src/containers/PipelineRuns/PipelineRuns.js @@ -1,5 +1,5 @@ /* -Copyright 2019-2021 The Tekton Authors +Copyright 2019-2022 The Tekton Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at @@ -316,30 +316,39 @@ export function PipelineRuns({ intl }) { ); return ( - - { - return runMatchesStatusFilter({ - run, - statusFilter - }); - })} - pipelineRunActions={pipelineRunActions()} - selectedNamespace={namespace} - toolbarButtons={toolbarButtons} - /> - {showDeleteModal ? ( - - ) : null} + { + return runMatchesStatusFilter({ + run, + statusFilter + }); + })} + title="PipelineRuns" + > + {({ resources }) => ( + <> + + {showDeleteModal ? ( + + ) : null} + + )} ); } diff --git a/src/containers/Pipelines/Pipelines.js b/src/containers/Pipelines/Pipelines.js index ad495ba11..e15f26bba 100644 --- a/src/containers/Pipelines/Pipelines.js +++ b/src/containers/Pipelines/Pipelines.js @@ -1,5 +1,5 @@ /* -Copyright 2019-2021 The Tekton Authors +Copyright 2019-2022 The Tekton Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at @@ -42,6 +42,98 @@ import { useSelectedNamespace } from '../../api'; +function getFormattedResources({ + intl, + isReadOnly, + openDeleteModal, + resources +}) { + return resources.map(pipeline => ({ + id: pipeline.metadata.uid, + name: ( + + {pipeline.metadata.name} + + ), + namespace: pipeline.metadata.namespace, + createdTime: ( + + ), + actions: ( + <> + {!isReadOnly ? ( +
- openDeleteModal([{ id: pipeline.metadata.uid }], () => {}) - } - renderIcon={DeleteIcon} - size="sm" - tooltipAlignment="center" - tooltipPosition="left" - /> - ) : null} - {!isReadOnly ? ( -
- {showDeleteModal ? ( - - ) : null} + {showDeleteModal ? ( + + ) : null} + + )} ); } diff --git a/src/containers/ResourceList/ResourceList.js b/src/containers/ResourceList/ResourceList.js index 43a464bef..1f53ba149 100644 --- a/src/containers/ResourceList/ResourceList.js +++ b/src/containers/ResourceList/ResourceList.js @@ -1,5 +1,5 @@ /* -Copyright 2019-2021 The Tekton Authors +Copyright 2019-2022 The Tekton Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at @@ -88,73 +88,76 @@ export function ResourceListContainer({ intl }) { error={getError()} filters={filters} hideNamespacesDropdown={!isNamespaced} + resources={resources} title={`${group}/${version}/${type}`} > -
{ - const { - creationTimestamp, - name, - namespace: resourceNamespace, - uid - } = resource.metadata; - - return { - id: uid, - name: ( - ( +
- {name} - - ), - namespace: resourceNamespace, - createdTime: - }; - })} - loading={isLoadingAPIResource || isLoadingResources} - emptyTextAllNamespaces={emptyText} - emptyTextSelectedNamespace={emptyText} - /> + : null, + { + key: 'createdTime', + header: intl.formatMessage({ + id: 'dashboard.tableHeader.createdTime', + defaultMessage: 'Created' + }) + } + ].filter(Boolean)} + rows={paginatedResources.map(resource => { + const { + creationTimestamp, + name, + namespace: resourceNamespace, + uid + } = resource.metadata; + + return { + id: uid, + name: ( + + {name} + + ), + namespace: resourceNamespace, + createdTime: + }; + })} + loading={isLoadingAPIResource || isLoadingResources} + emptyTextAllNamespaces={emptyText} + emptyTextSelectedNamespace={emptyText} + /> + )} ); } diff --git a/src/containers/TaskRuns/TaskRuns.js b/src/containers/TaskRuns/TaskRuns.js index cffdf1e07..976d65ddd 100644 --- a/src/containers/TaskRuns/TaskRuns.js +++ b/src/containers/TaskRuns/TaskRuns.js @@ -305,27 +305,36 @@ function TaskRuns({ intl }) { ); return ( - - { - return runMatchesStatusFilter({ run, statusFilter }); - })} - taskRunActions={taskRunActions()} - toolbarButtons={toolbarButtons} - /> - {showDeleteModal ? ( - - ) : null} + { + return runMatchesStatusFilter({ run, statusFilter }); + })} + title="TaskRuns" + > + {({ resources }) => ( + <> + + {showDeleteModal ? ( + + ) : null} + + )} ); } diff --git a/src/containers/Tasks/Tasks.js b/src/containers/Tasks/Tasks.js index 3d511e3a1..217a8a882 100644 --- a/src/containers/Tasks/Tasks.js +++ b/src/containers/Tasks/Tasks.js @@ -1,5 +1,5 @@ /* -Copyright 2019-2021 The Tekton Authors +Copyright 2019-2022 The Tekton Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at @@ -42,6 +42,99 @@ import { useTasks } from '../../api'; +function getFormattedResources({ + intl, + isReadOnly, + openDeleteModal, + resources +}) { + return resources.map(task => ({ + id: task.metadata.uid, + name: ( + + {task.metadata.name} + + ), + namespace: task.metadata.namespace, + createdTime: ( + + ), + actions: ( + <> + {!isReadOnly ? ( +
- openDeleteModal([{ id: task.metadata.uid }], () => {}) - } - renderIcon={DeleteIcon} - size="sm" - tooltipAlignment="center" - tooltipPosition="left" - /> - ) : null} - {!isReadOnly ? ( -
- {showDeleteModal ? ( - - ) : null} + {showDeleteModal ? ( + + ) : null} + + )} ); } diff --git a/src/containers/TriggerBindings/TriggerBindings.js b/src/containers/TriggerBindings/TriggerBindings.js index 8f92e0bfa..c5bed0080 100644 --- a/src/containers/TriggerBindings/TriggerBindings.js +++ b/src/containers/TriggerBindings/TriggerBindings.js @@ -1,5 +1,5 @@ /* -Copyright 2019-2021 The Tekton Authors +Copyright 2019-2022 The Tekton Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at @@ -21,6 +21,26 @@ import { Link as CarbonLink } from 'carbon-components-react'; import { ListPageLayout } from '..'; import { useSelectedNamespace, useTriggerBindings } from '../../api'; +function getFormattedResources(resources) { + return resources.map(binding => ({ + id: `${binding.metadata.namespace}:${binding.metadata.name}`, + name: ( + + {binding.metadata.name} + + ), + namespace: binding.metadata.namespace, + date: + })); +} + export function TriggerBindings({ intl }) { const location = useLocation(); const params = useParams(); @@ -69,51 +89,36 @@ export function TriggerBindings({ intl }) { } ]; - const triggerBindingsFormatted = triggerBindings.map(binding => ({ - id: `${binding.metadata.namespace}:${binding.metadata.name}`, - name: ( - - {binding.metadata.name} - - ), - namespace: binding.metadata.namespace, - date: - })); - return ( -
+ {({ resources }) => ( +
+ )} ); } diff --git a/src/containers/TriggerTemplates/TriggerTemplates.js b/src/containers/TriggerTemplates/TriggerTemplates.js index cb1215a51..cf6b159fe 100644 --- a/src/containers/TriggerTemplates/TriggerTemplates.js +++ b/src/containers/TriggerTemplates/TriggerTemplates.js @@ -1,5 +1,5 @@ /* -Copyright 2019-2021 The Tekton Authors +Copyright 2019-2022 The Tekton Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at @@ -21,6 +21,26 @@ import { Link as CarbonLink } from 'carbon-components-react'; import { ListPageLayout } from '..'; import { useSelectedNamespace, useTriggerTemplates } from '../../api'; +function getFormattedResources(resources) { + return resources.map(template => ({ + id: `${template.metadata.namespace}:${template.metadata.name}`, + name: ( + + {template.metadata.name} + + ), + namespace: template.metadata.namespace, + date: + })); +} + function TriggerTemplates({ intl }) { useTitleSync({ page: 'TriggerTemplates' }); @@ -65,51 +85,36 @@ function TriggerTemplates({ intl }) { } ]; - const triggerTemplatesFormatted = triggerTemplates.map(template => ({ - id: `${template.metadata.namespace}:${template.metadata.name}`, - name: ( - - {template.metadata.name} - - ), - namespace: template.metadata.namespace, - date: - })); - return ( -
+ {({ resources }) => ( +
+ )} ); } diff --git a/src/containers/Triggers/Triggers.js b/src/containers/Triggers/Triggers.js index c2d310416..9bc2379e2 100644 --- a/src/containers/Triggers/Triggers.js +++ b/src/containers/Triggers/Triggers.js @@ -1,5 +1,5 @@ /* -Copyright 2021 The Tekton Authors +Copyright 2021-2022 The Tekton Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at @@ -21,6 +21,28 @@ import { Link as CarbonLink } from 'carbon-components-react'; import { ListPageLayout } from '..'; import { useSelectedNamespace, useTriggers } from '../../api'; +function getFormattedResources(resources) { + return resources.map(trigger => ({ + id: trigger.metadata.uid, + name: ( + + {trigger.metadata.name} + + ), + namespace: trigger.metadata.namespace, + createdTime: ( + + ) + })); +} + function Triggers({ intl }) { useTitleSync({ page: 'Triggers' }); @@ -77,49 +99,36 @@ function Triggers({ intl }) { } ]; - const triggersFormatted = triggers.map(trigger => ({ - id: trigger.metadata.uid, - name: ( - - {trigger.metadata.name} - - ), - namespace: trigger.metadata.namespace, - createdTime: ( - - ) - })); - return ( - -
+ + {({ resources }) => ( +
+ )} ); } diff --git a/src/nls/messages_de.json b/src/nls/messages_de.json index 180a9b61b..9b4e422cf 100644 --- a/src/nls/messages_de.json +++ b/src/nls/messages_de.json @@ -164,6 +164,9 @@ "dashboard.notFound.description": "", "dashboard.notFound.title": "", "dashboard.notification.clear": "", + "dashboard.pagination.nextPage": "", + "dashboard.pagination.pageSize": "", + "dashboard.pagination.previousPage": "", "dashboard.parameters.title": "", "dashboard.pipelineResource.fieldName": "", "dashboard.pipelineResource.secretKey": "", diff --git a/src/nls/messages_en.json b/src/nls/messages_en.json index 4237020d5..bfcabdfeb 100644 --- a/src/nls/messages_en.json +++ b/src/nls/messages_en.json @@ -164,6 +164,9 @@ "dashboard.notFound.description": "We couldn’t find the page you were looking for, but here are some helpful links instead:", "dashboard.notFound.title": "Oops… Page not found", "dashboard.notification.clear": "Clear notification", + "dashboard.pagination.nextPage": "Next page", + "dashboard.pagination.pageSize": "Items per page:", + "dashboard.pagination.previousPage": "Previous page", "dashboard.parameters.title": "Parameters", "dashboard.pipelineResource.fieldName": "Field name", "dashboard.pipelineResource.secretKey": "Secret key", diff --git a/src/nls/messages_es.json b/src/nls/messages_es.json index ddbe465f9..327bc501e 100644 --- a/src/nls/messages_es.json +++ b/src/nls/messages_es.json @@ -164,6 +164,9 @@ "dashboard.notFound.description": "", "dashboard.notFound.title": "", "dashboard.notification.clear": "", + "dashboard.pagination.nextPage": "", + "dashboard.pagination.pageSize": "", + "dashboard.pagination.previousPage": "", "dashboard.parameters.title": "", "dashboard.pipelineResource.fieldName": "", "dashboard.pipelineResource.secretKey": "", diff --git a/src/nls/messages_fr.json b/src/nls/messages_fr.json index 4f6d7b5ab..6ed9c8c68 100644 --- a/src/nls/messages_fr.json +++ b/src/nls/messages_fr.json @@ -164,6 +164,9 @@ "dashboard.notFound.description": "", "dashboard.notFound.title": "", "dashboard.notification.clear": "", + "dashboard.pagination.nextPage": "", + "dashboard.pagination.pageSize": "", + "dashboard.pagination.previousPage": "", "dashboard.parameters.title": "", "dashboard.pipelineResource.fieldName": "", "dashboard.pipelineResource.secretKey": "", diff --git a/src/nls/messages_it.json b/src/nls/messages_it.json index 2d83b6e47..3027c63e6 100644 --- a/src/nls/messages_it.json +++ b/src/nls/messages_it.json @@ -164,6 +164,9 @@ "dashboard.notFound.description": "", "dashboard.notFound.title": "", "dashboard.notification.clear": "", + "dashboard.pagination.nextPage": "", + "dashboard.pagination.pageSize": "", + "dashboard.pagination.previousPage": "", "dashboard.parameters.title": "", "dashboard.pipelineResource.fieldName": "", "dashboard.pipelineResource.secretKey": "", diff --git a/src/nls/messages_ja.json b/src/nls/messages_ja.json index 2da309ff5..d8f71b108 100644 --- a/src/nls/messages_ja.json +++ b/src/nls/messages_ja.json @@ -164,6 +164,9 @@ "dashboard.notFound.description": "", "dashboard.notFound.title": "", "dashboard.notification.clear": "通知をクリア", + "dashboard.pagination.nextPage": "", + "dashboard.pagination.pageSize": "", + "dashboard.pagination.previousPage": "", "dashboard.parameters.title": "パラメータ", "dashboard.pipelineResource.fieldName": "フィールド名", "dashboard.pipelineResource.secretKey": "Secretキー", diff --git a/src/nls/messages_ko.json b/src/nls/messages_ko.json index e819e15a6..f48101fc7 100644 --- a/src/nls/messages_ko.json +++ b/src/nls/messages_ko.json @@ -164,6 +164,9 @@ "dashboard.notFound.description": "", "dashboard.notFound.title": "", "dashboard.notification.clear": "", + "dashboard.pagination.nextPage": "", + "dashboard.pagination.pageSize": "", + "dashboard.pagination.previousPage": "", "dashboard.parameters.title": "", "dashboard.pipelineResource.fieldName": "", "dashboard.pipelineResource.secretKey": "", diff --git a/src/nls/messages_pt.json b/src/nls/messages_pt.json index 4ec4b4be4..84157f137 100644 --- a/src/nls/messages_pt.json +++ b/src/nls/messages_pt.json @@ -164,6 +164,9 @@ "dashboard.notFound.description": "", "dashboard.notFound.title": "", "dashboard.notification.clear": "", + "dashboard.pagination.nextPage": "", + "dashboard.pagination.pageSize": "", + "dashboard.pagination.previousPage": "", "dashboard.parameters.title": "", "dashboard.pipelineResource.fieldName": "", "dashboard.pipelineResource.secretKey": "", diff --git a/src/nls/messages_zh-Hans.json b/src/nls/messages_zh-Hans.json index eafa2a36b..e8062ff82 100644 --- a/src/nls/messages_zh-Hans.json +++ b/src/nls/messages_zh-Hans.json @@ -164,6 +164,9 @@ "dashboard.notFound.description": "我们找不到您要查找的页面,但以下是一些有用的链接:", "dashboard.notFound.title": "糟糕,找不到页面", "dashboard.notification.clear": "清除通知", + "dashboard.pagination.nextPage": "", + "dashboard.pagination.pageSize": "", + "dashboard.pagination.previousPage": "", "dashboard.parameters.title": "参数", "dashboard.pipelineResource.fieldName": "字段名称", "dashboard.pipelineResource.secretKey": "Secret 密钥", diff --git a/src/nls/messages_zh-Hant.json b/src/nls/messages_zh-Hant.json index 3c5a73c1d..2965ab290 100644 --- a/src/nls/messages_zh-Hant.json +++ b/src/nls/messages_zh-Hant.json @@ -164,6 +164,9 @@ "dashboard.notFound.description": "", "dashboard.notFound.title": "", "dashboard.notification.clear": "", + "dashboard.pagination.nextPage": "", + "dashboard.pagination.pageSize": "", + "dashboard.pagination.previousPage": "", "dashboard.parameters.title": "", "dashboard.pipelineResource.fieldName": "", "dashboard.pipelineResource.secretKey": "", diff --git a/src/scss/_carbon.scss b/src/scss/_carbon.scss index be57eeeb9..0ac9c2f8d 100644 --- a/src/scss/_carbon.scss +++ b/src/scss/_carbon.scss @@ -74,6 +74,7 @@ $feature-flags: ( @import 'carbon-components/scss/components/modal/modal'; @import 'carbon-components/scss/components/notification/inline-notification'; @import 'carbon-components/scss/components/notification/toast-notification'; +@import 'carbon-components/scss/components/pagination/pagination'; @import 'carbon-components/scss/components/tooltip/tooltip'; @import 'carbon-components/scss/components/tabs/tabs'; @import 'carbon-components/scss/components/tag/tag';