Skip to content

Commit

Permalink
feat(ebay-table): support column sorting (#2291)
Browse files Browse the repository at this point in the history
  • Loading branch information
bill-min authored Oct 30, 2024
1 parent 047647b commit a23c4ec
Show file tree
Hide file tree
Showing 9 changed files with 1,269 additions and 16 deletions.
5 changes: 5 additions & 0 deletions .changeset/polite-cats-rest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@ebay/ebayui-core": minor
---

feat(ebay-table): suppport column sorting
58 changes: 50 additions & 8 deletions src/components/ebay-table/component.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
import { AttrTriState } from "marko/tags-html";
import { AttrString, AttrTriState } from "marko/tags-html";
import { WithNormalizedProps } from "../../global";
import { CheckboxEvent } from "../ebay-checkbox/component-browser";

type TableColRowName = string | number;
export type TableSort = "asc" | "desc" | "none";
export interface TableHeader extends Omit<Marko.Input<"th">, `on${string}`> {
columnType?: "normal" | "numeric" | "row-header" | "layout" | "icon-action";
name?: string;
sort?: TableSort | boolean;
href?: AttrString;
renderBody: Marko.Body;
}
export interface TableCell
extends Omit<Marko.Input<"td"> & Marko.Input<"th">, `on${string}`> {
renderBody: Marko.Body;
}
export interface TableRow extends Omit<Marko.Input<"tr">, `on${string}`> {
name?: TableColRowName;
name?: string;
selected?: boolean;
cell: Marko.AttrTag<TableCell>;
}
Expand All @@ -25,32 +28,36 @@ export interface TableInput extends Omit<Marko.Input<"div">, `on${string}`> {
"a11y-select-all-text"?: string;
"a11y-select-row-text"?: string;
"on-select"?: (event: {
selected: Record<TableColRowName, boolean>;
selected: Record<string, boolean>;
allSelected?: AttrTriState;
}) => void;
"on-sort"?: (event: { sorted: Record<string, TableSort> }) => void;
}
export interface Input extends WithNormalizedProps<TableInput> {}

interface State {
selected: Record<TableColRowName, boolean>;
selected: Record<string, boolean>;
sorted: Record<string, TableSort | undefined>;
allSelected: AttrTriState;
}

export default class EbayTable extends Marko.Component<Input, State> {
onCreate() {
this.state = {
selected: {},
sorted: {},
allSelected: "false",
};
}

onInput(input: Input) {
this.state.selected = this.getSelectedRowStateFromInput(input);
this.state.allSelected = this.getAllSelectedState(input);
this.state.sorted = this.getSortedColStateFromInput(input);
}

getSelectedRowStateFromInput(input: Input) {
const selected: Record<TableColRowName, boolean> = {};
const selected: Record<string, boolean> = {};
if (input.row) {
for (const [i, row] of Object.entries([...input.row])) {
const name = row.name || i;
Expand All @@ -60,6 +67,19 @@ export default class EbayTable extends Marko.Component<Input, State> {
return selected;
}

getSortedColStateFromInput(input: Input) {
const sorted: Record<string, TableSort> = {};
for (const [i, header] of Object.entries([...input.header])) {
const name = header.name || i;
if (header.sort === true) {
sorted[name] = "none";
} else if (header.sort) {
sorted[name] = header.sort;
}
}
return sorted;
}

getAllSelectedState(input: Input): AttrTriState {
if (input.allSelected) {
return input.allSelected;
Expand Down Expand Up @@ -88,7 +108,7 @@ export default class EbayTable extends Marko.Component<Input, State> {
acc[name || i] = allSelected !== "true";
return acc;
},
{} as Record<TableColRowName, boolean>,
{} as Record<string, boolean>,
);
this.state.allSelected = allSelected !== "true" ? "true" : "false";
this.emit("select", {
Expand All @@ -97,12 +117,34 @@ export default class EbayTable extends Marko.Component<Input, State> {
});
}

rowSelect(name: TableColRowName, { checked }: CheckboxEvent) {
rowSelect(name: string, { checked }: CheckboxEvent) {
this.state.selected[name] = checked;
this.setStateDirty("selected");
this.state.allSelected = this.getAllSelectedState(this.input);
this.emit("select", {
selected: this.state.selected,
});
}

sortColumn(name: string) {
const sortTo: Record<TableSort, TableSort> = {
asc: "desc",
desc: "none",
none: "asc",
};
const currSort = this.state.sorted[name];
if (currSort) {
const nextSort = sortTo[currSort];
this.state.sorted = Object.keys(this.state.sorted).reduce(
(acc, key) => {
acc[key] = key === name ? nextSort : "none";
return acc;
},
{} as Record<string, TableSort>,
);
this.emit("sort", {
sorted: { [name]: nextSort },
});
}
}
}
99 changes: 99 additions & 0 deletions src/components/ebay-table/examples/sort-client-side.marko
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import data from "./data.json";
import { type TableSort } from "../component";
class {
declare state: {
sorted: Record<string, TableSort>;
data: typeof data;
};
onCreate() {
this.state = { sorted: {}, data };
}
onSort(event: { sorted: Record<string, TableSort> }, ...params: any) {
this.state.sorted = event.sorted;
this.state.data = [...data].sort((a, b) => {
if (this.state.sorted.listPriceCol === "asc") {
return (
Number(a.listPrice.substring(1)) -
Number(b.listPrice.substring(1))
);
} else if (this.state.sorted.listPriceCol === "desc") {
return (
Number(b.listPrice.substring(1)) -
Number(a.listPrice.substring(1))
);
} else if (this.state.sorted.quantityCol === "asc") {
return (
Number(a.quantityAvailable) - Number(b.quantityAvailable)
);
} else if (this.state.sorted.quantityCol === "desc") {
return (
Number(b.quantityAvailable) - Number(a.quantityAvailable)
);
}
return 0;
});
this.emit("sort", event, ...params);
}
}
<ebay-table on-sort("onSort") ...input>
<@header name="sellerCol" column-type="row-header">
Seller
</@header>
<@header name="itemCol">
Item
</@header>
<@header name="statusCol">
Status
</@header>
<@header
name="listPriceCol"
column-type="numeric"
sort=(state.sorted.listPriceCol || "none")
>
List Price
</@header>
<@header
name="quantityCol"
column-type="numeric"
sort=(state.sorted.quantityCol || "none")
>
Quantity Available
</@header>
<@header name="orderCol">
Orders
</@header>
<@header name="watchersCol" column-type="numeric">
Watchers
</@header>
<@header name="protectionCol" column-type="numeric">
Protection
</@header>
<@header name="shippingCol">
Shipping
</@header>
<@header name="deliveryCol">
Delivery
</@header>
<for|r| of=state.data>
<@row>
<@cell>${r.seller}</@cell>
<@cell>${r.item.title}</@cell>
<@cell>
<ebay-signal status=r.statusType as any>
${r.status}
</ebay-signal>
</@cell>
<@cell>${r.listPrice}</@cell>
<@cell>${r.quantityAvailable}</@cell>
<@cell>
<a href="https://ebay.com">
${r.orders.number}
</a>
</@cell>
<@cell>${r.watchers}</@cell>
<@cell>${r.protection}</@cell>
<@cell>${r.shipping}</@cell>
<@cell>${r.delivery}</@cell>
</@row>
</for>
</ebay-table>
50 changes: 50 additions & 0 deletions src/components/ebay-table/examples/sort-with-link.marko
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import data from "./data.json";

<ebay-table ...input>
<@header
column-type="row-header"
sort=("asc" as const)
href="https://www.ebay.com"
>
Seller
</@header>
<@header>Item</@header>
<@header>Status</@header>
<@header column-type="numeric">
List Price
</@header>
<@header column-type="numeric">
Quantity Available
</@header>
<@header>Orders</@header>
<@header column-type="numeric">
Watchers
</@header>
<@header column-type="numeric">
Protection
</@header>
<@header>Shipping</@header>
<@header>Delivery</@header>
<for|r| of=data>
<@row>
<@cell>${r.seller}</@cell>
<@cell>${r.item.title}</@cell>
<@cell>
<ebay-signal status=r.statusType as any>
${r.status}
</ebay-signal>
</@cell>
<@cell>${r.listPrice}</@cell>
<@cell>${r.quantityAvailable}</@cell>
<@cell>
<a href="https://ebay.com">
${r.orders.number}
</a>
</@cell>
<@cell>${r.watchers}</@cell>
<@cell>${r.protection}</@cell>
<@cell>${r.shipping}</@cell>
<@cell>${r.delivery}</@cell>
</@row>
</for>
</ebay-table>
89 changes: 89 additions & 0 deletions src/components/ebay-table/examples/sort.marko
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import data from "./data.json";
import { type TableSort } from "../component";
class {
declare state: {
sorted: Record<string, TableSort>;
};
onCreate() {
this.state = { sorted: { sellerCol: "asc" } };
}
onSort(event: { sorted: Record<string, TableSort> }, ...params: any) {
this.state.sorted = event.sorted;
this.emit("sort", event, ...params);
}
}

<ebay-table on-sort("onSort") ...input>
<@header
name="sellerCol"
column-type="row-header"
sort=(state.sorted.sellerCol || "none")
>
Seller
</@header>
<@header name="itemCol" sort=(state.sorted.itemCol || "none")>
Item
</@header>
<@header name="statusCol" sort=(state.sorted.statusCol || "none")>
Status
</@header>
<@header
name="listPriceCol"
column-type="numeric"
sort=(state.sorted.listPriceCol || "none")
>
List Price
</@header>
<@header
name="quantityCol"
column-type="numeric"
sort=(state.sorted.quantityCol || "none")
>
Quantity Available
</@header>
<@header name="orderCol" sort=(state.sorted.orderCol || "none")>
Orders
</@header>
<@header
name="watchersCol"
column-type="numeric"
sort=(state.sorted.watchersCol || "none")
>
Watchers
</@header>
<@header
name="protectionCol"
column-type="numeric"
sort=(state.sorted.protectionCol || "none")
>
Protection
</@header>
<@header name="shippingCol" sort=(state.sorted.shippingCol || "none")>
Shipping
</@header>
<@header name="deliveryCol" sort=(state.sorted.deliveryCol || "none")>
Delivery
</@header>
<for|r| of=data>
<@row>
<@cell>${r.seller}</@cell>
<@cell>${r.item.title}</@cell>
<@cell>
<ebay-signal status=r.statusType as any>
${r.status}
</ebay-signal>
</@cell>
<@cell>${r.listPrice}</@cell>
<@cell>${r.quantityAvailable}</@cell>
<@cell>
<a href="https://ebay.com">
${r.orders.number}
</a>
</@cell>
<@cell>${r.watchers}</@cell>
<@cell>${r.protection}</@cell>
<@cell>${r.shipping}</@cell>
<@cell>${r.delivery}</@cell>
</@row>
</for>
</ebay-table>
Loading

0 comments on commit a23c4ec

Please sign in to comment.