Skip to content
This repository was archived by the owner on May 24, 2022. It is now read-only.

feat: wishlist paging #95

Merged
merged 1 commit into from
Sep 29, 2021
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
24 changes: 22 additions & 2 deletions cypress/integration/common/wishlist/prerequisites.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,37 @@ Given("the wishlist has {int} wishes", function (count) {
});

Given("the wishlist has the following wishes:", function (table) {
const wishes = table.hashes().map(({ body, id, subject, votes }) => {
const hashes = table.hashes();
const wishes = hashes.map(({ body, id, subject, votes }) => {
return {
authorId: 1,
body,
id,
subject,
voted: false,
moderate: "accepted",
votes,
__typename: "Wish",
};
});
this.count = wishes.length;
cy.gql("wishes", { wishes }, { alias: "gql_wishes" });
cy.gql([
{
operationName: "wishes",
data: { wishes },
alias: "gql_wishes",
},
// For aggregated Wishes
// {
// operationName: "aggregateWishes",
// data: {
// aggregateWishes: {
// count: {
// all: hashes.length,
// },
// },
// },
// alias: "gql_aggregateWishes",
// },
]);
});
2 changes: 2 additions & 0 deletions cypress/integration/common/wishlist/steps.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@ import { When } from "cypress-cucumber-preprocessor/steps";
When("the user is on the wishlist page", function () {
cy.visit("/wishlist");
cy.wait("@gql_wishes");
// Wait for aggregated Wishes
// cy.wait("@gql_aggregateWishes");
});
16 changes: 9 additions & 7 deletions cypress/support/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,15 @@ Cypress.Commands.add("logout", function () {
* @param {object} options Additional options to configure the stub
* @param {object} options.alias The alias of the stub
*/
Cypress.Commands.add("gql", function (operationName, data, { alias }) {
Cypress.Commands.add("gql", function (...operations) {
cy.intercept("POST", Cypress.env("backendUri"), req => {
if (hasOperationName(req, operationName)) {
req.alias = alias;
req.reply(res => {
res.body.data = data;
});
}
operations.forEach(({ operationName, data, alias }) => {
if (hasOperationName(req, operationName)) {
req.alias = alias;
req.reply(res => {
res.body.data = data;
});
}
});
});
});
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"@contentful/rich-text-react-renderer": "15.4.0",
"@contentful/rich-text-types": "15.3.6",
"@dekk-app/dekk-backend": "1.11.0",
"@dekk-utils/string-case": "0.1.2",
"@emotion/cache": "11.4.0",
"@emotion/core": "11.0.0",
"@emotion/react": "11.4.1",
Expand Down Expand Up @@ -70,6 +71,7 @@
"sitemap": "7.0.0",
"tslib": "2.3.1",
"use-cookie-consent": "0.1.15",
"use-dark-mode": "2.3.1",
"use-debounce": "7.0.0",
"zustand": "3.5.10"
},
Expand Down
6 changes: 5 additions & 1 deletion public/static/locales/de/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,9 @@
"home": "Home",
"signin": "Anmelden",
"signout": "Abmelden",
"updated-at": "Aktualisiert am"
"updated-at": "Aktualisiert am",
"first-page": "Erste Seite",
"last-page": "Letzte Seite",
"next-page": "Nächste Seite",
"previous-page": "Vorherige Seite"
}
4 changes: 3 additions & 1 deletion public/static/locales/de/wishlist.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,16 @@
},
"search-wishes": "Wünsche durchsuchen",
"filter": "Filter",
"hide": "Verstecken",
"pending": "Ausstehend",
"declined": "Abgelehnt",
"accepted": "Akzeptiert",
"tooltip": {
"disabled": "Wünsche mit Stimmen können nicht bearbeitet werden."
},
"add-wish": {
"headline": "Nenne uns deinen Wunsch",
"body": "Als Dankeschön schenken wir dir ein Dekk Pro Konto"
},
"my-wish": "Mein Wunsch"
"my-wish": "Von mir"
}
6 changes: 5 additions & 1 deletion public/static/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,9 @@
"home": "Home",
"signin": "Sign in",
"signout": "Sign out",
"updated-at": "Updated at"
"updated-at": "Updated at",
"first-page": "First page",
"last-page": "Last page",
"next-page": "Next page",
"previous-page": "Previous page"
}
4 changes: 3 additions & 1 deletion public/static/locales/en/wishlist.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,16 @@
},
"search-wishes": "Search wishes",
"filter": "Filter",
"hide": "Hide",
"pending": "Pending",
"declined": "Declined",
"accepted": "Accepted",
"tooltip": {
"disabled": "Can't edit wishes with votes."
},
"add-wish": {
"headline": "Tell us your wish",
"body": "As a thank-you we will gift you a Dekk pro account"
},
"my-wish": "My wish"
"my-wish": "Mine"
}
1 change: 1 addition & 0 deletions src/atoms/button/styled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export const StyledButton = styled.button<StyledButtonProps>`
margin: 0;
padding: 0 ${pxToRem(24)};
border: 0;
font-family: inherit;
font-size: ${pxToRem(16)};
font-weight: 600;
line-height: ${pxToRem(24)};
Expand Down
5 changes: 5 additions & 0 deletions src/atoms/icon-button/styled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,18 @@ export const StyledIconButton = styled.button`
border-radius: 50%;
background: none;
color: currentColor;
font-family: inherit;
font-size: inherit;
font-weight: 600;
line-height: ${pxToRem(24)};

