Skip to content

Commit

Permalink
[studio] create common table component #1359 (#1388)
Browse files Browse the repository at this point in the history
* studio - create common table component

* studio - common table component

* studio - fix filter

* studio - common table component

* studio - unit tests

* studio - unit tests

* studio - unit tests
  • Loading branch information
janavlachova authored Dec 17, 2024
1 parent c645d5a commit c179f7f
Show file tree
Hide file tree
Showing 30 changed files with 802 additions and 117 deletions.
2 changes: 1 addition & 1 deletion agdb_studio/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
<title>Agdb Studio</title>
</head>
<body>
<div id="app"></div>
Expand Down
32 changes: 32 additions & 0 deletions agdb_studio/src/components/base/table/AgdbTable.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { mount } from "@vue/test-utils";
import AgdbTable from "./AgdbTable.vue";
import { addTable, clearTables } from "@/composables/table/tableConfig";
import { setTableData } from "@/composables/table/tableData";
import { TABLE_NAME, tableConfig, tableData } from "@/tests/tableMocks";

describe("AgdbTable", () => {
beforeEach(() => {
clearTables();
});
it("should render for correct data", () => {
addTable({ name: TABLE_NAME, columns: tableConfig, uniqueKey: "name" });
setTableData(TABLE_NAME, tableData);

const wrapper = mount(AgdbTable, {
props: {
name: TABLE_NAME,
},
});
expect(wrapper.findAll(".agdb-table-row").length).toBe(
tableData.length,
);
});
it("should not render rows when table doesn't exist", () => {
const wrapper = mount(AgdbTable, {
props: {
name: TABLE_NAME,
},
});
expect(wrapper.findAll(".agdb-table-row").length).toBe(0);
});
});
50 changes: 50 additions & 0 deletions agdb_studio/src/components/base/table/AgdbTable.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<script lang="ts" setup>
import { computed, defineProps } from "vue";
import { getRows } from "@/composables/table/tableData";
import TableRow from "@/components/base/table/TableRow.vue";
import TableHeader from "./TableHeader.vue";
import { getTableColumns } from "@/composables/table/tableConfig";
import { type TRow } from "@/composables/table/types";
const props = defineProps({
name: {
type: String,
required: true,
},
});
const rows = computed(() => {
return getRows(props.name);
});
const columns = computed(() => {
return getTableColumns<TRow>(props.name);
});
</script>

<template>
<div class="agdb-table">
<TableHeader :tableKey="name" />
<template v-for="row in rows" :key="row[0]">
<TableRow :row="row[1]" :columns="columns" />
</template>
</div>
</template>

<style lang="less" scoped>
.agdb-table {
display: grid;
padding: 1rem;
border: 1px solid var(--color-border);
border-radius: 0.5rem;
margin: 0 auto;
max-width: 100%;
overflow: auto;
}
.agdb-table ::v-deep(.columns) {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: 1rem;
padding: 0.5rem;
border-bottom: 1px solid var(--color-border);
}
</style>
13 changes: 13 additions & 0 deletions agdb_studio/src/components/base/table/TableHeader.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { shallowMount } from "@vue/test-utils";
import TableHeader from "./TableHeader.vue";

describe("TableHeader", () => {
it("should render", () => {
const wrapper = shallowMount(TableHeader, {
props: {
tableKey: "table",
},
});
expect(wrapper.exists()).toBe(true);
});
});
35 changes: 35 additions & 0 deletions agdb_studio/src/components/base/table/TableHeader.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<script lang="ts" setup>
import { getTableColumnsArray } from "@/composables/table/tableConfig";
import { defineProps, computed } from "vue";
const props = defineProps({
tableKey: {
type: String,
required: true,
},
});
const columns = computed(() => {
return getTableColumnsArray(props.tableKey);
});
</script>

<template>
<div class="agdb-table-header columns">
<div v-for="column in columns" :key="column.key">
{{ column.title }}
</div>
</div>
</template>

<style lang="less" scoped>
.agdb-table-header {
&.columns {
border: 1px solid var(--color-border);
}
div {
font-weight: bold;
font-size: 1.05rem;
}
}
</style>
21 changes: 21 additions & 0 deletions agdb_studio/src/components/base/table/TableRow.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { shallowMount } from "@vue/test-utils";
import TableRow from "./TableRow.vue";
import { columnsMap } from "@/tests/tableMocks";

describe("TableRow", () => {
it("should render", () => {
const wrapper = shallowMount(TableRow, {
props: {
columns: columnsMap,
row: {
role: "admin",
name: "admin/app3",
db_type: "file",
size: 50,
backup: 0,
},
},
});
expect(wrapper.text()).toContain("admin");
});
});
36 changes: 36 additions & 0 deletions agdb_studio/src/components/base/table/TableRow.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<script lang="ts" setup>
import { defineProps, computed, type PropType } from "vue";
import { type Column, type TRow } from "@/composables/table/types";
const props = defineProps({
row: {
type: Object as PropType<TRow>,
required: true,
},
columns: {
type: Object as PropType<Map<string, Column<TRow>>>,
required: true,
},
});
const cellKeys = computed(() => {
return Object.keys(props.row);
});
const getFromattedValue = (key: string) => {
const column = props.columns.get(key);
const value = props.row[key];
if (column?.valueFormatter) {
return column.valueFormatter(value);
}
return value;
};
</script>

