diff --git a/packages/renderer/src/lib/table/Table.spec.ts b/packages/renderer/src/lib/table/Table.spec.ts new file mode 100644 index 000000000000..b9770728d51c --- /dev/null +++ b/packages/renderer/src/lib/table/Table.spec.ts @@ -0,0 +1,87 @@ +/********************************************************************** + * Copyright (C) 2023 Red Hat, Inc. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + ***********************************************************************/ + +import '@testing-library/jest-dom/vitest'; +import { test, expect } from 'vitest'; +import { fireEvent, render, screen } from '@testing-library/svelte'; + +import TestTable from './TestTable.svelte'; + +test('Expect basic table layout', async () => { + // render the component + render(TestTable, {}); + + // 3 people = header + 3 rows + const rows = await screen.findAllByRole('row'); + expect(rows).toBeDefined(); + expect(rows.length).toBe(4); + + // first data row should contain John and his age + expect(rows[1].textContent).toContain('John'); + expect(rows[1].textContent).toContain('57'); + + // second data row should contain Henry and his age + expect(rows[2].textContent).toContain('Henry'); + expect(rows[2].textContent).toContain('27'); + + // last data row should contain Charlie and his age + expect(rows[3].textContent).toContain('Charlie'); + expect(rows[3].textContent).toContain('43'); +}); + +test('Expect sorting by name works', async () => { + render(TestTable, {}); + + const nameCol = screen.getByText('Name'); + expect(nameCol).toBeInTheDocument(); + + let rows = await screen.findAllByRole('row'); + expect(rows).toBeDefined(); + expect(rows.length).toBe(4); + expect(rows[1].textContent).toContain('John'); + expect(rows[2].textContent).toContain('Henry'); + expect(rows[3].textContent).toContain('Charlie'); + + await fireEvent.click(nameCol); + + rows = await screen.findAllByRole('row'); + expect(rows[1].textContent).toContain('Charlie'); + expect(rows[2].textContent).toContain('Henry'); + expect(rows[3].textContent).toContain('John'); +}); + +test('Expect sorting by age works', async () => { + render(TestTable, {}); + + const ageCol = screen.getByText('Age'); + expect(ageCol).toBeInTheDocument(); + + let rows = await screen.findAllByRole('row'); + expect(rows).toBeDefined(); + expect(rows.length).toBe(4); + expect(rows[1].textContent).toContain('John'); + expect(rows[2].textContent).toContain('Henry'); + expect(rows[3].textContent).toContain('Charlie'); + + await fireEvent.click(ageCol); + + rows = await screen.findAllByRole('row'); + expect(rows[1].textContent).toContain('Henry'); + expect(rows[2].textContent).toContain('Charlie'); + expect(rows[3].textContent).toContain('John'); +}); diff --git a/packages/renderer/src/lib/table/Table.svelte b/packages/renderer/src/lib/table/Table.svelte new file mode 100644 index 000000000000..a746a4426b13 --- /dev/null +++ b/packages/renderer/src/lib/table/Table.svelte @@ -0,0 +1,165 @@ + + + + +
+ +
+
+
+ {#if row.selectable} + + {/if} +
+ {#each columns as column} + + +
+ {column.title}{#if column.comparator}{/if} +
+ {/each} +
+ + {#each data as object (object)} +
+
+
+ {#if row.selectable} + + {/if} +
+ {#each columns as column} +
+ {#if column.info.renderer} + + {/if} +
+ {/each} +
+ {/each} +
diff --git a/packages/renderer/src/lib/table/TestColumnAge.svelte b/packages/renderer/src/lib/table/TestColumnAge.svelte new file mode 100644 index 000000000000..dc71cbc7635a --- /dev/null +++ b/packages/renderer/src/lib/table/TestColumnAge.svelte @@ -0,0 +1,5 @@ + + +{object.age} diff --git a/packages/renderer/src/lib/table/TestColumnName.svelte b/packages/renderer/src/lib/table/TestColumnName.svelte new file mode 100644 index 000000000000..4fd618eddc59 --- /dev/null +++ b/packages/renderer/src/lib/table/TestColumnName.svelte @@ -0,0 +1,5 @@ + + +{object.name} diff --git a/packages/renderer/src/lib/table/TestTable.svelte b/packages/renderer/src/lib/table/TestTable.svelte new file mode 100644 index 000000000000..62d5281c3eee --- /dev/null +++ b/packages/renderer/src/lib/table/TestTable.svelte @@ -0,0 +1,40 @@ + + + +
diff --git a/packages/renderer/src/lib/table/table.ts b/packages/renderer/src/lib/table/table.ts new file mode 100644 index 000000000000..78480245081a --- /dev/null +++ b/packages/renderer/src/lib/table/table.ts @@ -0,0 +1,83 @@ +/********************************************************************** + * Copyright (C) 2023 Red Hat, Inc. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + ***********************************************************************/ + +/** + * Options to be used when creating a Column. + */ +export interface ColumnInformation { + /** + * Column alignment, one of 'left', 'center', or 'right'. + * + * Defaults to 'left' alignment. + */ + readonly align?: 'left' | 'center' | 'right'; + + /** + * Column width, typically in pixels or fractional units (fr). + * + * Defaults to '1fr'. + */ + readonly width?: string; + + /** + * Svelte component, renderer for each cell in the column. + * The component must have a property 'object' that has the + * same type as the Column. + */ + readonly renderer?: any; +} + +/** + * A table Column. + */ +export class Column { + comparator: ((object1: Type, object2: Type) => number) | undefined; + + constructor( + readonly title: string, + readonly info: ColumnInformation, + ) {} + + /** + * Set a comparator used to sort the data by the values in this column. + * + * @param comparator + */ + setComparator(comparator: (object1: Type, object2: Type) => number) { + this.comparator = comparator; + } +} + +/** + * A table row. + */ +export class Row { + selectable?: (object: Type) => boolean; + disabledText?: string; + + /** + * Set a function to be used to determine which objects in the data can be selected. + * + * @param selectable a function that returns false when the object should not be selectable + * @param disabledText text to display as a tooltip when selection is disabled + */ + setSelectable(selectable: (object: Type) => boolean, disabledText: string) { + this.selectable = selectable; + this.disabledText = disabledText; + } +}