Skip to content

Commit c20fc10

Browse files
committed
refactor: make FTable cells clickable (refs SFKUI-7650)
1 parent 0171472 commit c20fc10

File tree

3 files changed

+191
-3
lines changed

3 files changed

+191
-3
lines changed

packages/vue-labs/src/components/FTable/FTable.logic.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,14 +208,15 @@ export function maybeNavigateToCell(e: KeyboardEvent): void {
208208
export function activateCell(
209209
element: HTMLElement & { [tableCellApiSymbol]?: FTableCellApi },
210210
options?: { focus: boolean },
211-
): void {
211+
): HTMLElement {
212212
const api = element[tableCellApiSymbol];
213213
const targetEl = toValue(api?.tabstopEl) ?? element;
214214
targetEl.tabIndex = 0;
215215

216216
if (options?.focus) {
217217
targetEl.focus();
218218
}
219+
return targetEl;
219220
}
220221

221222
/** @internal */

packages/vue-labs/src/components/FTable/FTable.spec.ts

Lines changed: 183 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ref } from "vue";
1+
import { nextTick, ref } from "vue";
22
import { flushPromises, mount } from "@vue/test-utils";
33
import FTable from "./FTable.vue";
44
import { defineTableColumns } from "./table-column";
@@ -770,6 +770,75 @@ describe("7.1 Bulk checkbox in header when multiselect is enabled", () => {
770770
`<i-table-header-selectable-stub selectable="multi" state="false"></i-table-header-selectable-stub>`,
771771
);
772772
});
773+
774+
it("should select all rows when click on checkbox in header", async () => {
775+
expect.assertions(3);
776+
const rows = [{ text: "Foo" }, { text: "Bar" }, { text: "Baz" }];
777+
const columns = defineTableColumns<(typeof rows)[number]>([
778+
{
779+
type: "text",
780+
header: "Header",
781+
key: "text",
782+
},
783+
]);
784+
let selectedRows: typeof rows = [];
785+
const wrapper = mount(FTable<(typeof rows)[number]>, {
786+
attachTo: document.body,
787+
props: {
788+
selectable: "multi",
789+
rows,
790+
columns,
791+
"onUpdate:selectedRows": (value: typeof rows) => {
792+
selectedRows = value;
793+
},
794+
},
795+
});
796+
797+
const selectAllCheckbox = wrapper.get<HTMLInputElement>(
798+
"thead th input[type='checkbox']",
799+
);
800+
await nextTick();
801+
expect(selectedRows).toHaveLength(0);
802+
await selectAllCheckbox.trigger("click");
803+
expect(selectAllCheckbox.element.checked).toBeTruthy();
804+
expect(selectedRows).toHaveLength(3);
805+
});
806+
807+
it("should select all rows when click on th cell for sellect all in header", async () => {
808+
expect.assertions(3);
809+
const rows = [{ text: "Foo" }, { text: "Bar" }, { text: "Baz" }];
810+
const columns = defineTableColumns<(typeof rows)[number]>([
811+
{
812+
type: "text",
813+
header: "Header",
814+
key: "text",
815+
},
816+
]);
817+
let selectedRows: typeof rows = [];
818+
const wrapper = mount(FTable<(typeof rows)[number]>, {
819+
attachTo: document.body,
820+
props: {
821+
selectable: "multi",
822+
rows,
823+
columns,
824+
"onUpdate:selectedRows": (value: typeof rows) => {
825+
selectedRows = value;
826+
},
827+
},
828+
});
829+
830+
const selectThCell = wrapper.get<HTMLTableCellElement>(
831+
"thead th.table-ng__column--selectable",
832+
);
833+
const selectAllCheckbox = wrapper.get<HTMLInputElement>(
834+
"thead th input[type='checkbox']",
835+
);
836+
await nextTick();
837+
expect(selectedRows).toHaveLength(0);
838+
await selectThCell.trigger("click");
839+
expect(selectAllCheckbox.element.checked).toBeTruthy();
840+
expect(selectedRows).toHaveLength(3);
841+
});
773842
});
774843

