Skip to content

Commit

Permalink
Add ability to properly cast a string, number, null to an integer (tw…
Browse files Browse the repository at this point in the history
  • Loading branch information
charlesBochet authored and AdityaPimpalkar committed Aug 3, 2023
1 parent fc6768c commit 70390ed
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ import { IconUsers } from '@/ui/icon';
import { InplaceInputText } from '@/ui/inplace-input/components/InplaceInputText';
import { RecoilScope } from '@/ui/recoil-scope/components/RecoilScope';
import { Company, useUpdateOneCompanyMutation } from '~/generated/graphql';
import {
canBeCastAsIntegerOrNull,
castAsIntegerOrNull,
} from '~/utils/cast-as-integer-or-null';

type OwnProps = {
company: Pick<Company, 'id' | 'employees'>;
Expand All @@ -27,30 +31,25 @@ export function CompanyEmployeesEditableField({ company }: OwnProps) {
}

async function handleSubmit() {
if (!internalValue) return;

try {
const numberValue = parseInt(internalValue);
if (!canBeCastAsIntegerOrNull(internalValue)) {
handleCancel();
return;
}

if (isNaN(numberValue)) {
throw new Error('Not a number');
}
const valueCastedAsNumberOrNull = castAsIntegerOrNull(internalValue);

await updateCompany({
variables: {
where: {
id: company.id,
},
data: {
employees: numberValue,
},
await updateCompany({
variables: {
where: {
id: company.id,
},
});
data: {
employees: valueCastedAsNumberOrNull,
},
},
});

setInternalValue(numberValue.toString());
} catch {
handleCancel();
}
setInternalValue(valueCastedAsNumberOrNull?.toString());
}

async function handleCancel() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@ import { EditableField } from '@/ui/editable-field/components/EditableField';
import { FieldContext } from '@/ui/editable-field/states/FieldContext';
import { InplaceInputText } from '@/ui/inplace-input/components/InplaceInputText';
import { RecoilScope } from '@/ui/recoil-scope/components/RecoilScope';
import {
canBeCastAsIntegerOrNull,
castAsIntegerOrNull,
} from '~/utils/cast-as-integer-or-null';

type OwnProps = {
icon?: React.ReactNode;
placeholder?: string;
value: number | null | undefined;
onSubmit?: (newValue: number) => void;
onSubmit?: (newValue: number | null) => void;
};

export function NumberEditableField({
Expand All @@ -29,26 +33,16 @@ export function NumberEditableField({
}

async function handleSubmit() {
if (!internalValue) return;

try {
const numberValue = parseInt(internalValue);

if (isNaN(numberValue)) {
throw new Error('Not a number');
}
if (!canBeCastAsIntegerOrNull(internalValue)) {
handleCancel();
return;
}

// TODO: find a way to store this better in DB
if (numberValue > 2000000000) {
throw new Error('Number too big');
}
const valueCastedAsNumberOrNull = castAsIntegerOrNull(internalValue);

onSubmit?.(numberValue);
onSubmit?.(valueCastedAsNumberOrNull);

setInternalValue(numberValue.toString());
} catch {
handleCancel();
}
setInternalValue(valueCastedAsNumberOrNull?.toString());
}

async function handleCancel() {
Expand Down
72 changes: 72 additions & 0 deletions front/src/utils/__tests__/cast-as-integer-or-null.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import {
canBeCastAsIntegerOrNull,
castAsIntegerOrNull,
} from '../cast-as-integer-or-null';

describe('canBeCastAsIntegerOrNull', () => {
it(`should return true if null`, () => {
expect(canBeCastAsIntegerOrNull(null)).toBeTruthy();
});

it(`should return true if number`, () => {
expect(canBeCastAsIntegerOrNull(9)).toBeTruthy();
});

it(`should return true if empty string`, () => {
expect(canBeCastAsIntegerOrNull('')).toBeTruthy();
});

it(`should return true if integer string`, () => {
expect(canBeCastAsIntegerOrNull('9')).toBeTruthy();
});

it(`should return false if undefined`, () => {
expect(canBeCastAsIntegerOrNull(undefined)).toBeFalsy();
});

it(`should return false if non numeric string`, () => {
expect(canBeCastAsIntegerOrNull('9a')).toBeFalsy();
});

it(`should return false if non numeric string #2`, () => {
expect(canBeCastAsIntegerOrNull('a9a')).toBeFalsy();
});

it(`should return false if float`, () => {
expect(canBeCastAsIntegerOrNull(0.9)).toBeFalsy();
});

it(`should return false if float string`, () => {
expect(canBeCastAsIntegerOrNull('0.9')).toBeFalsy();
});
});

describe('castAsIntegerOrNull', () => {
it(`should cast null to null`, () => {
expect(castAsIntegerOrNull(null)).toBe(null);
});

it(`should cast empty string to null`, () => {
expect(castAsIntegerOrNull('')).toBe(null);
});

it(`should cast an integer to an integer`, () => {
expect(castAsIntegerOrNull(9)).toBe(9);
});

it(`should cast an integer string to an integer`, () => {
expect(castAsIntegerOrNull('9')).toBe(9);
});

it(`should throw if trying to cast a float string to an integer`, () => {
expect(() => castAsIntegerOrNull('9.9')).toThrow(Error);
});

it(`should throw if trying to cast a non numeric string to an integer`, () => {
expect(() => castAsIntegerOrNull('9.9a')).toThrow(Error);
});

it(`should throw if trying to cast an undefined to an integer`, () => {
expect(() => castAsIntegerOrNull(undefined)).toThrow(Error);
});
});
58 changes: 58 additions & 0 deletions front/src/utils/cast-as-integer-or-null.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
export function canBeCastAsIntegerOrNull(
probableNumberOrNull: string | undefined | number | null,
): probableNumberOrNull is number | null {
if (probableNumberOrNull === undefined) {
return false;
}

if (typeof probableNumberOrNull === 'number') {
return Number.isInteger(probableNumberOrNull);
}

if (probableNumberOrNull === null) {
return true;
}

if (probableNumberOrNull === '') {
return true;
}

if (typeof probableNumberOrNull === 'string') {
const stringAsNumber = +probableNumberOrNull;

if (isNaN(stringAsNumber)) {
return false;
}
if (Number.isInteger(stringAsNumber)) {
return true;
}
}

return false;
}

export function castAsIntegerOrNull(
probableNumberOrNull: string | undefined | number | null,
): number | null {
if (canBeCastAsIntegerOrNull(probableNumberOrNull) === false) {
throw new Error('Cannot cast to number or null');
}

if (probableNumberOrNull === null) {
return null;
}

if (probableNumberOrNull === '') {
return null;
}

if (typeof probableNumberOrNull === 'number') {
return probableNumberOrNull;
}

if (typeof probableNumberOrNull === 'string') {
return +probableNumberOrNull;
}

return null;
}

0 comments on commit 70390ed

Please sign in to comment.