Skip to content

Add chevron collapsible table #3598

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

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 23 additions & 13 deletions assets/js/common/Table/CollapsibleTableRow.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { Fragment, useState } from 'react';
import classNames from 'classnames';
import { EOS_KEYBOARD_ARROW_DOWN } from 'eos-icons-react';

function CollapsibleTableRow({
columns,
Expand All @@ -11,30 +12,39 @@ function CollapsibleTableRow({
collapsedRowClassName,
}) {
const [rowExpanded, toggleRow] = useState(false);
const collapsibleRowSpan = collapsibleDetailRenderer ? colSpan + 1 : colSpan;

return (
<>
<tr
className={classNames(className, {
'cursor-pointer': !!collapsibleDetailRenderer,
})}
onClick={() => {
if (collapsibleDetailRenderer) {
toggleRow(!rowExpanded);
}
}}
>
<tr className={className}>
{collapsibleDetailRenderer && (
<td
className="pl-4 border-b border-gray-200 bg-white"
onClick={() => toggleRow(!rowExpanded)}
>
<EOS_KEYBOARD_ARROW_DOWN
className={classNames(
'cursor-pointer self-center fill-gray-500',
{
'transform rotate-180': rowExpanded,
}
)}
/>
</td>
)}
{renderCells(columns, item)}
</tr>
{collapsibleDetailRenderer && (
<tr
className={classNames(
collapsedRowClassName,
'overflow-y-hidden transition-all ease-in-out duration-600',
{ visible: rowExpanded, hidden: !rowExpanded }
'overflow-y-hidden transition-all ease-in-out duration-600'
)}
hidden={!rowExpanded}
>
<td colSpan={colSpan}>{collapsibleDetailRenderer(item)}</td>
<td colSpan={collapsibleRowSpan}>
{collapsibleDetailRenderer(item)}
</td>
</tr>
)}
</>
Expand Down
16 changes: 15 additions & 1 deletion assets/js/common/Table/Table.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ function Table({
const {
columns,
collapsibleDetailRenderer = undefined,
headerClassName = '',
rowClassName = '',
collapsedRowClassName = '',
pagination,
Expand Down Expand Up @@ -197,6 +198,14 @@ function Table({
<table className="min-w-full leading-normal table-fixed">
<thead>
<tr>
{collapsibleDetailRenderer && (
<th
key="collapsible"
scope="col"
className={classNames('w-6 bg-gray-100', headerClassName)}
aria-label="collapsible"
/>
)}
{columns.map(
({
title,
Expand All @@ -214,6 +223,7 @@ function Table({
? 'cursor-pointer hover:text-gray-700 '
: ''
}px-5 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider bg-gray-100`,
headerClassName,
columnClassName
)}
onClick={handleClick}
Expand All @@ -231,7 +241,11 @@ function Table({
<tbody>
{renderedData.length === 0 ? (
<EmptyState
colSpan={columns.length}
colSpan={
collapsibleDetailRenderer
? columns.length + 1
: columns.length
}
emptyStateText={emptyStateText}
/>
) : (
Expand Down
13 changes: 13 additions & 0 deletions assets/js/common/Table/Table.stories.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,15 @@ const filteredConfig = {
],
};

const collapsibleConfig = {
...config,
collapsibleDetailRenderer: () => (
<div className="p-4 bg-gray-100">
<p>This is a collapsible row data</p>
</div>
),
};

const data = [
{
user: 'Tony Kekw',
Expand Down Expand Up @@ -201,6 +210,10 @@ export function WithHeader(args) {
);
}

export function WithCollapsibleRow(args) {
return <Table config={collapsibleConfig} data={data} {...args} />;
}

export function Empty() {
return <Table config={config} data={[]} />;
}
26 changes: 26 additions & 0 deletions assets/js/common/Table/Table.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -344,4 +344,30 @@ describe('Table component', () => {
});
});
});

describe('collapsible row', () => {
it('should display the collapsed row when chevron is clicked', async () => {
const data = tableDataFactory.buildList(1);
const content = 'This is a collapsible row data';

render(
<Table
config={{
...tableConfig,
collapsibleDetailRenderer: () => <p>{content}</p>,
}}
data={data}
/>
);

const table = screen.getByRole('table');
const collapsibleCell = table.querySelector(
'tbody > tr:nth-child(1) > td:nth-child(1)'
);
expect(screen.queryByText(content)).not.toBeVisible();

fireEvent.click(collapsibleCell);
expect(screen.queryByText(content)).toBeVisible();
});
});
});
10 changes: 10 additions & 0 deletions assets/js/pages/DatabasesOverview/DatabasesOverview.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ describe('DatabasesOverview component', () => {
/>
);

const table = screen.getByRole('table');
await user.click(
table.querySelector('tbody tr:nth-child(1) td:nth-child(1)')
);

const cleanUpButton = screen.queryByRole('button', { name: 'Clean up' });
await user.click(cleanUpButton);
expect(
Expand Down Expand Up @@ -92,6 +97,11 @@ describe('DatabasesOverview component', () => {
/>
);

const table = screen.getByRole('table');
await user.click(
table.querySelector('tbody tr:nth-child(1) td:nth-child(1)')
);

const cleanUpButton = screen.getByText('Clean up').closest('button');

expect(cleanUpButton).toBeDisabled();
Expand Down
6 changes: 3 additions & 3 deletions assets/js/pages/ExecutionResults/ExecutionResults.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,14 @@ const defaultSavedFilters = [];

const resultsTableConfig = {
usePadding: false,
headerClassName: 'bg-gray-50 border-b h-auto',
rowClassName: 'tn-check-result-row',
columns: [
{
title: 'Id',
key: 'checkID',
fontSize: 'text-base',
className: 'bg-gray-50 border-b w-1/6 h-auto',
className: 'w-1/6',
render: (checkID, { customized, onClick }) => (
<div className="flex whitespace-nowrap text-jungle-green-500 justify-between">
<span
Expand All @@ -58,7 +59,6 @@ const resultsTableConfig = {
title: 'Description',
key: 'description',
fontSize: 'text-base',
className: 'bg-gray-50 border-b h-auto',
render: (description) => (
<ReactMarkdown className="markdown" remarkPlugins={[remarkGfm]}>
{description}
Expand All @@ -69,7 +69,7 @@ const resultsTableConfig = {
title: 'Result',
key: 'result',
fontSize: 'text-base',
className: 'bg-gray-50 border-b w-1/6 h-auto',
className: 'w-1/6',
render: (_, { result }) => <HealthIcon health={result} />,
},
],
Expand Down
30 changes: 20 additions & 10 deletions assets/js/pages/SapSystemsOverviewPage/SapSystemsOverview.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,28 +75,28 @@ describe('SapSystemsOverviews component', () => {
const rows = screen.getByRole('table').querySelectorAll('tbody > tr');
const mainRow = rows[0];

expect(mainRow.querySelector('td:nth-child(2)')).toHaveTextContent(sid);
expect(mainRow.querySelector('td:nth-child(2) > a')).toHaveAttribute(
expect(mainRow.querySelector('td:nth-child(3)')).toHaveTextContent(sid);
expect(mainRow.querySelector('td:nth-child(3) > a')).toHaveAttribute(
'href',
`/sap_systems/${sapSystemID}`
);
expect(mainRow.querySelector('td:nth-child(3)')).toHaveTextContent(
expect(mainRow.querySelector('td:nth-child(4)')).toHaveTextContent(
attachedRdbms
);
expect(mainRow.querySelector('td:nth-child(3) > a')).toHaveAttribute(
expect(mainRow.querySelector('td:nth-child(4) > a')).toHaveAttribute(
'href',
`/databases/${databaseID}`
);
expect(mainRow.querySelector('td:nth-child(4)')).toHaveTextContent(
expect(mainRow.querySelector('td:nth-child(5)')).toHaveTextContent(
tenant
);
expect(mainRow.querySelector('td:nth-child(5)')).toHaveTextContent(
expect(mainRow.querySelector('td:nth-child(6)')).toHaveTextContent(
sapSystemType
);
expect(mainRow.querySelector('td:nth-child(6)')).toHaveTextContent(
expect(mainRow.querySelector('td:nth-child(7)')).toHaveTextContent(
dbAddress
);
expect(mainRow.querySelector('td:nth-child(7)')).toHaveTextContent(
expect(mainRow.querySelector('td:nth-child(8)')).toHaveTextContent(
'ENSA1'
);
});
Expand Down Expand Up @@ -137,7 +137,7 @@ describe('SapSystemsOverviews component', () => {
expectedSapSystemTypes.forEach((expectedType, index) => {
const rowIndex = index * 2;
const sapSystemRow = rows[rowIndex];
expect(sapSystemRow.querySelector('td:nth-child(5)')).toHaveTextContent(
expect(sapSystemRow.querySelector('td:nth-child(6)')).toHaveTextContent(
expectedType
);
});
Expand Down Expand Up @@ -175,7 +175,7 @@ describe('SapSystemsOverviews component', () => {
/>
);
const rows = screen.getByRole('table').querySelectorAll('tbody > tr');
expect(rows[0].querySelector('td:nth-child(5)')).toHaveTextContent(
expect(rows[0].querySelector('td:nth-child(6)')).toHaveTextContent(
expectedSapSystemTypes
);
});
Expand Down Expand Up @@ -332,6 +332,11 @@ describe('SapSystemsOverviews component', () => {
/>
);

const table = screen.getByRole('table');
await user.click(
table.querySelector('tbody tr:nth-child(1) td:nth-child(1)')
);

const cleanUpButton = screen.queryAllByRole('button', {
name: 'Clean up',
})[row];
Expand Down Expand Up @@ -372,6 +377,11 @@ describe('SapSystemsOverviews component', () => {
/>
);

const table = screen.getByRole('table');
await user.click(
table.querySelector('tbody tr:nth-child(1) td:nth-child(1)')
);

const cleanUpButtons = screen.getAllByRole('button', {
name: 'Clean up',
});
Expand Down
4 changes: 2 additions & 2 deletions test/e2e/cypress/e2e/databases_overview.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ context('Databases Overview', () => {
describe('Deregistration', () => {
beforeEach(() => {
databasesOverviewPage.refresh();
databasesOverviewPage.clickHdqDatabaseRow();
databasesOverviewPage.expandHdqDatabaseRow();
});

it(`should not display DB ${databasesOverviewPage.hdqDatabase.sid} after deregistering the primary instance`, () => {
Expand Down Expand Up @@ -43,7 +43,7 @@ context('Databases Overview', () => {

describe('Instance deregistration', () => {
before(() => {
databasesOverviewPage.clickHddDatabaseRow();
databasesOverviewPage.expandHddDatabaseRow();
});

beforeEach(() => {
Expand Down
4 changes: 2 additions & 2 deletions test/e2e/cypress/e2e/sap_systems_overview.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ context('SAP Systems Overview', () => {
beforeEach(() => {
sapSystemsOverviewPage.revertMovedScenario();
sapSystemsOverviewPage.systemNwdIsVisible();
sapSystemsOverviewPage.clickSystemToRemove();
sapSystemsOverviewPage.expandSystemToRemove();
});

it('should move a clustered application instance', () => {
Expand Down Expand Up @@ -153,7 +153,7 @@ context('SAP Systems Overview', () => {
beforeEach(() => {
sapSystemsOverviewPage.restoreNwdHost();
sapSystemsOverviewPage.sapSystemNwdIsDisplayed();
sapSystemsOverviewPage.clickNwdSapSystem();
sapSystemsOverviewPage.expandNwdSapSystem();
});

it('should mark an instance as absent and restore it as present on received respective discovery messages', () => {
Expand Down
8 changes: 6 additions & 2 deletions test/e2e/cypress/pageObject/databases_overview_po.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,10 @@ const hddDatabase = {
// Selectors

const hdqDatabaseCell = `tr:contains("${hdqDatabase.sid}")`;
const hdqDatabaseRowCollapsibleCell = `${hdqDatabaseCell} > td:eq(0)`;

const hddDatabaseCell = `tr:contains("${hddDatabase.sid}")`;
const hddDatabaseRowCollapsibleCell = `${hddDatabaseCell} > td:eq(0)`;

const databaseInstance1 = `a:contains("${hdqDatabase.instances[0].name}")`;
const databaseInstance2 = `a:contains("${hdqDatabase.instances[1].name}")`;
Expand Down Expand Up @@ -117,9 +119,11 @@ export const cleanUpButtonIsNotDisplayed = () => {

// UI Interactions

export const clickHdqDatabaseRow = () => cy.get(hdqDatabaseCell).click();
export const expandHdqDatabaseRow = () =>
cy.get(hdqDatabaseRowCollapsibleCell).click();

export const clickHddDatabaseRow = () => cy.get(hddDatabaseCell).click();
export const expandHddDatabaseRow = () =>
cy.get(hddDatabaseRowCollapsibleCell).click();

export const clickCleanUpButton = () => {
const cleanUpButtonSelector = getCleanUpButtonByIdAndInstanceIndex(
Expand Down
Loading
Loading