Skip to content

Commit

Permalink
Add custom DataTableSkeleton component to support display of filters
Browse files Browse the repository at this point in the history
If a consumer of the Table component provides a value for the `filters`
prop, they should reasonably be able to expect that those filters will
be displayed regardless of the loading state of the rest of the table.

Update the loading state to use a custom DataTableSkeleton component
based on the Carbon DataTableSkeleton, but with some minor changes:
- only display the description skeleton if the table uses a description
- remove support for zebra striping as this is not used in the Dashboard
- include support for different row sizes directly in the skeleton component
  instead of relying on consumers to provide custom classnames to control this
- if the table has `filters`, render them in the table skeleton toolbar
  - ensure the filters are left-aligned, replacing the toolbar button
    skeleton which is present by default and right-aligned
  • Loading branch information
AlanGreene authored and tekton-robot committed Aug 6, 2024
1 parent 7b8f7bc commit 755db6d
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 13 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
Copyright 2024 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
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import { usePrefix } from '@carbon/react';
import { classNames } from '@tektoncd/dashboard-utils';

export default function DataTableSkeleton({
filters,
headers,
rowCount = 5,
columnCount = 5,
compact = false,
className,
showDescription,
showHeader = true,
showToolbar = true,
size,
...rest
}) {
const prefix = usePrefix();

const dataTableSkeletonClasses = classNames(className, {
[`${prefix}--skeleton`]: true,
[`${prefix}--data-table`]: true,
[`${prefix}--data-table--compact`]: compact,
[`${prefix}--data-table--${size}`]: true
});

const rows = Array(rowCount);
const columnsArray = Array.from({ length: columnCount }, (_, index) => index);
for (let i = 0; i < rowCount; i += 1) {
rows[i] = (
<tr key={i}>
{columnsArray.map(j => (
<td key={j}>
<span />
</td>
))}
</tr>
);
}

return (
<div className={`${prefix}--skeleton ${prefix}--data-table-container`}>
{showHeader ? (
<div className={`${prefix}--data-table-header`}>
<div className={`${prefix}--data-table-header__title`} />
{showDescription && (
<div className={`${prefix}--data-table-header__description`} />
)}
</div>
) : null}
{showToolbar ? (
<section
aria-label="data table toolbar"
className={`${prefix}--table-toolbar`}
>
<div className={`${prefix}--toolbar-content`}>
{filters || (
<span
className={`${prefix}--skeleton ${prefix}--btn ${prefix}--btn--sm`}
/>
)}
</div>
</section>
) : null}
<table className={dataTableSkeletonClasses} {...rest}>
<thead>
<tr>
{columnsArray.map(i => (
<th key={i}>
{headers ? (
<div className={`${prefix}--table-header-label`}>
{headers[i]?.header}
</div>
) : (
<span />
)}
</th>
))}
</tr>
</thead>
<tbody>{rows}</tbody>
</table>
</div>
);
}
15 changes: 15 additions & 0 deletions packages/components/src/components/DataTableSkeleton/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
Copyright 2024 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
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/* istanbul ignore file */

export { default } from './DataTableSkeleton';
17 changes: 4 additions & 13 deletions packages/components/src/components/Table/Table.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,17 @@ import PropTypes from 'prop-types';
import {
Button,
DataTable,
DataTableSkeleton,
TableBatchAction,
TableBatchActions,
TableSelectAll,
TableSelectRow,
TableToolbar,
TableToolbarContent,
usePrefix
TableToolbarContent
} from '@carbon/react';
import { ALL_NAMESPACES, classNames } from '@tektoncd/dashboard-utils';

import DataTableSkeleton from '../DataTableSkeleton';

const {
TableContainer,
Table: CarbonTable,
Expand Down Expand Up @@ -138,7 +138,6 @@ const Table = ({
title = null,
toolbarButtons = defaults.toolbarButtons
}) => {
const carbonPrefix = usePrefix();
const intl = useIntl();
const shouldRenderBatchActions = !!(
dataRows.length && batchActionButtons.length
Expand All @@ -157,20 +156,12 @@ const Table = ({
});

if (loading) {
const tableSizeClassNames = {
xs: `${carbonPrefix}--data-table--xs`,
sm: `${carbonPrefix}--data-table--sm`,
md: `${carbonPrefix}--data-table--md`,
lg: `${carbonPrefix}--data-table--lg`,
xl: `${carbonPrefix}--data-table--xl`
};

return (
<div className={tableClassNames} id={id}>
<DataTableSkeleton
aria-label={title}
className={tableSizeClassNames[size]}
columnCount={dataHeaders.length}
filters={filters}
headers={dataHeaders}
rowCount={skeletonRowCount}
showHeader={!!title}
Expand Down
20 changes: 20 additions & 0 deletions packages/components/src/components/Table/Table.stories.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -152,3 +152,23 @@ export const Filters = {
notes: 'table with filters'
}
};

export const Loading = {
args: {
...ToolbarButton.args,
loading: true
},
parameters: {
notes: 'table loading state'
}
};

export const LoadingWithFilters = {
args: {
...Filters.args,
loading: true
},
parameters: {
notes: 'table loading state with filters'
}
};
4 changes: 4 additions & 0 deletions packages/components/src/components/Table/_Table.scss
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ limitations under the License.
&.tkn--table-with-filters {
.#{$prefix}--data-table-container {
overflow: visible;

&.#{$prefix}--skeleton .#{$prefix}--toolbar-content {
justify-content: flex-start;
}
}

.#{$prefix}--table-toolbar {
Expand Down
1 change: 1 addition & 0 deletions packages/components/src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ limitations under the License.

export { default as ActionableNotification } from './ActionableNotification';
export { default as Actions } from './Actions';
export { default as DataTableSkeleton } from './DataTableSkeleton';
export { default as DetailsHeader } from './DetailsHeader';
export { default as DeleteModal } from './DeleteModal';
export { default as DotSpinner } from './DotSpinner';
Expand Down

0 comments on commit 755db6d

Please sign in to comment.