<template>
<div class="agdb-table-row columns">
<div v-for="cellKey in cellKeys" :key="cellKey">
<p>{{ getFromattedValue(cellKey) }}</p>
</div>
</div>
</template>

<style lang="less" scoped></style>
2 changes: 1 addition & 1 deletion agdb_studio/src/components/db/DbAddForm.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const { addDatabase } = vi.hoisted(() => {
};
});

vi.mock("@/composables/DbStore", () => {
vi.mock("@/composables/stores/DbStore", () => {
return {
useDbList: () => {
return {
Expand Down
2 changes: 1 addition & 1 deletion agdb_studio/src/components/db/DbAddForm.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script lang="ts" setup>
import { ref } from "vue";
import { useDbList } from "@/composables/DbStore";
import { useDbList } from "@/composables/stores/DbStore";
import type { DbType } from "agdb_api/dist/openapi";
const name = ref("");
Expand Down
12 changes: 5 additions & 7 deletions agdb_studio/src/components/db/DbList.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import DbList from "./DbList.vue";
import { mount } from "@vue/test-utils";
import { mount, shallowMount } from "@vue/test-utils";

const { databases, fetchDatabases } = vi.hoisted(() => {
return {
Expand All @@ -24,7 +24,7 @@ const { databases, fetchDatabases } = vi.hoisted(() => {
};
});

vi.mock("@/composables/DbStore", () => {
vi.mock("@/composables/stores/DbStore", () => {
return {
useDbList: () => {
return {
Expand All @@ -40,11 +40,9 @@ describe("DbList", () => {
vi.clearAllMocks();
});
it("should render databases when the page view loads", () => {
const wrapper = mount(DbList);
const rows = wrapper.findAll("li");
expect(rows).toHaveLength(2);
expect(rows[0].text()).toContain("test_db");
expect(rows[1].text()).toContain("test_db2");
const wrapper = shallowMount(DbList);
expect(wrapper.html()).toContain("db-add-form-stub");
expect(wrapper.html()).toContain("db-table-stub");
});
it("should fetch databases when the page view loads", () => {
expect(fetchDatabases).not.toHaveBeenCalled();
Expand Down
13 changes: 4 additions & 9 deletions agdb_studio/src/components/db/DbList.vue
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<script lang="ts" setup>
import { useDbList } from "@/composables/DbStore";
import { useDbList } from "@/composables/stores/DbStore";
import { onMounted } from "vue";
import DbListRow from "./DbListRow.vue";
import DbAddForm from "./DbAddForm.vue";
import DbTable from "./DbTable.vue";
const { fetchDatabases, databases } = useDbList();
const { fetchDatabases } = useDbList();
onMounted(async () => {
await fetchDatabases();
});
Expand All @@ -14,12 +14,7 @@ onMounted(async () => {
<div class="db-list">
<button class="button" @click="fetchDatabases">Refresh</button>
<DbAddForm />
<ul v-if="databases.length">
<li v-for="db in databases" :key="db.name">
<DbListRow :db="db" />
</li>
</ul>
<p v-else>No databases found</p>
<DbTable />
</div>
</template>

Expand Down
22 changes: 0 additions & 22 deletions agdb_studio/src/components/db/DbListRow.spec.ts

This file was deleted.

31 changes: 0 additions & 31 deletions agdb_studio/src/components/db/DbListRow.vue

This file was deleted.

13 changes: 13 additions & 0 deletions agdb_studio/src/components/db/DbTable.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { shallowMount } from "@vue/test-utils";
import DbTable from "./DbTable.vue";

describe("DbTable", () => {
it("should render", () => {
const wrapper = shallowMount(DbTable, {
props: {
tableKey: "table",
},
});
expect(wrapper.exists()).toBe(true);
});
});
52 changes: 52 additions & 0 deletions agdb_studio/src/components/db/DbTable.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<script lang="ts" setup>
import AgdbTable from "../base/table/AgdbTable.vue";
import { useDbList } from "@/composables/stores/DbStore";
import { addTable } from "@/composables/table/tableConfig";
import { setTableData } from "@/composables/table/tableData";
import { watchEffect } from "vue";
import { dateFormatter } from "@/composables/table/utils";
const { databases } = useDbList();
const TABLE_KEY = "databases";
addTable({
name: TABLE_KEY,
columns: [
{ key: "role", title: "Role" },
{ key: "name", title: "Owner/Name" },
{ key: "db_type", title: "Type" },
{ key: "size", title: "Size" },
{
key: "backup",
title: "Backup",
valueFormatter: dateFormatter,
},
],
uniqueKey: "name",
});
watchEffect(() => {
setTableData(TABLE_KEY, databases.value);
});
</script>

<template>
<div class="table-wrap">
<div v-if="databases.length" class="db-table">
<AgdbTable :name="TABLE_KEY" />
</div>

<p v-else>No databases found</p>
</div>
</template>

<style lang="less" scoped>
.table-wrap {
overflow: auto;
}
.db-table {
width: 1000px;
margin: 0 auto;
}
</style>
Loading

0 comments on commit c179f7f

Please sign in to comment.