Skip to content
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

fix(table): prevent TableColumn function props trigger recursive reactivity #848

Merged
merged 2 commits into from
Mar 9, 2024
Merged
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
4 changes: 2 additions & 2 deletions packages/docs/components/Table.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,8 @@ title: Table
| sortable | Enable column sortability | boolean | - | <code style='white-space: nowrap; padding: 0;'>false</code> |
| sticky | Whether the column is sticky or not | boolean | - | <code style='white-space: nowrap; padding: 0;'>false</code> |
| subheading | Define a column sub heading | string | - | |
| tdAttrs | Adds native attributes to td | (row: unknown, column: Column) =&gt; object | - | Default function (see source code) |
| thAttrs | Adds native attributes to th | (column: Column) =&gt; object | - | Default function (see source code) |
| tdAttrs | Adds native attributes to td | (row: unknown, column: typeof props) =&gt; object | - | Default function (see source code) |
| thAttrs | Adds native attributes to th | (column: typeof props) =&gt; object | - | Default function (see source code) |
| visible | Define whether the column is visible or not | boolean | - | <code style='white-space: nowrap; padding: 0;'>true</code> |
| width | Column fixed width | number\|string | - | |

Expand Down
20 changes: 13 additions & 7 deletions packages/oruga/src/components/table/Table.vue
Original file line number Diff line number Diff line change
Expand Up @@ -716,6 +716,8 @@
index: column.index,
identifier: column.identifier,
...toValue(column.data),
thAttrsData: {},
tdAttrsData: [],
})),
);

Expand Down Expand Up @@ -793,17 +795,19 @@
});

const visibleColumns = computed(() => {
if (!tableColumns.value) return tableColumns.value;
return tableColumns.value.filter((column) => {
return column.visible || column.visible === undefined;
});
if (!tableColumns.value) return [];
return tableColumns.value.filter(

Check warning on line 799 in packages/oruga/src/components/table/Table.vue

View check run for this annotation

Codecov / codecov/patch

packages/oruga/src/components/table/Table.vue#L799

Added line #L799 was not covered by tests
(column) => column.visible || column.visible === undefined,
);
});

