Skip to content

Commit

Permalink
♻️ refactor(typescript): improve general type safety
Browse files Browse the repository at this point in the history
  • Loading branch information
thrownullexception committed Dec 8, 2023
1 parent 0eecd44 commit 429fd8c
Show file tree
Hide file tree
Showing 27 changed files with 197 additions and 150 deletions.
4 changes: 2 additions & 2 deletions src/backend/dashboard-widgets/dashboard-widgets.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
import {
listOrderApiService,
ListOrderApiService,
sortByListOrder,
sortListByOrder,
} from "backend/list-order/list-order.service";
import { rolesApiService, RolesApiService } from "backend/roles/roles.service";
import { userFriendlyCase } from "shared/lib/strings/friendly-case";
Expand Down Expand Up @@ -183,7 +183,7 @@ return [actual[0], relative[0]];
await this._dashboardWidgetsPersistenceService.getAllItemsIn(widgetList)
);

return sortByListOrder(widgetList, widgets);
return sortListByOrder(widgetList, widgets);
}

async listDashboardWidgets(
Expand Down
2 changes: 1 addition & 1 deletion src/backend/list-order/list-order.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
} from "backend/lib/config-persistence";
import { IApplicationService } from "backend/types";

export { sortByListOrder } from "./utils";
export { sortListByOrder } from "./utils";

