Skip to content

Commit

Permalink
feat(data-table): sortable columns for userlist
Browse files Browse the repository at this point in the history
  • Loading branch information
derrabauke committed Dec 14, 2021
1 parent 136b9b9 commit 70fa1a9
Show file tree
Hide file tree
Showing 9 changed files with 177 additions and 27 deletions.
4 changes: 2 additions & 2 deletions addon/-private/controllers/pagination.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { inject as service } from "@ember/service";
import { tracked } from "@glimmer/tracking";

export default class PaginationController extends Controller {
queryParams = ["page", "search"];
queryParams = ["page", "search", "sort", "include"];

@service router;

Expand All @@ -13,7 +13,7 @@ export default class PaginationController extends Controller {

@action
updateQueryParam(field, value) {
// We dont want to set an empty stirng as this is still serialized
// We dont want to set an empty string as this is still serialized
if (typeof value === "string" && !value.length) {
value = null;
}
Expand Down
2 changes: 1 addition & 1 deletion addon/components/data-table.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
{{yield
(hash
body=(component "data-table/body" models=this.data.value)
head=(component "data-table/head")
head=(component "data-table/head" sortedBy=this.sort update=this.updateSort)
refresh=(perform this.fetchData)
)
}}
Expand Down
57 changes: 47 additions & 10 deletions addon/components/data-table.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,21 @@ export default class DataTableComponent extends Component {
@tracked numPages;
@tracked internalSearch;
@tracked internalPage = 1;
@tracked internalSort;

// While using 'useTask' we ended up in an infinite loop.
// data = useTask(this, this.fetchData, () => [this.args.filter]);
data = useFunction(this, this.fetchData.perform, () => [
this.args.filter,
this.page,
this.search,
this.sort,
this.page,
]);

get sort() {
return this.internalSort || this.args.sort;
}

get page() {
return this.args.page || this.internalPage;
}
Expand All @@ -31,6 +37,14 @@ export default class DataTableComponent extends Component {
return this.args.search || this.internalSearch;
}

set sort(sort) {
if (this.args.updateSort) {
this.args.updateSort(sort);
} else {
this.internalSort = sort;
}
}

set search(search) {
if (this.args.updateSearch) {
this.args.updateSearch(search);
Expand All @@ -39,6 +53,14 @@ export default class DataTableComponent extends Component {
}
}

set page(page) {
if (this.args.updatePage) {
this.args.updatePage(page);
} else {
this.internalPage = page;
}
}

get isLoading() {
return this.fetchData.isRunning;
}
Expand Down Expand Up @@ -66,7 +88,7 @@ export default class DataTableComponent extends Component {

let options = {
filter: { search: this.search, ...(this.args.filter || {}) },
sort: this.args.sort,
sort: this.sort,
include: this.args.include || "",
};

Expand All @@ -87,28 +109,43 @@ export default class DataTableComponent extends Component {
return data;
}

@action
updateSort(sortLabel) {
if (this.args.updateSort) {
this.args.updateSort(sortLabel);
}

const invers = this.sort[0] === "-";

if (
this.sort === sortLabel ||
(invers && this.sort.slice(1) === sortLabel)
) {
if (invers) {
this.internalSort = undefined;
} else {
this.internalSort = `-${sortLabel}`;
}
} else {
this.internalSort = sortLabel;
}
}

@action
updateSearch(submitEvent) {
// Prevent reload because of form submit
submitEvent.preventDefault();
this.search = submitEvent.target.elements.search.value;
this.fetchData.perform();
}

@action
resetSearch(event) {
event.preventDefault();
this.search = "";
this.fetchData.perform();
}

@action
updatePage(page) {
if (this.args.updatePage) {
this.args.updatePage(page);
} else {
this.internalPage = page;
}
this.fetchData.perform();
this.page = page;
}
}
4 changes: 2 additions & 2 deletions addon/components/data-table/head.hbs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<thead>
<tr>
{{yield}}
{{yield (hash sorthead=(component "data-table/head/sortable-th" update=@update sortedBy=@sortedBy))}}
</tr>
</thead>
</thead>
15 changes: 15 additions & 0 deletions addon/components/data-table/head/sortable-th.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<th>
<span
data-test-sortable-th={{@sort}}
class="sortable-th"
role="button"
{{on "click" (fn @update @sort)}}
>
{{yield}}
{{#if (eq @sortedBy @sort)}}
<UkIcon @icon="chevron-down"/>
{{else if (eq @sortedBy (concat "-" @sort))}}
<UkIcon @icon="chevron-up"/>
{{/if}}
</span>
</th>
25 changes: 14 additions & 11 deletions addon/templates/users/index.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,26 @@
@search={{this.search}}
@sort="last_name"
@updatePage={{fn this.updateQueryParam "page"}}
@updateSearch={{fn this.updateQueryParam "search"}} as |table|
@updateSearch={{fn this.updateQueryParam "search"}}
@updateSort={{fn this.updateQueryParam "sort"}}
@include={{"acls.role"}}
as |table|
>
<table.head>
<table.head as |head|>
{{#unless this.emailAsUsername}}
<th>
<head.sorthead @sort="username">
{{t "emeis.users.headings.username"}}
</th>
{{/unless}}
<th>
</head.sorthead>
{{/unless}}
<head.sorthead @sort="last_name">
{{t "emeis.users.headings.lastName"}}
</th>
<th>
</head.sorthead>
<head.sorthead @sort="first_name">
{{t "emeis.users.headings.firstName"}}
</th>
<th>
</head.sorthead>
<head.sorthead @sort="email">
{{t "emeis.users.headings.email"}}
</th>
</head.sorthead>
</table.head>
<table.body as |body|>
<body.row>
Expand Down
1 change: 1 addition & 0 deletions app/components/data-table/head/sortable-th.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "ember-emeis/components/data-table/head/sortable-th";
9 changes: 8 additions & 1 deletion app/styles/ember-emeis.scss
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,21 @@ $ember-power-select-border-color: $global-border;
border-left-color: transparent;
}
}

.sortable-th {
cursor: pointer;

:hover {
text-decoration: underline;
}
}
/* END GENERAL INPUTS*/

/* TREE VIEW */
@media screen and (min-width: 640px) {
.tree-border-right {
border-right: solid 1px $global-border;
}
}

$tree_node_height: 1.9em;
$tree_indicator_width: 0.6em;
Expand Down
87 changes: 87 additions & 0 deletions tests/integration/components/data-table/head/sortable-th-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { render, click } from "@ember/test-helpers";
import { hbs } from "ember-cli-htmlbars";
import { setupRenderingTest } from "ember-qunit";
import { module, test } from "qunit";

module(
"Integration | Component | data-table/head/sortable-th",
function (hooks) {
setupRenderingTest(hooks);

hooks.beforeEach(function (assert) {
this.set("sort", "last_name");
this.set("update", (sort) => {
this.set("sort", sort);
assert.step("update");
});
});

test("it renders", async function (assert) {
await render(hbs`
<DataTable::Head @sortedBy={{this.sort}} @update={{this.update}} as |head|>
<head.sorthead @sort={{this.sort}}>
test
</head.sorthead>
</DataTable::Head>
`);

assert.dom(this.element).hasText("test");
assert.dom("span[icon=chevron-down]").exists();
});

test("it renders as block", async function (assert) {
await render(hbs`
<DataTable::Head::SortableTh @update={{this.update}}>
template block text
</DataTable::Head::SortableTh>
`);

assert.dom(this.element).hasText("template block text");
});

test("it toggles sort state", async function (assert) {
await render(hbs`
<DataTable::Head @sortedBy={{this.sort}} @update={{this.update}} as |head|>
<head.sorthead @sort={{"last_name"}}>
one
</head.sorthead>
<head.sorthead @sort={{"first_name"}}>
two
</head.sorthead>
</DataTable::Head>
`);

assert.dom("[data-test-sortable-th=last_name]").hasText("one");
assert.dom("[data-test-sortable-th=first_name]").hasText("two");
assert
.dom("[data-test-sortable-th=last_name] span[icon=chevron-down]")
.exists();

this.set("sort", "first_name");

assert
.dom("[data-test-sortable-th=last_name] span[icon=chevron-down]")
.doesNotExist();
assert
.dom("[data-test-sortable-th=first_name] span[icon=chevron-down]")
.exists();

await click("[data-test-sortable-th=last_name]");

assert.verifySteps(["update"]);

assert
.dom("[data-test-sortable-th=first_name] span[icon=chevron-down]")
.doesNotExist();
assert
.dom("[data-test-sortable-th=last_name] span[icon=chevron-down]")
.exists();

this.set("sort", "-last_name");

assert
.dom("[data-test-sortable-th=last_name] span[icon=chevron-up]")
.exists();
});
}
);

0 comments on commit 70fa1a9

Please sign in to comment.