Fix strict null errors in utils (#3007)

* Fix strict null in utils/errors

* Fix auth-related src/utils strict null errors

* Fix data, maps and tables utils

* Fix utils/urls

* Fix filters

* Fix strict null errors in utils/handlers

* Fix strict nulls in utils/menu

* Fix strict nulls in utils/sort

* Fix strict nulls in utils/richText

* Fix search handler

* Fix siteSettings leftover type mismatch

* Fix page map error for attribute case

* Fix type error for sdk error codes

* Fix Choice type in page maps

* Fix error types mismatch

* Remove addressType from type union in auth error message
This commit is contained in:
Michał Droń 2023-01-23 09:30:04 +01:00 committed by GitHub
parent 221adf25d6
commit abbe76442d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
39 changed files with 121 additions and 90 deletions

View file

@ -6,7 +6,7 @@ import makeQuery, { UseQueryResult } from "./makeQuery";
import useDebounce from "./useDebounce"; import useDebounce from "./useDebounce";
export interface SearchVariables { export interface SearchVariables {
after?: string; after?: string | null;
first: number; first: number;
query: string; query: string;
} }

View file

@ -9,7 +9,7 @@ import SiteSettingsPage, { SiteSettingsPageProps } from "./SiteSettingsPage";
const props: Omit<SiteSettingsPageProps, "classes"> = { const props: Omit<SiteSettingsPageProps, "classes"> = {
disabled: false, disabled: false,
errors: [], errors: [],
onSubmit: () => undefined, onSubmit: async () => undefined,
saveButtonBarState: "default", saveButtonBarState: "default",
shop, shop,
}; };

View file

@ -7,6 +7,6 @@ export enum GqlErrors {
export function hasError(err: ApolloError, ...errorCodes: string[]): boolean { export function hasError(err: ApolloError, ...errorCodes: string[]): boolean {
return err.graphQLErrors.some(gqlError => return err.graphQLErrors.some(gqlError =>
errorCodes.includes(gqlError.extensions.exception.code), errorCodes.includes(gqlError.extensions?.exception.code),
); );
} }

View file

@ -1,31 +1,30 @@
import { LoginData } from "@saleor/sdk"; import { UserFragment } from "@dashboard/graphql";
import { UserDetailsFragment } from "@saleor/sdk/dist/apollo/types";
export const isSupported = !!( export const isSupported = !!window.PasswordCredential;
navigator?.credentials?.preventSilentAccess && window.PasswordCredential
);
export async function login<T>( export async function login<T>(
loginFn: (id: string, password: string) => Promise<T>, loginFn: (id: string, password: string) => Promise<T>,
): Promise<T | null> { ): Promise<T | null> {
let result: T; let result: T | null;
try { try {
const credential = await navigator.credentials.get({ password: true }); const credential = await navigator.credentials.get({ password: true });
if (credential instanceof PasswordCredential) { if (credential instanceof PasswordCredential) {
result = await loginFn(credential.id, credential.password); result = await loginFn(credential.id, credential.password ?? "");
} }
} catch { } catch {
result = null; result = null;
} }
return result; return result!;
} }
export function saveCredentials( export function saveCredentials(
user: LoginData["user"], user: UserFragment | UserDetailsFragment,
password: string, password: string,
): Promise<CredentialType | null> { ): Promise<CredentialType> | null {
let result: Promise<CredentialType | null>; let result: Promise<CredentialType> | null;
if (isSupported) { if (isSupported) {
const cred = new PasswordCredential({ const cred = new PasswordCredential({

View file

@ -1,5 +1,5 @@
interface PublicationData { interface PublicationData {
publicationDate: string; publicationDate: string | null;
isPublished: boolean; isPublished: boolean;
} }

View file

@ -1,5 +1,8 @@
import { AccountErrorCode } from "@dashboard/graphql"; import { AccountErrorCode } from "@dashboard/graphql";
import { SetPasswordData } from "@saleor/sdk"; import {
AccountError,
AccountErrorCode as SdkAccountErrorCode,
} from "@saleor/sdk/dist/apollo/types";
import { defineMessages, IntlShape } from "react-intl"; import { defineMessages, IntlShape } from "react-intl";
import { getCommonFormFieldErrorMessage } from "./common"; import { getCommonFormFieldErrorMessage } from "./common";
@ -48,11 +51,14 @@ const messages = defineMessages({
}); });
interface ErrorFragment { interface ErrorFragment {
code: AccountErrorCode | SetPasswordData["errors"][number]["code"]; code: AccountErrorCode;
field: string | null; field: string | null;
} }
function getAccountErrorMessage(err: ErrorFragment, intl: IntlShape): string { function getAccountErrorMessage(
err: ErrorFragment | Omit<AccountError, "addressType">,
intl: IntlShape,
): string | undefined {
if (err) { if (err) {
switch (err.code) { switch (err.code) {
case AccountErrorCode.INVALID_PASSWORD: case AccountErrorCode.INVALID_PASSWORD:
@ -78,7 +84,10 @@ function getAccountErrorMessage(err: ErrorFragment, intl: IntlShape): string {
} }
} }
return getCommonFormFieldErrorMessage(err, intl); return getCommonFormFieldErrorMessage<AccountErrorCode | SdkAccountErrorCode>(
err,
intl,
);
} }
export default getAccountErrorMessage; export default getAccountErrorMessage;

View file

@ -38,7 +38,10 @@ const messages = defineMessages({
}, },
}); });
function getAppErrorMessage(err: AppErrorFragment, intl: IntlShape): string { function getAppErrorMessage(
err: AppErrorFragment,
intl: IntlShape,
): string | undefined {
if (err) { if (err) {
switch (err.code) { switch (err.code) {
case AppErrorCode.INVALID_MANIFEST_FORMAT: case AppErrorCode.INVALID_MANIFEST_FORMAT:

View file

@ -21,7 +21,7 @@ const messages = defineMessages({
function getAttributeErrorMessage( function getAttributeErrorMessage(
err: Omit<AttributeErrorFragment, "__typename"> | undefined, err: Omit<AttributeErrorFragment, "__typename"> | undefined,
intl: IntlShape, intl: IntlShape,
): string { ): string | undefined {
if (err) { if (err) {
switch (err.code) { switch (err.code) {
case AttributeErrorCode.ALREADY_EXISTS: case AttributeErrorCode.ALREADY_EXISTS:

View file

@ -21,7 +21,7 @@ const messages = defineMessages({
function getChannelsErrorMessage( function getChannelsErrorMessage(
err: Omit<ChannelErrorFragment, "__typename"> | undefined, err: Omit<ChannelErrorFragment, "__typename"> | undefined,
intl: IntlShape, intl: IntlShape,
): string { ): string | undefined {
if (err) { if (err) {
switch (err.code) { switch (err.code) {
case ChannelErrorCode.ALREADY_EXISTS: case ChannelErrorCode.ALREADY_EXISTS:

View file

@ -33,7 +33,7 @@ export interface CommonError<ErrorCode> {
export function getCommonFormFieldErrorMessage<ErrorCode>( export function getCommonFormFieldErrorMessage<ErrorCode>(
error: CommonError<ErrorCode> | undefined, error: CommonError<ErrorCode> | undefined,
intl: IntlShape, intl: IntlShape,
): string { ): string | undefined {
if (error) { if (error) {
switch (error.code) { switch (error.code) {
case "GRAPHQL_ERROR": case "GRAPHQL_ERROR":

View file

@ -14,7 +14,7 @@ const messages = defineMessages({
function getDiscountErrorMessage( function getDiscountErrorMessage(
err: Omit<DiscountErrorFragment, "__typename"> | undefined, err: Omit<DiscountErrorFragment, "__typename"> | undefined,
intl: IntlShape, intl: IntlShape,
): string { ): string | undefined {
if (err) { if (err) {
switch (err.code) { switch (err.code) {
case DiscountErrorCode.ALREADY_EXISTS: case DiscountErrorCode.ALREADY_EXISTS:

View file

@ -6,7 +6,7 @@ import { getCommonFormFieldErrorMessage } from "./common";
function getExportErrorMessage( function getExportErrorMessage(
err: Omit<ExportErrorFragment, "__typename"> | undefined, err: Omit<ExportErrorFragment, "__typename"> | undefined,
intl: IntlShape, intl: IntlShape,
): string { ): string | undefined {
return getCommonFormFieldErrorMessage(err, intl); return getCommonFormFieldErrorMessage(err, intl);
} }

View file

@ -3,20 +3,14 @@ import { UserError } from "@dashboard/types";
export function getFieldError<T extends UserError>( export function getFieldError<T extends UserError>(
errors: T[], errors: T[],
field: string, field: string,
): T { ): T | undefined {
return errors.find(err => err.field === field); return errors.find(err => err.field === field);
} }
export function getErrors(errors: UserError[]): string[] {
return errors
.filter(err => ["", null].includes(err.field))
.map(err => err.message);
}
export type FormErrors< export type FormErrors<
TField extends string, TField extends string,
TError extends UserError TError extends UserError
> = Record<TField, TError>; > = Record<TField, TError | undefined>;
export function getFormErrors<TField extends string, TError extends UserError>( export function getFormErrors<TField extends string, TError extends UserError>(
fields: TField[], fields: TField[],
@ -25,7 +19,7 @@ export function getFormErrors<TField extends string, TError extends UserError>(
return fields.reduce((errs, field) => { return fields.reduce((errs, field) => {
errs[field] = getFieldError(errors, field); errs[field] = getFieldError(errors, field);
return errs; return errs;
}, ({} as unknown) as Record<TField, TError>); }, ({} as unknown) as Record<TField, TError | undefined>);
} }
export interface ChannelError { export interface ChannelError {

View file

@ -40,7 +40,7 @@ const messages = defineMessages({
function getInvoiceErrorMessage( function getInvoiceErrorMessage(
err: InvoiceErrorFragment, err: InvoiceErrorFragment,
intl: IntlShape, intl: IntlShape,
): string { ): string | undefined {
if (err) { if (err) {
switch (err.code) { switch (err.code) {
case InvoiceErrorCode.EMAIL_NOT_SET: case InvoiceErrorCode.EMAIL_NOT_SET:

View file

@ -6,7 +6,7 @@ import { getCommonFormFieldErrorMessage } from "./common";
function getMenuErrorMessage( function getMenuErrorMessage(
err: Omit<MenuErrorFragment, "__typename"> | undefined, err: Omit<MenuErrorFragment, "__typename"> | undefined,
intl: IntlShape, intl: IntlShape,
): string { ): string | undefined {
return getCommonFormFieldErrorMessage(err, intl); return getCommonFormFieldErrorMessage(err, intl);
} }

View file

@ -75,7 +75,7 @@ const messages = defineMessages({
function getOrderErrorMessage( function getOrderErrorMessage(
err: OrderErrorFragment, err: OrderErrorFragment,
intl: IntlShape, intl: IntlShape,
): string { ): string | undefined {
if (err) { if (err) {
switch (err.code) { switch (err.code) {
case OrderErrorCode.BILLING_ADDRESS_NOT_SET: case OrderErrorCode.BILLING_ADDRESS_NOT_SET:

View file

@ -29,7 +29,7 @@ const messages = defineMessages({
function getPageErrorMessage( function getPageErrorMessage(
err: Omit<PageErrorFragment, "__typename"> | undefined, err: Omit<PageErrorFragment, "__typename"> | undefined,
intl: IntlShape, intl: IntlShape,
): string { ): string | undefined {
if (err) { if (err) {
switch (err.code) { switch (err.code) {
case PageErrorCode.UNIQUE: case PageErrorCode.UNIQUE:

View file

@ -32,7 +32,7 @@ const messages = defineMessages({
function getPermissionGroupErrorMessage( function getPermissionGroupErrorMessage(
err: PermissionGroupErrorFragment, err: PermissionGroupErrorFragment,
intl: IntlShape, intl: IntlShape,
): string { ): string | undefined {
if (err) { if (err) {
switch (err.code) { switch (err.code) {
case PermissionGroupErrorCode.ASSIGN_NON_STAFF_MEMBER: case PermissionGroupErrorCode.ASSIGN_NON_STAFF_MEMBER:

View file

@ -17,7 +17,7 @@ const messages = defineMessages({
function getPluginErrorMessage( function getPluginErrorMessage(
err: PluginErrorFragment, err: PluginErrorFragment,
intl: IntlShape, intl: IntlShape,
): string { ): string | undefined {
if (err) { if (err) {
switch (err.code) { switch (err.code) {
case PluginErrorCode.PLUGIN_MISCONFIGURED: case PluginErrorCode.PLUGIN_MISCONFIGURED:

View file

@ -83,7 +83,7 @@ function getProductErrorMessage(
> >
| undefined, | undefined,
intl: IntlShape, intl: IntlShape,
): string { ): string | undefined {
if (err) { if (err) {
switch (err.code) { switch (err.code) {
case ProductErrorCode.ATTRIBUTE_ALREADY_ASSIGNED: case ProductErrorCode.ATTRIBUTE_ALREADY_ASSIGNED:
@ -119,7 +119,7 @@ function getProductErrorMessage(
export function getProductVariantAttributeErrorMessage( export function getProductVariantAttributeErrorMessage(
err: Omit<ProductErrorFragment, "__typename"> | undefined, err: Omit<ProductErrorFragment, "__typename"> | undefined,
intl: IntlShape, intl: IntlShape,
): string { ): string | undefined {
if (err) { if (err) {
switch (err.code) { switch (err.code) {
case ProductErrorCode.UNIQUE: case ProductErrorCode.UNIQUE:
@ -135,7 +135,7 @@ export function getProductVariantAttributeErrorMessage(
export function getBulkProductErrorMessage( export function getBulkProductErrorMessage(
err: BulkProductErrorFragment | undefined, err: BulkProductErrorFragment | undefined,
intl: IntlShape, intl: IntlShape,
): string { ): string | undefined {
if (err?.code === ProductErrorCode.UNIQUE && err.field === "sku") { if (err?.code === ProductErrorCode.UNIQUE && err.field === "sku") {
return intl.formatMessage(messages.skuUnique); return intl.formatMessage(messages.skuUnique);
} }

View file

@ -19,7 +19,7 @@ const messages = defineMessages({
function getShippingErrorMessage( function getShippingErrorMessage(
err: Omit<ShippingErrorFragment, "__typename"> | undefined, err: Omit<ShippingErrorFragment, "__typename"> | undefined,
intl: IntlShape, intl: IntlShape,
): string { ): string | undefined {
if (err) { if (err) {
switch (err.code) { switch (err.code) {
case ShippingErrorCode.ALREADY_EXISTS: case ShippingErrorCode.ALREADY_EXISTS:

View file

@ -14,7 +14,7 @@ const messages = defineMessages({
function getShopErrorMessage( function getShopErrorMessage(
err: Omit<ShopErrorFragment, "__typename"> | undefined, err: Omit<ShopErrorFragment, "__typename"> | undefined,
intl: IntlShape, intl: IntlShape,
): string { ): string | undefined {
if (err) { if (err) {
switch (err.code) { switch (err.code) {
case ShopErrorCode.ALREADY_EXISTS: case ShopErrorCode.ALREADY_EXISTS:

View file

@ -6,7 +6,7 @@ import getAccountErrorMessage from "./account";
function getStaffErrorMessage( function getStaffErrorMessage(
err: StaffErrorFragment, err: StaffErrorFragment,
intl: IntlShape, intl: IntlShape,
): string { ): string | undefined {
return getAccountErrorMessage(err, intl); return getAccountErrorMessage(err, intl);
} }

View file

@ -20,7 +20,7 @@ const messages = defineMessages({
function getStockErrorMessage( function getStockErrorMessage(
err: Omit<StockErrorFragment, "__typename"> | undefined, err: Omit<StockErrorFragment, "__typename"> | undefined,
intl: IntlShape, intl: IntlShape,
): string { ): string | undefined {
if (err) { if (err) {
switch (err.code) { switch (err.code) {
case StockErrorCode.UNIQUE: case StockErrorCode.UNIQUE:
@ -34,7 +34,7 @@ function getStockErrorMessage(
export function getBulkStockErrorMessage( export function getBulkStockErrorMessage(
err: Omit<BulkStockErrorFragment, "__typename"> | undefined, err: Omit<BulkStockErrorFragment, "__typename"> | undefined,
intl: IntlShape, intl: IntlShape,
): string { ): string | undefined {
return getProductErrorMessage(err, intl); return getProductErrorMessage(err, intl);
} }

View file

@ -20,7 +20,7 @@ export type TaxClassError =
function getTaxesErrorMessage( function getTaxesErrorMessage(
err: Omit<TaxClassError, "__typename"> | undefined, err: Omit<TaxClassError, "__typename"> | undefined,
intl: IntlShape, intl: IntlShape,
): string { ): string | undefined {
return getCommonFormFieldErrorMessage(err, intl); return getCommonFormFieldErrorMessage(err, intl);
} }

View file

@ -14,14 +14,14 @@ const messages = defineMessages({
function getWarehouseErrorMessage( function getWarehouseErrorMessage(
err: Omit<WarehouseErrorFragment, "__typename"> | undefined, err: Omit<WarehouseErrorFragment, "__typename"> | undefined,
intl: IntlShape, intl: IntlShape,
): string { ): string | undefined {
return getCommonFormFieldErrorMessage(err, intl); return getCommonFormFieldErrorMessage(err, intl);
} }
export function getWarehouseSlugErrorMessage( export function getWarehouseSlugErrorMessage(
err: Omit<WarehouseErrorFragment, "__typename"> | undefined, err: Omit<WarehouseErrorFragment, "__typename"> | undefined,
intl: IntlShape, intl: IntlShape,
): string { ): string | undefined {
if (err) { if (err) {
switch (err.code) { switch (err.code) {
case WarehouseErrorCode.UNIQUE: case WarehouseErrorCode.UNIQUE:

View file

@ -6,7 +6,7 @@ import { getCommonFormFieldErrorMessage } from "./common";
function getWebhookErrorMessage( function getWebhookErrorMessage(
err: Omit<WebhookErrorFragment, "__typename"> | undefined, err: Omit<WebhookErrorFragment, "__typename"> | undefined,
intl: IntlShape, intl: IntlShape,
): string { ): string | undefined {
return getCommonFormFieldErrorMessage(err, intl); return getCommonFormFieldErrorMessage(err, intl);
} }

View file

@ -161,6 +161,6 @@ export function createBooleanField<K extends string>(
}, },
], ],
type: FieldType.boolean, type: FieldType.boolean,
value: [defaultValue?.toString()], value: [defaultValue?.toString() ?? ""],
}; };
} }

View file

@ -91,7 +91,7 @@ export function getSingleValueQueryParam<
>(param: FilterElement<TKey>, key: TUrlKey) { >(param: FilterElement<TKey>, key: TUrlKey) {
const { active, value } = param; const { active, value } = param;
if (!active) { if (!active || !value) {
return { return {
[key]: undefined, [key]: undefined,
}; };
@ -161,7 +161,7 @@ export function getMinMaxQueryParam<
>(param: FilterElement<TKey>, keyFrom: TUrlKey, keyTo: TUrlKey) { >(param: FilterElement<TKey>, keyFrom: TUrlKey, keyTo: TUrlKey) {
const { active, multiple, value } = param; const { active, multiple, value } = param;
if (!active) { if (!active || !value) {
return { return {
[keyFrom]: undefined, [keyFrom]: undefined,
[keyTo]: undefined, [keyTo]: undefined,

View file

@ -8,7 +8,8 @@ export type GetFilterTabsOutput<TUrlFilters> = Array<UserFilter<TUrlFilters>>;
function getFilterTabs<TUrlFilters>( function getFilterTabs<TUrlFilters>(
key: string, key: string,
): GetFilterTabsOutput<TUrlFilters> { ): GetFilterTabsOutput<TUrlFilters> {
return JSON.parse(localStorage.getItem(key)) || []; const filterTabs = localStorage.getItem(key);
return filterTabs ? JSON.parse(filterTabs) : [];
} }
function saveFilterTab<TUrlFilters>( function saveFilterTab<TUrlFilters>(

View file

@ -20,7 +20,7 @@ function createMetadataCreateHandler<T extends MetadataFormData, TError>(
return async (data: T) => { return async (data: T) => {
const { id, errors } = await create(data); const { id, errors } = await create(data);
if (id === null || !!errors?.length) { if (!id || !!errors?.length) {
return errors; return errors;
} }
@ -33,8 +33,8 @@ function createMetadataCreateHandler<T extends MetadataFormData, TError>(
}, },
}); });
const updateMetaErrors = [ const updateMetaErrors = [
...(updateMetaResult.data.deleteMetadata.errors || []), ...(updateMetaResult.data?.deleteMetadata?.errors || []),
...(updateMetaResult.data.updateMetadata.errors || []), ...(updateMetaResult.data?.updateMetadata?.errors || []),
]; ];
if (updateMetaErrors.length > 0) { if (updateMetaErrors.length > 0) {
@ -52,8 +52,8 @@ function createMetadataCreateHandler<T extends MetadataFormData, TError>(
}); });
const updatePrivateMetaErrors = [ const updatePrivateMetaErrors = [
...(updatePrivateMetaResult.data.deletePrivateMetadata.errors || []), ...(updatePrivateMetaResult.data?.deletePrivateMetadata?.errors || []),
...(updatePrivateMetaResult.data.updatePrivateMetadata.errors || []), ...(updatePrivateMetaResult.data?.updatePrivateMetadata?.errors || []),
]; ];
if (updatePrivateMetaErrors.length > 0) { if (updatePrivateMetaErrors.length > 0) {

View file

@ -62,8 +62,8 @@ function createMetadataUpdateHandler<TData extends MetadataFormData, TError>(
}); });
const updateMetaErrors = [ const updateMetaErrors = [
...(updateMetaResult.data.deleteMetadata.errors || []), ...(updateMetaResult.data?.deleteMetadata?.errors || []),
...(updateMetaResult.data.updateMetadata.errors || []), ...(updateMetaResult.data?.updateMetadata?.errors || []),
]; ];
if (updateMetaErrors.length > 0) { if (updateMetaErrors.length > 0) {
@ -84,8 +84,10 @@ function createMetadataUpdateHandler<TData extends MetadataFormData, TError>(
}); });
const updatePrivateMetaErrors = [ const updatePrivateMetaErrors = [
...(updatePrivateMetaResult.data.deletePrivateMetadata.errors || []), ...(updatePrivateMetaResult.data?.deletePrivateMetadata?.errors ||
...(updatePrivateMetaResult.data.updatePrivateMetadata.errors || []), []),
...(updatePrivateMetaResult.data?.updatePrivateMetadata?.errors ||
[]),
]; ];
if (updatePrivateMetaErrors.length > 0) { if (updatePrivateMetaErrors.length > 0) {

View file

@ -20,6 +20,10 @@ function createMultiAutocompleteSelectHandler(
const id = event.target.value; const id = event.target.value;
const choice = combinedChoices.find(choice => choice.value === id); const choice = combinedChoices.find(choice => choice.value === id);
if (!choice) {
return;
}
setSelected(toggle(choice, selected, (a, b) => a.value === b.value)); setSelected(toggle(choice, selected, (a, b) => a.value === b.value));
}; };
} }

View file

@ -8,10 +8,11 @@ import {
CountryWithCodeFragment, CountryWithCodeFragment,
MetadataInput, MetadataInput,
MetadataItemFragment, MetadataItemFragment,
SearchPagesQuery, PageFragment,
} from "@dashboard/graphql"; } from "@dashboard/graphql";
import { getFullName } from "@dashboard/misc"; import { getFullName } from "@dashboard/misc";
import { Node, RelayToFlat, SlugNode, TagNode } from "@dashboard/types"; import { Node, SlugNode, TagNode } from "@dashboard/types";
import { Choice } from "@saleor/macaw-ui";
interface Edge<T> { interface Edge<T> {
node: T; node: T;
@ -40,8 +41,8 @@ export function mapCountriesToChoices(countries: CountryWithCodeFragment[]) {
} }
export function mapPagesToChoices( export function mapPagesToChoices(
pages: RelayToFlat<SearchPagesQuery["search"]>, pages: Array<Pick<PageFragment, "title" | "id">>,
) { ): Choice[] {
return pages.map(page => ({ return pages.map(page => ({
label: page.title, label: page.title,
value: page.id, value: page.id,
@ -105,6 +106,10 @@ export function mapMultiValueNodeToChoice<T extends Record<string, any>>(
return (nodes as string[]).map(node => ({ label: node, value: node })); return (nodes as string[]).map(node => ({ label: node, value: node }));
} }
if (!key) {
return [];
}
return (nodes as T[]).map(node => ({ label: node[key], value: node[key] })); return (nodes as T[]).map(node => ({ label: node[key], value: node[key] }));
} }
@ -120,6 +125,10 @@ export function mapSingleValueNodeToChoice<T extends Record<string, any>>(
return (nodes as string[]).map(node => ({ label: node, value: node })); return (nodes as string[]).map(node => ({ label: node, value: node }));
} }
if (!key) {
return [];
}
return (nodes as T[]).map(node => ({ label: node[key], value: node[key] })); return (nodes as T[]).map(node => ({ label: node[key], value: node[key] }));
} }

View file

@ -27,9 +27,11 @@ export type IFlatMenu<TMenuData = {}, TValue = string> = Array<
export function validateMenuOptions<TMenuData = {}, TValue = string>( export function validateMenuOptions<TMenuData = {}, TValue = string>(
menu: IMenu<TMenuData, TValue>, menu: IMenu<TMenuData, TValue>,
): boolean { ): boolean {
const isValue = (val: TValue | undefined): val is TValue => val !== undefined;
const values: TValue[] = toFlat(menu) const values: TValue[] = toFlat(menu)
.map(menuItem => menuItem.value) .map(menuItem => menuItem.value)
.filter(value => value !== undefined); .filter(isValue);
const uniqueValues = Array.from(new Set(values)); const uniqueValues = Array.from(new Set(values));
return uniqueValues.length === values.length; return uniqueValues.length === values.length;
} }
@ -56,9 +58,9 @@ export function getMenuItemByValue<TMenuData = {}, TValue = string>(
value: TValue, value: TValue,
): IMenuItem<TMenuData, TValue> { ): IMenuItem<TMenuData, TValue> {
const flatMenu = toFlat(menu); const flatMenu = toFlat(menu);
const flatMenuItem: IFlatMenuItem<TMenuData, TValue> = flatMenu.find( const flatMenuItem:
menuItem => menuItem.value === value, | IFlatMenuItem<TMenuData, TValue>
); | undefined = flatMenu.find(menuItem => menuItem.value === value);
if (flatMenuItem === undefined) { if (flatMenuItem === undefined) {
throw new Error(`Value ${value} does not exist in menu`); throw new Error(`Value ${value} does not exist in menu`);
@ -94,6 +96,10 @@ function _walkToRoot<TMenuData = {}, TValue = string>(
): IFlatMenu<TMenuData, TValue> { ): IFlatMenu<TMenuData, TValue> {
const menuItem = flatMenu.find(menuItem => menuItem.id === parent); const menuItem = flatMenu.find(menuItem => menuItem.id === parent);
if (menuItem === undefined) {
throw new Error(`Value ${parent} does not exist in menu`);
}
if (menuItem.parent === null) { if (menuItem.parent === null) {
return [menuItem]; return [menuItem];
} }
@ -107,6 +113,10 @@ export function walkToRoot<TMenuData = {}, TValue = string>(
const flatMenu = toFlat(menu); const flatMenu = toFlat(menu);
const menuItem = flatMenu.find(menuItem => menuItem.value === value); const menuItem = flatMenu.find(menuItem => menuItem.value === value);
if (menuItem === undefined) {
throw new Error(`Value ${value} does not exist in menu`);
}
return (menuItem.parent === null return (menuItem.parent === null
? [menuItem] ? [menuItem]
: [menuItem, ..._walkToRoot(flatMenu, menuItem.parent)] : [menuItem, ..._walkToRoot(flatMenu, menuItem.parent)]
@ -116,7 +126,7 @@ export function walkToRoot<TMenuData = {}, TValue = string>(
function _toFlat<TMenuData = {}, TValue = string>( function _toFlat<TMenuData = {}, TValue = string>(
menuItem: IMenuItem<TMenuData, TValue>, menuItem: IMenuItem<TMenuData, TValue>,
sort: number, sort: number,
parent: string, parent: string | null,
): IFlatMenu<TMenuData, TValue> { ): IFlatMenu<TMenuData, TValue> {
const id = parent ? [parent, sort].join(":") : sort.toString(); const id = parent ? [parent, sort].join(":") : sort.toString();
const flatMenuItem: IFlatMenuItem<TMenuData, TValue> = { const flatMenuItem: IFlatMenuItem<TMenuData, TValue> = {

View file

@ -1,19 +1,27 @@
import { EditorCore } from "@dashboard/components/RichTextEditor"; import { EditorCore } from "@dashboard/components/RichTextEditor";
import { OutputData } from "@editorjs/editorjs"; import { OutputData } from "@editorjs/editorjs";
import { useMemo, useRef, useState } from "react"; import { MutableRefObject, useMemo, useRef, useState } from "react";
interface UseRichTextOptions { interface UseRichTextOptions {
initial: string | null; initial: string | null | undefined;
loading?: boolean; loading?: boolean;
triggerChange: () => void; triggerChange: () => void;
} }
interface UseRichTextResult {
editorRef: MutableRefObject<EditorCore | null>;
handleChange: () => void;
getValue: () => Promise<OutputData>;
defaultValue: OutputData | undefined;
isReadyForMount: boolean;
}
export function useRichText({ export function useRichText({
initial, initial,
loading, loading,
triggerChange, triggerChange,
}: UseRichTextOptions) { }: UseRichTextOptions): UseRichTextResult {
const editorRef = useRef<EditorCore>(null); const editorRef = useRef<EditorCore | null>(null);
const [isReadyForMount, setIsReadyForMount] = useState(false); const [isReadyForMount, setIsReadyForMount] = useState(false);
const handleChange = () => { const handleChange = () => {
@ -33,7 +41,7 @@ export function useRichText({
return; return;
} }
if (initial === undefined) { if (!initial) {
setIsReadyForMount(true); setIsReadyForMount(true);
return ""; return "";
} }

View file

@ -72,7 +72,7 @@ type GetSortQueryField<TUrlField extends string, TSortField extends string> = (
type GetSortQueryVariables< type GetSortQueryVariables<
TSortField extends string, TSortField extends string,
TParams extends Record<any, any> TParams extends Record<any, any>
> = (params: TParams) => SortingInput<TSortField>; > = (params: TParams) => SortingInput<TSortField> | undefined;
export function createGetSortQueryVariables< export function createGetSortQueryVariables<
TUrlField extends string, TUrlField extends string,
TSortField extends string, TSortField extends string,

View file

@ -1,10 +0,0 @@
export function getFooterColSpanWithBulkActions(
arr: any[],
numberOfColumns: number,
): number {
if (arr === undefined || arr.length > 0) {
return numberOfColumns + 1;
}
return numberOfColumns;
}

View file

@ -8,7 +8,9 @@ export function stringifyQs(params: unknown, arrayFormat?: string): string {
}); });
} }
export function getArrayQueryParam(param: string | string[]): string[] { export function getArrayQueryParam(
param: string | string[],
): string[] | undefined {
if (!param) { if (!param) {
return undefined; return undefined;
} }