watch([() => visibleRows.value, () => visibleColumns.value], () => {
// process tdAttrs when row or columns got changed
/** process thAttrs & tdAttrs when row or columns got changed */
watch([visibleRows, visibleColumns], () => {

Check warning on line 805 in packages/oruga/src/components/table/Table.vue

View check run for this annotation

Codecov / codecov/patch

packages/oruga/src/components/table/Table.vue#L805

Added line #L805 was not covered by tests
if (visibleColumns.value.length && visibleRows.value.length) {
for (let i = 0; i < visibleColumns.value.length; i++) {
const col = visibleColumns.value[i];
col.thAttrsData =
typeof col.thAttrs === "function" ? col.thAttrs(col) : {};

Check warning on line 810 in packages/oruga/src/components/table/Table.vue

View check run for this annotation

Codecov / codecov/patch

packages/oruga/src/components/table/Table.vue#L809-L810

Added lines #L809 - L810 were not covered by tests
col.tdAttrsData = visibleRows.value.map((data) =>
typeof col.tdAttrs === "function" ? col.tdAttrs(data, col) : {},
);
Expand Down Expand Up @@ -1808,7 +1812,9 @@
tag="span"
:props="{ column, index }" />
</template>
<template v-else>{{ column.subheading }}</template>
<template v-else>
{{ column.subheading }}
</template>
</th>
<th v-if="checkable && checkboxPosition === 'right'" />
</tr>
Expand Down
33 changes: 8 additions & 25 deletions packages/oruga/src/components/table/TableColumn.vue
Original file line number Diff line number Diff line change
@@ -1,17 +1,10 @@
<script setup lang="ts">
import {
computed,
onBeforeMount,
ref,
useSlots,
getCurrentInstance,
type PropType,
} from "vue";
import { toRaw, computed, getCurrentInstance, type PropType } from "vue";

import { useProviderChild } from "@/composables";
import { toCssDimension } from "@/utils/helpers";

import type { TableColumnComponent, Column } from "./types";
import type { TableColumnComponent } from "./types";

/**
* @displayName Table Column
Expand Down Expand Up @@ -72,19 +65,18 @@ const props = defineProps({
headerSelectable: { type: Boolean, default: false },
/** Adds native attributes to th */
thAttrs: {
type: Function as PropType<(column: Column) => object>,
type: Function as PropType<(column: typeof props) => object>,
default: () => ({}),
},
/** Adds native attributes to td */
tdAttrs: {
type: Function as PropType<(row: unknown, column: Column) => object>,
type: Function as PropType<
(row: unknown, column: typeof props) => object
>,
default: () => ({}),
},
});

const thAttrsData = ref({});
const tdAttrsData = ref([]);

const style = computed(() => ({
width: toCssDimension(props.width),
}));
Expand All @@ -94,25 +86,16 @@ const isHeaderUnselectable = computed(
);

const vm = getCurrentInstance();
const slots = useSlots();

const providedData = computed<TableColumnComponent>(() => ({
...props,
...toRaw(props),
$el: vm.proxy,
$slots: slots,
$slots: vm.slots,
style: style.value,
thAttrsData: thAttrsData.value,
tdAttrsData: tdAttrsData.value,
isHeaderUnselectable: isHeaderUnselectable.value,
}));

const { item } = useProviderChild({ data: providedData });

onBeforeMount(() => {
if (typeof props.thAttrs !== "undefined") {
thAttrsData.value = props.thAttrs(props);
}
});
</script>

<template>
Expand Down
51 changes: 10 additions & 41 deletions packages/oruga/src/components/table/types.ts
Original file line number Diff line number Diff line change
@@ -1,55 +1,24 @@
import type {
ComponentProps,
ComponentSlots,
} from "vue-component-type-helpers";
import type { ComponentPublicInstance, StyleValue } from "vue";
import TableColumn from "./TableColumn.vue";
import type { ProviderItem } from "@/composables";
import type { ComponentSlots } from "vue-component-type-helpers";

export type Column = Partial<{
/** Define the column label */
label: string;
/** Define the object property key */
field: string;
/** Define a column sub label */
subheading: string;
/** Enable column sortability */
sortable: boolean;
/** Enable an additional searchbar below the column header */
searchable: boolean;
/** Add addtional meta information for the column */
meta: unknown;
/** Column fixed width */
width: number | string;
/** Define column value as number */
numeric: boolean;
/**
* Position of the column content
* @values left, centered, right
*/
position: string;
/** Define whether the column is visible or not - default true */
visible: boolean;
/** Define a custom sort function */
customSort: (a: Column, b: Column, isAsc: boolean) => number;
/** Define a custom funtion for the filter search */
customSearch: (row: unknown, filter: string) => boolean;
/** Whether the column is sticky or not */
sticky: boolean;
/** Make header selectable */
headerSelectable;
/** Adds native attributes to th :th-attrs="(column)" => ({})" */
thAttrs: (column: Column) => object;
/** Adds native attributes to td :td-attrs="(row, column)" => ({})" */
tdAttrs: (row: unknown, column: Column) => object;
}>;
export type Column = ComponentProps<typeof TableColumn>;

export type TableColumnSlots = ComponentSlots<typeof TableColumn>;

export type TableColumnComponent = Column & {
$el: ComponentPublicInstance;
$slots: TableColumnSlots;
style: StyleValue;
thAttrsData: object;
tdAttrsData: Array<object>;
isHeaderUnselectable: boolean;
};

export type TableColumn = Omit<ProviderItem, "data"> & TableColumnComponent;
export type TableColumn = Omit<ProviderItem, "data"> &
TableColumnComponent & {
thAttrsData: object;
tdAttrsData: Array<object>;
};
Loading