775844
describe("7.4 Bulk selection in expandable", () => {
@@ -1017,3 +1086,116 @@ describe("select cell", () => {
10171086
expect(cellEditable[1].attributes("aria-expanded")).toBe("false");
10181087
});
10191088
});
1089+
1090+
describe("Clickable cells", () => {
1091+
it("Should be possible to click on cell instead of checkbox", async () => {
1092+
expect.assertions(2);
1093+
const rows = [{ active: false }, { active: false }, { active: false }];
1094+
const columns = defineTableColumns<(typeof rows)[number]>([
1095+
{
1096+
type: "checkbox",
1097+
header: "Header",
1098+
label: () => "Label",
1099+
key: "active",
1100+
editable: true,
1101+
},
1102+
]);
1103+
const wrapper = mount(FTable<(typeof rows)[number]>, {
1104+
attachTo: document.body,
1105+
props: {
1106+
rows,
1107+
columns,
1108+
},
1109+
});
1110+
1111+
const cell = wrapper.get<HTMLTableCellElement>(
1112+
"table tr:nth-child(2) td:first-child",
1113+
);
1114+
1115+
await nextTick();
1116+
await cell.trigger("click");
1117+
1118+
const checkbox = wrapper.get<HTMLInputElement>(
1119+
"table tr:nth-child(2) td:first-child input[type='checkbox']",
1120+
);
1121+
1122+
expect(checkbox.element.checked).toBeTruthy();
1123+
expect(rows).toEqual([
1124+
{ active: false },
1125+
{ active: true },
1126+
{ active: false },
1127+
]);
1128+
});
1129+
1130+
it("Should be possible to click on cell instead of radio button", async () => {
1131+
expect.assertions(1);
1132+
const rows = [{ active: false }, { active: false }, { active: false }];
1133+
const columns = defineTableColumns<(typeof rows)[number]>([
1134+
{
1135+
type: "radio",
1136+
header: "Header",
1137+
label: () => "Label",
1138+
key: "active",
1139+
},
1140+
]);
1141+
const wrapper = mount(FTable<(typeof rows)[number]>, {
1142+
attachTo: document.body,
1143+
props: {
1144+
rows,
1145+
columns,
1146+
},
1147+
});
1148+
1149+
const cell = wrapper.get<HTMLTableCellElement>(
1150+
"table tr:nth-child(2) td:first-child",
1151+
);
1152+
1153+
await nextTick();
1154+
await cell.trigger("click");
1155+
1156+
expect(rows).toEqual([
1157+
{ active: false },
1158+
{ active: true },
1159+
{ active: false },
1160+
]);
1161+
});
1162+
1163+
it("Should be possible to click on cell instead of button", async () => {
1164+
const rows = [
1165+
{ text: "text 1" },
1166+
{ text: "text 2" },
1167+
{ text: "text 3" },
1168+
];
1169+
const onClickSpy = jest.fn();
1170+
const columns = defineTableColumns<(typeof rows)[number]>([
1171+
{
1172+
type: "button",
1173+
header: "Knapp",
1174+
icon: "trashcan",
1175+
size: "shrink",
1176+
text() {
1177+
return `Ta bort`;
1178+
},
1179+
onClick: (row) => {
1180+
onClickSpy(row);
1181+
},
1182+
},
1183+
]);
1184+
const wrapper = mount(FTable<(typeof rows)[number]>, {
1185+
attachTo: document.body,
1186+
props: {
1187+
rows,
1188+
columns,
1189+
},
1190+
});
1191+
1192+
const cell = wrapper.get<HTMLTableCellElement>(
1193+
"table tr:nth-child(2) td:first-child",
1194+
);
1195+
1196+
await nextTick();
1197+
await cell.trigger("click");
1198+
expect(onClickSpy).toHaveBeenCalledTimes(1);
1199+
expect(onClickSpy).toHaveBeenCalledWith({ text: "text 2" });
1200+
});
1201+
});

packages/vue-labs/src/components/FTable/FTable.vue

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,12 @@ function onClick(e: MouseEvent): void {
164164
const cell = (e.target as HTMLElement).closest<HTMLElement>("td, th");
165165
166166
if (cell) {
167-
activateCell(cell, { focus: true });
167+
const targetEl = activateCell(cell, { focus: true });
168+
169+
// If you haven't clicked on target, click on it.
170+
if (e.target instanceof Node && !targetEl.contains(e.target)) {
171+
targetEl.click();
172+
}
168173
}
169174
}
170175

0 commit comments

Comments
 (0)