Skip to content

Commit

Permalink
frontend: Use new Table component in the ResourceTable
Browse files Browse the repository at this point in the history
Replaces SimpleTable with the new Table component. Out of the box
ResourceTable will enable sorting and filtering on all columns.
To provide that functionality 'getter' in the column definition has
to provide a plaintext value (but will keep working if it returns jsx
for compatibility reasons). e2e and snapshot tests were also updated

Signed-off-by: Oleksandr Dubenko <oldubenko@microsoft.com>
  • Loading branch information
sniok committed May 10, 2024
1 parent 1f8d116 commit af8c5d6
Show file tree
Hide file tree
Showing 90 changed files with 30,207 additions and 9,890 deletions.
2 changes: 1 addition & 1 deletion docs/development/api/modules/plugin_registry.md
Original file line number Diff line number Diff line change
Expand Up @@ -639,7 +639,7 @@ registerResourceTableColumnsProcessor(function ageRemover({ id, columns }) {
if (id === 'headlamp-pods') {
columns.push({
label: 'Init Containers',
getter: (pod: Pod) => {
getValue: (pod: Pod) => {
return pod.spec.initContainers.length;
},
});
Expand Down
8 changes: 5 additions & 3 deletions e2e-tests/tests/headlampPage.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/// <reference types="node" />
import { expect, Page } from '@playwright/test';

export class HeadlampPage {
constructor(private page: Page) {}

async authenticate() {
await this.page.goto('/');

await this.page.waitForSelector('h1:has-text("Authentication")');

// Expects the URL to contain c/main/token
Expand Down Expand Up @@ -104,7 +104,9 @@ export class HeadlampPage {
const rowsDisplayed1 = await this.getRowsDisplayed();

// Click on the next page button
const nextPageButton = this.page.getByTitle('Next page');
const nextPageButton = this.page.getByRole('button', {
name: 'Go to next page',
});
await nextPageButton.click();

// Get value of rows per page after clicking next page button
Expand All @@ -115,7 +117,7 @@ export class HeadlampPage {
}

async getRowsDisplayed() {
const paginationCaption = this.page.locator("p:has-text(' of ')");
const paginationCaption = this.page.locator("span:has-text(' of ')");
const captionText = await paginationCaption.textContent();
return captionText;
}
Expand Down
933 changes: 756 additions & 177 deletions frontend/src/components/App/Home/__snapshots__/index.stories.storyshot

Large diffs are not rendered by default.

26 changes: 13 additions & 13 deletions frontend/src/components/App/Home/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -212,38 +212,38 @@ function HomeComponent(props: HomeComponentProps) {
>
<ResourceTable
filterFunction={filterFunc}
defaultSortingColumn={1}
defaultSortingColumn={{ id: 'name', desc: false }}
columns={[
{
id: 'name',
label: t('Name'),
getter: ({ name }: Cluster) => (
getValue: cluster => cluster.name,
render: ({ name }) => (
<Link routeName="cluster" params={{ cluster: name }}>
{name}
</Link>
),
sort: (c1: Cluster, c2: Cluster) => c1.name.localeCompare(c2.name),
},
{
label: t('Status'),
getter: ({ name }: Cluster) => <ClusterStatus error={errors[name]} />,
getValue: cluster => cluster.name,
render: ({ name }) => <ClusterStatus error={errors[name]} />,
},
{
label: t('Warnings'),
getter: ({ name }: Cluster) => renderWarningsText(name),
sort: true,
getValue: ({ name }) => renderWarningsText(name),
},
{
label: t('glossary|Kubernetes Version'),
getter: ({ name }: Cluster) => versions[name]?.gitVersion || '⋯',
sort: true,
getValue: ({ name }) => versions[name]?.gitVersion || '⋯',
},
{
label: '',
getter: (cluster: Cluster) => (
<Box textAlign="right">
<ContextMenu cluster={cluster} />
</Box>
),
getValue: () => '',
cellProps: {
align: 'right',
},
render: cluster => <ContextMenu cluster={cluster} />,
},
]}
data={Object.values(clusters)}
Expand Down
42 changes: 15 additions & 27 deletions frontend/src/components/cluster/Overview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import React from 'react';
import { useTranslation } from 'react-i18next';
import { useDispatch } from 'react-redux';
import { useLocation } from 'react-router';
import Event, { KubeEvent } from '../../lib/k8s/event';
import Event from '../../lib/k8s/event';
import Node from '../../lib/k8s/node';
import Pod from '../../lib/k8s/pod';
import { useFilterFunc } from '../../lib/util';
Expand Down Expand Up @@ -68,7 +68,7 @@ function EventsSection() {
const queryParams = new URLSearchParams(location.search);
const eventsFilter = queryParams.get('eventsFilter');
const dispatch = useDispatch();
const filterFunc = useFilterFunc(['.jsonData.involvedObject.kind']);
const filterFunc = useFilterFunc<Event>(['.jsonData.involvedObject.kind']);
const [isWarningEventSwitchChecked, setIsWarningEventSwitchChecked] = React.useState(
Boolean(
JSON.parse(
Expand All @@ -79,7 +79,7 @@ function EventsSection() {
);
const [events, eventsError] = Event.useList({ limit: Event.maxLimit });

const warningActionFilterFunc = (event: KubeEvent) => {
const warningActionFilterFunc = (event: Event) => {
if (!filterFunc(event)) {
return false;
}
Expand Down Expand Up @@ -112,7 +112,6 @@ function EventsSection() {
status={event.type === 'Normal' ? '' : 'warning'}
sx={theme => ({
[theme.breakpoints.up('md')]: {
minWidth: '180px',
display: 'unset',
},
})}
Expand Down Expand Up @@ -152,51 +151,40 @@ function EventsSection() {
columns={[
{
label: t('Type'),
getter: event => event.involvedObject.kind,
sort: true,
getValue: event => event.involvedObject.kind,
},
{
label: t('Name'),
getter: event => makeObjectLink(event),
cellProps: {
scope: 'row',
component: 'th',
},
gridTemplate: 1.5,
sort: true,
getValue: event => event.involvedObjectInstance?.getName() ?? event.involvedObject.name,
render: event => makeObjectLink(event),
},
'namespace',
{
label: t('Reason'),
getter: event => (
getValue: event => event.reason,
render: event => (
<LightTooltip title={event.reason} interactive>
{makeStatusLabel(event)}
</LightTooltip>
),
sort: (e1: Event, e2: Event) => e1.reason.localeCompare(e2.reason),
},
{
label: t('Message'),
getter: event => (
getValue: event => event.message ?? '',
render: event => (
<ShowHideLabel labelId={event.metadata?.uid || ''}>{event.message || ''}</ShowHideLabel>
),
sort: true,
gridTemplate: 1.5,
},
{
id: 'last-seen',
label: t('Last Seen'),
getter: event => <DateLabel date={event.lastOccurrence} format="mini" />,
cellProps: { style: { textAlign: 'right' } },
gridTemplate: 'minmax(150px, 0.5fr)',
sort: (e1: Event, e2: Event) => {
const date1 = e1.lastTimestamp || e1.metadata.creationTimestamp;
const date2 = e2.lastTimestamp || e2.metadata.creationTimestamp;
return new Date(date2).getTime() - new Date(date1).getTime();
},
getValue: event => -new Date(event.lastOccurrence).getTime(),
render: event => <DateLabel date={event.lastOccurrence} format="mini" />,
cellProps: { align: 'right' },
},
]}
filterFunction={warningActionFilterFunc}
defaultSortingColumn={6}
defaultSortingColumn={{ id: 'last-seen', desc: false }}
id="headlamp-cluster.overview.events"
/>
);
Expand Down
Loading

0 comments on commit af8c5d6

Please sign in to comment.