-
Notifications
You must be signed in to change notification settings - Fork 50
feat: add pagination to Dashboard page #3403
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
5af6edd
feat: created new dashboard paginated files
blegesse-w 7dbe634
feat: adding function to render dashboards
blegesse-w ef519dc
chore: clean up
blegesse-w 449ed97
feat: pagination is working
blegesse-w 0dc2865
chore: moved filter one component higher
blegesse-w bec8654
chore: clean up
blegesse-w 5d311b6
feat: adding a spinner to dashboardsIndexPaginated
blegesse-w 8daad74
feat: usibility fixes with url
blegesse-w 529d62d
feat: feature flag added to ui
blegesse-w d359e71
chore: clean up
blegesse-w 81556ab
fix: prettier
blegesse-w f1c83ea
chore: clean up
blegesse-w bea23b1
fix: prettier
blegesse-w b72b3fc
fix: fixing pagination controls position
blegesse-w 5e23f6a
fix: prettier
blegesse-w File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
96 changes: 96 additions & 0 deletions
96
src/dashboards/components/dashboard_index/DashboardCardsPaginated.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| // Libraries | ||
| import React, {PureComponent} from 'react' | ||
| import {connect} from 'react-redux' | ||
|
|
||
| // Components | ||
| import DashboardCard from 'src/dashboards/components/dashboard_index/DashboardCard' | ||
| import AssetLimitAlert from 'src/cloud/components/AssetLimitAlert' | ||
|
|
||
| // Types | ||
| import {AppState, Dashboard} from 'src/types' | ||
| import {LimitStatus} from 'src/cloud/actions/limits' | ||
|
|
||
| // Utils | ||
| import {extractDashboardLimits} from 'src/cloud/utils/limits' | ||
| import {isFlagEnabled} from 'src/shared/utils/featureFlag' | ||
| import {CLOUD} from 'src/shared/constants' | ||
|
|
||
| let getPinnedItems | ||
| if (CLOUD) { | ||
| getPinnedItems = require('src/shared/contexts/pinneditems').getPinnedItems | ||
| } | ||
|
|
||
| interface StateProps { | ||
| limitStatus: LimitStatus['status'] | ||
| } | ||
|
|
||
| interface OwnProps { | ||
| dashboards: Dashboard[] | ||
| onFilterChange: (searchTerm: string) => void | ||
| } | ||
|
|
||
| class DashboardCards extends PureComponent<OwnProps & StateProps> { | ||
| private _isMounted = true | ||
|
|
||
| state = { | ||
| pinnedItems: [], | ||
| } | ||
|
|
||
| public componentDidMount() { | ||
| if (isFlagEnabled('pinnedItems') && CLOUD) { | ||
| getPinnedItems() | ||
| .then(res => { | ||
| if (this._isMounted) { | ||
| this.setState(prev => ({...prev, pinnedItems: res})) | ||
| } | ||
| }) | ||
| .catch(err => { | ||
| console.error(err) | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| public componentWillUnmount() { | ||
| this._isMounted = false | ||
| } | ||
|
|
||
| public render() { | ||
| const {dashboards, onFilterChange} = this.props | ||
|
|
||
| const {pinnedItems} = this.state | ||
|
|
||
| return ( | ||
| <div> | ||
| <div className="dashboards-card-grid"> | ||
| {dashboards.map(({id, name, description, labels, meta}) => ( | ||
| <DashboardCard | ||
| key={id} | ||
| id={id} | ||
| name={name} | ||
| labels={labels} | ||
| updatedAt={meta.updatedAt} | ||
| description={description} | ||
| onFilterChange={onFilterChange} | ||
| isPinned={ | ||
| !!pinnedItems.find(item => item?.metadata.dashboardID === id) | ||
| } | ||
| /> | ||
| ))} | ||
| <AssetLimitAlert | ||
| className="dashboards--asset-alert" | ||
| resourceName="dashboards" | ||
| limitStatus={this.props.limitStatus} | ||
| /> | ||
| </div> | ||
| </div> | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| const mstp = (state: AppState) => { | ||
| return { | ||
| limitStatus: extractDashboardLimits(state), | ||
| } | ||
| } | ||
|
|
||
| export default connect(mstp)(DashboardCards) |
208 changes: 208 additions & 0 deletions
208
src/dashboards/components/dashboard_index/DashboardsIndexContentsPaginated.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,208 @@ | ||
| // Libraries | ||
| import React, {Component, createRef, RefObject} from 'react' | ||
| import {connect, ConnectedProps} from 'react-redux' | ||
| import {withRouter, RouteComponentProps} from 'react-router-dom' | ||
| import memoizeOne from 'memoize-one' | ||
|
|
||
| // Components | ||
| import DashboardsTableEmpty from 'src/dashboards/components/dashboard_index/DashboardsTableEmpty' | ||
| import DashboardCardsPaginated from 'src/dashboards/components/dashboard_index/DashboardCardsPaginated' | ||
| import {ResourceList} from '@influxdata/clockface' | ||
|
|
||
| // Actions | ||
| import {retainRangesDashTimeV1 as retainRangesDashTimeV1Action} from 'src/dashboards/actions/ranges' | ||
| import {checkDashboardLimits as checkDashboardLimitsAction} from 'src/cloud/actions/limits' | ||
| import {createDashboard} from 'src/dashboards/actions/thunks' | ||
|
|
||
| // Decorators | ||
| import {ErrorHandling} from 'src/shared/decorators/errors' | ||
|
|
||
| // Types | ||
| import {Dashboard, AppState, Pageable, RemoteDataState} from 'src/types' | ||
| import {Sort} from '@influxdata/clockface' | ||
| import {SortTypes, getSortedResources} from 'src/shared/utils/sort' | ||
| import {DashboardSortKey} from 'src/shared/components/resource_sort_dropdown/generateSortItems' | ||
|
|
||
| // Utils | ||
| import {PaginationNav} from '@influxdata/clockface' | ||
|
|
||
| interface OwnProps { | ||
| onFilterChange: (searchTerm: string) => void | ||
| searchTerm: string | ||
| filterComponent?: JSX.Element | ||
| sortDirection: Sort | ||
| sortType: SortTypes | ||
| sortKey: DashboardSortKey | ||
| pageHeight: number | ||
| pageWidth: number | ||
| dashboards: Dashboard[] | ||
| totalDashboards: number | ||
| } | ||
|
|
||
| type ReduxProps = ConnectedProps<typeof connector> | ||
| type Props = ReduxProps & OwnProps & RouteComponentProps<{orgID: string}> | ||
|
|
||
| const DEFAULT_PAGINATION_CONTROL_HEIGHT = 62 | ||
|
|
||
| @ErrorHandling | ||
| class DashboardsIndexContents extends Component<Props> implements Pageable { | ||
| private paginationRef: RefObject<HTMLDivElement> | ||
| public currentPage: number = 1 | ||
| public rowsPerPage: number = 12 | ||
| public totalPages: number | ||
|
|
||
| private memGetSortedResources = memoizeOne<typeof getSortedResources>( | ||
| getSortedResources | ||
| ) | ||
| constructor(props) { | ||
| super(props) | ||
| this.paginationRef = createRef<HTMLDivElement>() | ||
| } | ||
|
|
||
| public componentDidMount() { | ||
| const {dashboards} = this.props | ||
|
|
||
| const dashboardIDs = dashboards.map(d => d.id) | ||
| this.props.retainRangesDashTimeV1(dashboardIDs) | ||
| this.props.checkDashboardLimits() | ||
|
|
||
| const params = new URLSearchParams(window.location.search) | ||
| const urlPageNumber = parseInt(params.get('page'), 10) | ||
|
|
||
| const passedInPageIsValid = | ||
| urlPageNumber && urlPageNumber <= this.totalPages && urlPageNumber > 0 | ||
| if (passedInPageIsValid) { | ||
| this.currentPage = urlPageNumber | ||
| } | ||
| } | ||
|
|
||
| public componentDidUpdate() { | ||
| // if the user filters the list while on a page that is | ||
| // outside the new filtered list put them on the last page of the new list | ||
| if (this.currentPage > this.totalPages) { | ||
| this.paginate(this.totalPages) | ||
| } | ||
| } | ||
|
|
||
| public paginate = page => { | ||
| this.currentPage = page | ||
| const url = new URL(location.href) | ||
| url.searchParams.set('page', page) | ||
| history.replaceState(null, '', url.toString()) | ||
| this.forceUpdate() | ||
| } | ||
|
|
||
| public renderDashboardCards() { | ||
| const sortedDashboards = this.memGetSortedResources( | ||
| this.props.dashboards, | ||
| this.props.sortKey, | ||
| this.props.sortDirection, | ||
| this.props.sortType | ||
| ) | ||
|
|
||
| const startIndex = this.rowsPerPage * Math.max(this.currentPage - 1, 0) | ||
| const endIndex = Math.min( | ||
| startIndex + this.rowsPerPage, | ||
| this.props.totalDashboards | ||
| ) | ||
| const rows = [] | ||
| for (let i = startIndex; i < endIndex; i++) { | ||
| const dashboard = sortedDashboards[i] | ||
| if (dashboard) { | ||
| rows.push(dashboard) | ||
| } | ||
| } | ||
| return rows | ||
| } | ||
|
|
||
| public render() { | ||
| const { | ||
| searchTerm, | ||
| status, | ||
| dashboards, | ||
| onFilterChange, | ||
| onCreateDashboard, | ||
| } = this.props | ||
|
|
||
| const heightWithPagination = | ||
| this.paginationRef?.current?.clientHeight || | ||
| DEFAULT_PAGINATION_CONTROL_HEIGHT | ||
| const height = this.props.pageHeight - heightWithPagination | ||
|
|
||
| this.totalPages = Math.max( | ||
| Math.ceil(dashboards.length / this.rowsPerPage), | ||
| 1 | ||
| ) | ||
|
|
||
| if (status === RemoteDataState.Done && !dashboards.length) { | ||
| return ( | ||
| <DashboardsTableEmpty | ||
| searchTerm={searchTerm} | ||
| onCreateDashboard={onCreateDashboard} | ||
| summonImportFromTemplateOverlay={this.summonImportFromTemplateOverlay} | ||
| summonImportOverlay={this.summonImportOverlay} | ||
| /> | ||
| ) | ||
| } | ||
|
|
||
| return ( | ||
| <> | ||
| <ResourceList style={{width: this.props.pageWidth}}> | ||
| <ResourceList.Body | ||
| style={{maxHeight: height, minHeight: height, overflow: 'auto'}} | ||
| emptyState={null} | ||
| > | ||
| <DashboardCardsPaginated | ||
| dashboards={this.renderDashboardCards()} | ||
| onFilterChange={onFilterChange} | ||
| /> | ||
| </ResourceList.Body> | ||
| </ResourceList> | ||
| <PaginationNav.PaginationNav | ||
| ref={this.paginationRef} | ||
| style={{width: this.props.pageWidth}} | ||
| totalPages={this.totalPages} | ||
| currentPage={this.currentPage} | ||
| pageRangeOffset={1} | ||
| onChange={this.paginate} | ||
| /> | ||
| </> | ||
| ) | ||
| } | ||
|
|
||
| private summonImportOverlay = (): void => { | ||
| const { | ||
| history, | ||
| match: { | ||
| params: {orgID}, | ||
| }, | ||
| } = this.props | ||
| history.push(`/orgs/${orgID}/dashboards-list/import`) | ||
| } | ||
|
|
||
| private summonImportFromTemplateOverlay = (): void => { | ||
| const { | ||
| history, | ||
| match: { | ||
| params: {orgID}, | ||
| }, | ||
| } = this.props | ||
| history.push(`/orgs/${orgID}/dashboards-list/import/template`) | ||
| } | ||
| } | ||
|
|
||
| const mstp = (state: AppState) => { | ||
| return { | ||
| status: state.resources.dashboards.status, | ||
| } | ||
| } | ||
|
|
||
| const mdtp = { | ||
| retainRangesDashTimeV1: retainRangesDashTimeV1Action, | ||
| checkDashboardLimits: checkDashboardLimitsAction, | ||
| onCreateDashboard: createDashboard as any, | ||
| } | ||
|
|
||
| const connector = connect(mstp, mdtp) | ||
|
|
||
| export default connector(withRouter(DashboardsIndexContents)) | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cool, this
overflow: 'auto'avoids the bug I keep seeming to stumble over.