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

feat(OnyxDataGrid): implement onyx data grid filter action #2597

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 8 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
6 changes: 5 additions & 1 deletion apps/demo-app/src/views/DataGridView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
import { computed, h, ref } from "vue";

const sortingEnabled = ref(false);
const filteringEnabled = ref(false);
const moreActions = ref(false);

const data = [
Expand Down Expand Up @@ -50,10 +51,12 @@ const dummyFeature = createFeature(() => ({

const dataFeatures = computed(() => {
const enabled = [];
if (filteringEnabled.value) {
enabled.push(DataGridFeatures.useFiltering());
}
if (sortingEnabled.value) {
enabled.push(DataGridFeatures.useSorting());
}

if (moreActions.value) {
enabled.push(dummyFeature());
}
Expand All @@ -67,6 +70,7 @@ const dataFeatures = computed(() => {
<OnyxHeadline is="h1">Data-Grid example</OnyxHeadline>
<section class="data-grid-settings">
<OnyxSwitch v-model="sortingEnabled" label="Enable sorting" />
<OnyxSwitch v-model="filteringEnabled" label="Enable filtering" />
<OnyxSwitch v-model="moreActions" label="Enable more actions" />
</section>
<OnyxDataGrid :features="dataFeatures" :data :columns="['name', 'age']" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ export const createMenuButton = createBuilder((options: CreateMenuButtonOptions)
focusRelativeItem("last");
break;
case " ":
if (event.target instanceof HTMLInputElement) break;
event.preventDefault();
(event.target as HTMLElement).click();
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,19 @@ const props = defineProps<{

const slots = defineSlots<{
actions?(): unknown;
/**
* Elements to remove aktive Actions like Filters etc.
*/
removeActions?(): unknown;
}>();
</script>

<template>
<div class="onyx-component onyx-data-grid-header-cell">
<span class="onyx-data-grid-header-cell__label">{{ props.label }}</span>
<div v-if="slots.removeActions" class="onyx-data-grid-header-cell__remove-actions">
<slot name="removeActions"></slot>
</div>
<div v-if="slots.actions" class="onyx-data-grid-header-cell__actions">
<slot name="actions"></slot>
</div>
Expand All @@ -26,8 +33,14 @@ const slots = defineSlots<{
align-items: center;
gap: var(--onyx-spacing-2xs);

&__actions {
&__actions,
&__remove-actions {
display: inline-flex;
}
.onyx-system-button--multiple-actions + .onyx-flyout-menu__list {
.onyx-filter-search {
border-bottom: var(--onyx-1px-in-rem) solid var(--onyx-color-component-border-neutral);
}
}
}
</style>
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "./sorting/types";

export { useFiltering } from "./filtering/filtering";
export { useSorting } from "./sorting/sorting";
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<script setup lang="ts">
import { computed, toRefs } from "vue";
import type { DataGridEntry, OnyxDataGridProps } from "../../../..";
import { DataGridFeatures, OnyxDataGrid } from "../../../..";
import type { FilterOptions } from "./types";

const props = defineProps<{
/**
* columns
*/
columns: OnyxDataGridProps["columns"];
/**
* data
*/
data: OnyxDataGridProps["data"];
/**
* config
*/
filterOptions?: FilterOptions<DataGridEntry>;
}>();
const { columns, data, filterOptions } = toRefs(props);

const withFiltering = computed(() => DataGridFeatures.useFiltering(filterOptions.value));
const features = computed(() => [withFiltering.value]);
</script>

<template>
<OnyxDataGrid :columns :data :features />
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
/* eslint-disable playwright/no-conditional-expect */
/* eslint-disable playwright/no-conditional-in-test */
import { expect, test } from "../../../../playwright/a11y";
import TestCase from "./TestCase.vue";

const getTestData = () => [
{ id: 1, a: "1", b: "b A" },
{ id: 2, a: "1", b: "B" },
{ id: 3, a: "2", b: "A" },
{ id: 4, a: "2", b: "B" },
{ id: 5, a: "3", b: "a" },
{ id: 6, a: "4", b: "ab" },
];

test("set a filter", async ({ mount, page }) => {
// ARRANGE
const data = getTestData();
const component = await mount(<TestCase data={data} columns={["a", "b"]} />);

const getFirstColumn = () => component.locator("tbody tr td:first-of-type");

// ASSERT
let rows = await getFirstColumn().all();
expect(rows).toHaveLength(data.length);

// ACT
await component
.getByRole("columnheader", { name: "a Toggle column actions" })
.getByLabel("Toggle column actions")
.click();
await component.getByLabel("a", { exact: true }).fill("3");
await page.keyboard.press("Enter");

// ASSERT
rows = await getFirstColumn().all();
expect(rows).toHaveLength(1);
});

test("combine two filter", async ({ mount, page }) => {
// ARRANGE
const data = getTestData();
const component = await mount(<TestCase data={data} columns={["a", "b"]} />);

const getFirstColumn = () => component.locator("tbody tr td:first-of-type");

// ASSERT
let rows = await getFirstColumn().all();
expect(rows).toHaveLength(data.length);

// ACT
await component
.getByRole("columnheader", { name: "a Toggle column actions" })
.getByLabel("Toggle column actions")
.click();
await component.getByLabel("a", { exact: true }).fill("1");
await page.keyboard.press("Enter");

// ASSERT
rows = await getFirstColumn().all();
expect(rows).toHaveLength(2);

await component
.getByRole("columnheader", { name: "b Toggle column actions" })
.getByLabel("Toggle column actions")
.click();
await component.getByLabel("b", { exact: true }).fill("A");
await page.keyboard.press("Enter");

rows = await getFirstColumn().all();
expect(rows).toHaveLength(1);
});

test("remove filter", async ({ mount, page }) => {
// ARRANGE
const data = getTestData();
const component = await mount(<TestCase data={data} columns={["a", "b"]} />);

const getFirstColumn = () => component.locator("tbody tr td:first-of-type");

// ASSERT
let rows = await getFirstColumn().all();
expect(rows).toHaveLength(data.length);

// ACT
await component
.getByRole("columnheader", { name: "a Toggle column actions" })
.getByLabel("Toggle column actions")
.click();
await component.getByLabel("a", { exact: true }).fill("3");
await page.keyboard.press("Enter");

// ASSERT
rows = await getFirstColumn().all();
expect(rows).toHaveLength(1);

await component.getByRole("button", { name: "a", exact: true }).click();

rows = await getFirstColumn().all();
expect(rows).toHaveLength(6);
});

test("filterOptions updateMode(onInput)", async ({ mount }) => {
// ARRANGE
const data = getTestData();
const component = await mount(
<TestCase data={data} columns={["a", "b"]} filterOptions={{ updateMode: "onInput" }} />,
);

const getFirstColumn = () => component.locator("tbody tr td:first-of-type");

// ASSERT
let rows = await getFirstColumn().all();
expect(rows).toHaveLength(data.length);

// ACT
await component
.getByRole("columnheader", { name: "a Toggle column actions" })
.getByLabel("Toggle column actions")
.click();
await component.getByLabel("a", { exact: true }).fill("3");

// ASSERT
rows = await getFirstColumn().all();
expect(rows).toHaveLength(1);
});

const filterConfigs = [
{ caseSensitive: true },
{ exactMatch: true },
{ searchFromStart: true },
{ trimWhitespace: true, searchFromStart: true },
];
for (const filterConfig of filterConfigs) {
const configName = Object.keys(filterConfig)[0];

test(`should apply filterConfig: ${configName}`, async ({ mount, page }) => {
// ARRANGE
const data = getTestData();
const component = await mount(
<TestCase data={data} columns={["a", "b"]} filterOptions={{ filterConfig }} />,
);

const getFirstColumn = () => component.locator("tbody tr td:first-of-type");

// ASSERT
let rows = await getFirstColumn().all();
expect(rows).toHaveLength(data.length);

// ACT
await component
.getByRole("columnheader", { name: "b Toggle column actions" })
.getByLabel("Toggle column actions")
.click();
await component.getByLabel("b", { exact: true }).fill("a");
await page.keyboard.press("Enter");

// ASSERT
rows = await getFirstColumn().all();
if (configName === "caseSensitive") {
expect(rows).toHaveLength(2);
Fixed Show fixed Hide fixed
} else if (configName === "exactMatch") {
expect(rows).toHaveLength(2);
Fixed Show fixed Hide fixed
} else if (configName === "searchFromStart") {
expect(rows).toHaveLength(3);
Fixed Show fixed Hide fixed
} else {
await component.getByRole("button", { name: "b", exact: true }).click();
await component
.getByRole("columnheader", { name: "b Toggle column actions" })
.getByLabel("Toggle column actions")
.click();
await component.getByLabel("b", { exact: true }).fill("bA");
await page.keyboard.press("Enter");
rows = await getFirstColumn().all();
expect(rows).toHaveLength(1);
Fixed Show fixed Hide fixed
}
Fixed Show fixed Hide fixed
Fixed Show fixed Hide fixed
Fixed Show fixed Hide fixed
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import type { Meta, StoryObj } from "@storybook/vue3";
import FilteringDataGrid from "../../../examples/DataGrid/FilteringDataGrid.vue";
import FilteringDataGridExampleCode from "../../../examples/DataGrid/FilteringDataGrid.vue?raw";

const meta: Meta<typeof FilteringDataGrid> = {
title: "Data/DataGrid/Features", // new features can add their story under the same title
component: FilteringDataGrid,
tags: ["!autodocs"],
};

export default meta;
type Story = StoryObj<typeof FilteringDataGrid>;

export const Filtering = {
args: {
columns: {
name: {
enabled: true,
filter: "",
filterConfig: {
caseSensitive: true,
},
},
rank: { enabled: false },
birthday: {
enabled: true,
filterConfig: {},
},
},
filterConfig: {
caseSensitive: false,
exactMatch: false,
searchFromStart: false,
trimWhitespace: true,
},
updateMode: "onEnter",
},
parameters: {
docs: {
source: {
// Removes the comment enclosed block to simplify the source example
code: FilteringDataGridExampleCode.replaceAll('from "../../.."', 'from "sit-onyx"').replace(
/\/\/ STORY SETUP START[\s\S]*\/\/ STORY SETUP END/,
"",
),
},
},
},
} satisfies Story;
Loading
Loading