export class ListOrderApiService implements IApplicationService {
constructor(
Expand Down
16 changes: 11 additions & 5 deletions src/backend/list-order/utils.spec.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
import { sortByListOrder } from "./utils";
import { sortListByOrder } from "./utils";

describe("sortByListOrder", () => {
describe("sortListByOrder", () => {
it("should return the same list if the order is empty", () => {
const list = [{ id: "1" }, { id: "2" }, { id: "3" }];
const order = [];
expect(sortByListOrder(order, list)).toEqual(list);
expect(sortListByOrder(order, list)).toEqual(list);
});

it("should return only items in the list even if present in the order", () => {
const list = [{ id: "1" }, { id: "3" }];
const order = ["3", "2", "1"];
expect(sortListByOrder(order, list)).toEqual([{ id: "3" }, { id: "1" }]);
});

it("should order the list based on the order", () => {
const list = [{ id: "1" }, { id: "2" }, { id: "3" }];
const order = ["3", "2", "1"];
expect(sortByListOrder(order, list)).toEqual([
expect(sortListByOrder(order, list)).toEqual([
{ id: "3" },
{ id: "2" },
{ id: "1" },
Expand All @@ -20,7 +26,7 @@ describe("sortByListOrder", () => {
it("should append the remaining items at the end of the list", () => {
const list = [{ id: "1" }, { id: "2" }, { id: "3" }, { id: "4" }];
const order = ["3", "2", "1"];
expect(sortByListOrder(order, list)).toEqual([
expect(sortListByOrder(order, list)).toEqual([
{ id: "3" },
{ id: "2" },
{ id: "1" },
Expand Down
14 changes: 7 additions & 7 deletions src/backend/list-order/utils.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
export function sortByListOrder<T extends { id: string }>(
export function sortListByOrder<T extends { id: string }>(
order: string[],
itemListOrder: T[]
itemsToOrder: T[]
): T[] {
if (order.length === 0) {
return itemListOrder;
return itemsToOrder;
}
const itemsMap = Object.fromEntries(
itemListOrder.map((item) => [item.id, item])
itemsToOrder.map((item) => [item.id, item])
);

const orderedItems = order.map((item) => itemsMap[item]);

if (order.length === itemListOrder.length) {
if (order.length === itemsToOrder.length) {
return orderedItems;
}

const remainingItems = itemListOrder.filter(
const remainingItems = itemsToOrder.filter(
(item) => !order.includes(item.id)
);

return [...orderedItems, ...remainingItems];
return [...orderedItems, ...remainingItems].filter(Boolean);
}
7 changes: 2 additions & 5 deletions src/frontend/components/SchemaForm/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,8 @@ import { FormButton } from "frontend/design-system/components/Button/FormButton"
import { userFriendlyCase } from "shared/lib/strings/friendly-case";
import { ButtonIconTypes } from "frontend/design-system/components/Button/constants";
import styled from "styled-components";
import {
GridSpanSizes,
gridItem,
gridRoot,
} from "frontend/design-system/constants/grid";
import { gridItem, gridRoot } from "frontend/design-system/constants/grid";
import { GridSpanSizes } from "shared/types/ui";
import { RenderFormInput } from "./_RenderFormInput";
import { IFormExtension } from "./types";
import { runFormBeforeSubmit, runFormFieldState } from "./form-run";
Expand Down
10 changes: 8 additions & 2 deletions src/frontend/design-system/components/Form/FormSelect/Simple.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@ interface ISimpleSelect {
value: number | string;
fullWidth?: boolean;
sm?: true;
width: number;
ariaLabel?: string;
}

const SimpleSelectStyled = styled(Input)<{ fullWidth?: boolean }>`
const SimpleSelectStyled = styled(Input)<{
fullWidth?: boolean;
width: number;
}>`
display: inline-block;
width: 50px;
width: ${(props) => props.width}px;
vertical-align: middle;
background: ${USE_ROOT_COLOR("base-color")}
url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%232c3652' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e")
Expand All @@ -34,6 +38,7 @@ export function SimpleSelect({
value,
fullWidth,
sm,
width,
ariaLabel,
}: ISimpleSelect) {
return (
Expand All @@ -42,6 +47,7 @@ export function SimpleSelect({
aria-label={ariaLabel}
value={value}
sm={sm}
width={width}
fullWidth={fullWidth}
onChange={(e: { target: { value: string } }) => {
onChange(e.target.value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ export function SectionBox({
setSelectionValue(newSelectionValue);
selection.onChange(newSelectionValue);
}}
width={50}
value={selectionValue}
/>
) : null}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ interface IProps {
export const GridRoot = styled.div`
display: grid;
grid-gap: 16px;
grid-template-columns: 2fr 9fr;
grid-template-columns: 2fr 8fr;
@media (max-width: ${BREAKPOINTS.md}) {
grid-template-columns: 1fr;
}
Expand All @@ -38,7 +38,7 @@ ContentLayout.Left = function SectionLeft({ children }: IProps) {
};

ContentLayout.Right = function SectionRight({ children }: IProps) {
return <div style={{ overflow: "scroll" }}>{children}</div>;
return <div style={{ overflowX: "scroll" }}>{children}</div>;
};

ContentLayout.Center = function SectionCenter({ children }: IProps) {
Expand Down
6 changes: 3 additions & 3 deletions src/frontend/design-system/components/SortList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import React, { useState, useEffect } from "react";
import SortableList, { SortableItem } from "react-easy-sort";
import { Move } from "react-feather";
import styled from "styled-components";
import { USE_ROOT_COLOR } from "frontend/design-system/theme/root";
import { DataStateKeys } from "frontend/lib/data/types";
import { Stack } from "frontend/design-system/primitives/Stack";
import { pluralize } from "shared/lib/strings";
Expand All @@ -13,6 +12,7 @@ import { EmptyWrapper } from "../EmptyWrapper";
import { FormButton } from "../Button/FormButton";
import { defaultToEmptyArray } from "./utils";
import { ListSkeleton } from "../Skeleton/List";
import { SHADOW_CSS } from "../Card";

function arrayMoveMutable<T>(array: T[], fromIndex: number, toIndex: number) {
const startIndex = fromIndex < 0 ? array.length + fromIndex : fromIndex;
Expand All @@ -39,8 +39,8 @@ export interface IProps<T> {
const THRESHOLD_FOR_LONG_ITEMS_TO_SHOW_SAVE_CHANGES_AT_TOP = 10;

const SortItem = styled(Stack)`
border: 1px solid ${USE_ROOT_COLOR("border-color")};
margin: 4px 0px;
${SHADOW_CSS}
margin: 12px 0px;
padding: 8px;
user-select: none;
border-radius: 4px;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export function TablePagination({
<Typo.MD>
Showing{" "}
<SimpleSelect
width={55}
options={PAGE_SIZES.map((option) => ({
value: `${option}`,
label: `${option}`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export function FilterTableByBooleans({
value: value === "" ? undefined : value === "true",
});
}}
width={0}
ariaLabel="Select Boolean"
fullWidth
value={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,14 @@ export function DateSelection({
[field]: setCountValue(value, currentFilterValue),
});
}}
width={50}
value={getCountValue(currentFilterValue) || "1"}
/>
)}
<SimpleSelect
options={dateOptions.map(({ value, label }) => ({ label, value }))}
fullWidth
width={0}
onChange={(value) => {
setFilter({
...filterValue,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export function RenderFilterOperator<T>({
]}
ariaLabel="Select Filter Operator"
fullWidth
width={0}
onChange={(value) => {
setFilter({
...filterValue,
Expand Down
5 changes: 1 addition & 4 deletions src/frontend/design-system/constants/grid.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { css } from "styled-components";
import { GridHeightSizes, GridSpanSizes } from "shared/types/ui";
import { BREAKPOINTS } from "./breakpoints";

export type GridSpanSizes = "1" | "2" | "3" | "4";

export type GridHeightSizes = "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8";

export const gridHeightToPx = (unit: GridHeightSizes) => +unit * 100;

export const GRID_SPAN_CONFIG: Record<
Expand Down
2 changes: 1 addition & 1 deletion src/frontend/design-system/primitives/Divider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ import styled from "styled-components";
import { USE_ROOT_COLOR } from "frontend/design-system/theme/root";

export const Divider = styled.div`
border-top: 1px solid ${USE_ROOT_COLOR("border-color")};
border-top: 2px solid ${USE_ROOT_COLOR("border-color")};
`;
5 changes: 1 addition & 4 deletions src/frontend/views/Dashboard/Widget/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import {
GridSpanSizes,
GridHeightSizes,
} from "frontend/design-system/constants/grid";
import { GridSpanSizes, GridHeightSizes } from "shared/types/ui";
import { ReactElement } from "react";
import { z } from "zod";

Expand Down
9 changes: 3 additions & 6 deletions src/frontend/views/Dashboard/styles.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import {
GridSpanSizes,
gridItem,
gridRoot,
} from "frontend/design-system/constants/grid";
import { gridItem, gridRoot } from "frontend/design-system/constants/grid";
import { GridSpanSizes } from "shared/types/ui";
import styled, { css } from "styled-components";

export const dashboardGridRoot = css`
Expand All @@ -20,7 +17,7 @@ export const WidgetRoot = styled.div<{
${(props) =>
props.hasSetting &&
css`
cursor: grab;
cursor: move;
user-select: none;
`}
Expand Down
36 changes: 23 additions & 13 deletions src/frontend/views/entity/Crud/EntityFieldsSelectionSettings.tsx
Original file line number Diff line number Diff line change
@@ -1,40 +1,50 @@
import { ViewStateMachine } from "frontend/components/ViewStateMachine";
import { useEffect, useState } from "react";
import { IEntityField } from "shared/types/db";
import { FormButton } from "frontend/design-system/components/Button/FormButton";
import { ListSkeleton } from "frontend/design-system/components/Skeleton/List";
import { RenderList } from "frontend/design-system/components/RenderList";
import { Spacer } from "frontend/design-system/primitives/Spacer";
import { SectionListItem } from "frontend/design-system/components/Section/SectionList";
import { Stack } from "frontend/design-system/primitives/Stack";
import { useStringSelections } from "frontend/lib/selection";
import { useEntityFields } from "frontend/hooks/entity/entity.store";
import {
useEntityFieldLabels,
useEntitySlug,
} from "frontend/hooks/entity/entity.config";
import { IEntityCrudSettings } from "shared/configurations";
import { ENTITY_CRUD_LABELS } from "../constants";
import { makeEntityFieldsSelectionKey } from "./constants";

interface IProps {
label: string;
columns?: {
fields: IEntityField[];
submit?: (columnsSelection: string[]) => Promise<string[]>;
hidden: string[];
getEntityFieldLabels?: (fieldName: string) => string;
};
toggling: {
onToggle?: () => void;
enabled: boolean;
label: string;
};
isLoading: boolean;
error: unknown;
crudKey: keyof IEntityCrudSettings | "table";
}

export function EntityFieldsSelectionSettings({
columns,
isLoading,
toggling,
error,
label,
crudKey,
}: IProps) {
const entity = useEntitySlug();

const entityFields = useEntityFields(entity);

const getEntityFieldLabels = useEntityFieldLabels();

const { toggleSelection, allSelections, selectMutiple, isSelected } =
useStringSelections(`${label}CrudEntityFieldsSelectionSettings}`);
useStringSelections(makeEntityFieldsSelectionKey(entity, crudKey));

const [touched, setTouched] = useState(false);

Expand All @@ -54,10 +64,10 @@ export function EntityFieldsSelectionSettings({
{toggling && toggling.onToggle && (
<FormButton
isMakingRequest={false}
icon={toggling?.enabled ? "check" : "square"}
icon={toggling.enabled ? "check" : "square"}
size="sm"
isInverse
text={() => `Enable ${toggling?.label} Functionality`}
text={() => `Enable ${ENTITY_CRUD_LABELS[crudKey]} Functionality`}
onClick={() => toggling.onToggle()}
/>
)}
Expand All @@ -66,9 +76,9 @@ export function EntityFieldsSelectionSettings({
{columns && (
<>
<RenderList
items={columns.fields}
items={entityFields.data}
singular="Field"
getLabel={columns.getEntityFieldLabels}
getLabel={getEntityFieldLabels}
render={(menuItem) => {
const isHidden = isSelected(menuItem.name);

Expand Down Expand Up @@ -106,8 +116,8 @@ export function EntityFieldsSelectionSettings({
}}
text={(isSubmitting) =>
isSubmitting
? `Saving ${label} Selections`
: `Save ${label} Selections`
? `Saving ${ENTITY_CRUD_LABELS[crudKey]} Selections`
: `Save ${ENTITY_CRUD_LABELS[crudKey]} Selections`
}
icon="save"
disabled={!touched}
Expand Down
8 changes: 8 additions & 0 deletions src/frontend/views/entity/Crud/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { IEntityCrudSettings } from "shared/configurations";

export const makeEntityFieldsSelectionKey = (
entity: string,
crudKey: keyof IEntityCrudSettings | "table"
) => {
return `${crudKey}-${entity}CrudEntityFieldsSelectionSettings}`;
};
Loading

0 comments on commit 429fd8c

Please sign in to comment.