&:focus {
outline: 0;
}

&[disabled] {
opacity: 0.2;
pointer-events: none;
}

${({ theme }) => css`
Expand Down
5 changes: 5 additions & 0 deletions src/ions/hooks/case/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { objectSnakeToCamel } from "@/ions/utils/object";
import { useMemo } from "react";

export const useObjectSnakeToCamel = <T>(value: Record<string, T>) =>
useMemo(() => objectSnakeToCamel<T>(value), [value]);
81 changes: 81 additions & 0 deletions src/ions/hooks/paging/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { useObjectSnakeToCamel } from "@/ions/hooks/case";
import { useQueryRouter } from "@/ions/hooks/query-router";
import { objectCamelToSnake } from "@/ions/utils/object";
import { decreaseStringNumber, increaseStringNumber, toString } from "@/ions/utils/string";
import { useCallback, useEffect } from "react";

export const usePaging = (length: number, { first = 0, property = "page" }) => {
const { push, query } = useQueryRouter();
const camelObject = useObjectSnakeToCamel<string | string[]>({
[property]: toString(first),
...query,
});

const last = length - 1 + first;
const current = camelObject[property]
? Number.parseInt(camelObject[property] as string, 10)
: first;
// Clamp to max
const clamped = Math.max(Math.min(current, last), first);

useEffect(() => {
if (clamped < current) {
void push(
objectCamelToSnake({
[property]: toString(last),
})
);
}
}, [push, property, current, clamped, last]);

const goTo = useCallback(
(nextState: number) => {
void push(
objectCamelToSnake({
[property]: toString(nextState),
})
);
},
[property, push]
);

const toNext = useCallback(() => {
void push(
objectCamelToSnake({
[property]: increaseStringNumber(camelObject[property] as string),
})
);
}, [property, push, camelObject]);

const toPrevious = useCallback(() => {
void push(
objectCamelToSnake({
[property]: decreaseStringNumber(camelObject[property] as string),
})
);
}, [property, push, camelObject]);

const toFirst = useCallback(() => {
void push(
objectCamelToSnake({
[property]: toString(first),
})
);
}, [property, push, first]);

const toLast = useCallback(() => {
void push(
objectCamelToSnake({
[property]: toString(last),
})
);
}, [property, push, last]);
return {
goTo,
toNext,
toPrevious,
toFirst,
toLast,
current,
};
};
34 changes: 34 additions & 0 deletions src/ions/hooks/query-router/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { useRouter } from "next/router";
import { useCallback } from "react";

export const cleanQuery = <T>(query: Record<string, T>) => {
const NextQuery = {};
for (const key in query) {
if (query[key] !== null && query[key] !== undefined) {
NextQuery[key] = query[key];
}
}

return NextQuery;
};

export const useQueryRouter = () => {
const { query, push: routerPush, pathname } = useRouter();
const push = useCallback(
async (nextQuery: Record<string, string>) => {
return routerPush(
{
pathname,
query: cleanQuery({
...query,
...nextQuery,
}),
},
undefined,
{ shallow: true }
);
},
[pathname, routerPush, query]
);
return { push, query, pathname };
};
31 changes: 18 additions & 13 deletions src/ions/hooks/route-loader/index.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,43 @@
import { Router } from "next/router";
import { useEffect, useMemo, useState } from "react";

export interface Options {
shallow?: boolean;
}

export const useRouteLoader = () => {
const [loading, setLoading] = useState(false);
const [loaded, setLoaded] = useState(false);
useEffect(() => {
let timer: number;
const start = () => {
setLoaded(false);
setLoading(true);
const start = (pathname: string, options: Options) => {
if (!options.shallow) {
setLoaded(false);
setLoading(true);
}
};

const end = () => {
setLoading(false);
setLoaded(true);

timer = window.setTimeout(() => {
setLoaded(false);
const end = (pathname: string, options: Options) => {
if (!options.shallow) {
setLoading(false);
}, 250);
setLoaded(true);

timer = window.setTimeout(() => {
setLoaded(false);
}, 250);
}
};

Router.events.on("routeChangeStart", start);
Router.events.on("routeChangeComplete", end);

const unsubscribe = () => {
return () => {
Router.events.off("routeChangeStart", start);
Router.events.off("routeChangeComplete", end);
window.clearTimeout(timer);
setLoaded(false);
setLoading(false);
};

return unsubscribe;
}, []);

return useMemo(() => ({ loaded, loading }), [loaded, loading]);
Expand Down
12 changes: 8 additions & 4 deletions src/ions/icons/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import { IconName } from "./types";

export const icons: Record<IconName, string> = {
chevronDown: "M7.41,8.58L12,13.17L16.59,8.58L18,10L12,16L6,10L7.41,8.58Z",
chevronUp: "M7.41,15.41L12,10.83L16.59,15.41L18,14L12,8L6,14L7.41,15.41Z",
chevronLeft: "M15.41,16.58L10.83,12L15.41,7.41L14,6L8,12L14,18L15.41,16.58Z",
chevronRight: "M8.59,16.58L13.17,12L8.59,7.41L10,6L16,12L10,18L8.59,16.58Z",
check: "M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z",
checkCircleOutline:
"M12 2C6.5 2 2 6.5 2 12S6.5 22 12 22 22 17.5 22 12 17.5 2 12 2M12 20C7.59 20 4 16.41 4 12S7.59 4 12 4 20 7.59 20 12 16.41 20 12 20M16.59 7.58L10 14.17L7.41 11.59L6 13L10 17L18 9L16.59 7.58Z",
chevronDoubleLeft:
"M18.41,7.41L17,6L11,12L17,18L18.41,16.59L13.83,12L18.41,7.41M12.41,7.41L11,6L5,12L11,18L12.41,16.59L7.83,12L12.41,7.41Z",
chevronDoubleRight:
"M5.59,7.41L7,6L13,12L7,18L5.59,16.59L10.17,12L5.59,7.41M11.59,7.41L13,6L19,12L13,18L11.59,16.59L16.17,12L11.59,7.41Z",
chevronDown: "M7.41,8.58L12,13.17L16.59,8.58L18,10L12,16L6,10L7.41,8.58Z",
chevronLeft: "M15.41,16.58L10.83,12L15.41,7.41L14,6L8,12L14,18L15.41,16.58Z",
chevronRight: "M8.59,16.58L13.17,12L8.59,7.41L10,6L16,12L10,18L8.59,16.58Z",
chevronUp: "M7.41,15.41L12,10.83L16.59,15.41L18,14L12,8L6,14L7.41,15.41Z",
close: "M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z",
delete: "M19,4H15.5L14.5,3H9.5L8.5,4H5V6H19M6,19A2,2 0 0,0 8,21H16A2,2 0 0,0 18,19V7H6V19Z",
edit: "M20.71,7.04C21.1,6.65 21.1,6 20.71,5.63L18.37,3.29C18,2.9 17.35,2.9 16.96,3.29L15.12,5.12L18.87,8.87M3,17.25V21H6.75L17.81,9.93L14.06,6.18L3,17.25Z",
Expand Down
2 changes: 2 additions & 0 deletions src/ions/icons/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
export type IconName =
| "check"
| "checkCircleOutline"
| "chevronDoubleLeft"
| "chevronDoubleRight"
| "chevronDown"
| "chevronRight"
| "chevronLeft"
Expand Down
Loading