Skip to content

Commit

Permalink
feature(@leav/ui): Handle 'read mode' on record edit form (#584)
Browse files Browse the repository at this point in the history
  • Loading branch information
TdyP authored Oct 16, 2024
1 parent 0ea9d8e commit 2fe80c7
Show file tree
Hide file tree
Showing 50 changed files with 1,089 additions and 783 deletions.
22 changes: 12 additions & 10 deletions apps/core/src/domain/actions/formatDateAction.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright LEAV Solutions 2017
// This file is released under LGPL V3
// License text available at https://www.gnu.org/licenses/lgpl-3.0.txt
import {IStandardValue} from '_types/value';
import {AttributeFormats, AttributeTypes, IAttribute} from '../../_types/attribute';
import formatDateAction from './formatDateAction';

Expand All @@ -10,6 +11,7 @@ describe('formatDateAction', () => {
const ctx = {attribute: attrText, userId: 'test_user'};

const testingDate = 2119477320;
const testValue: IStandardValue = {payload: testingDate, raw_payload: testingDate};

describe('Localized format', () => {
test('with options', async () => {
Expand All @@ -23,7 +25,7 @@ describe('formatDateAction', () => {
expect(
(
await action(
[{payload: testingDate}],
[testValue],
{localized},
{
...ctx,
Expand All @@ -35,7 +37,7 @@ describe('formatDateAction', () => {
expect(
(
await action(
[{payload: testingDate}],
[testValue],
{localized},
{
...ctx,
Expand All @@ -47,7 +49,7 @@ describe('formatDateAction', () => {
expect(
(
await action(
[{payload: testingDate}],
[testValue],
{localized},
{
...ctx,
Expand All @@ -63,7 +65,7 @@ describe('formatDateAction', () => {
'auto should print default on invalid json format: `%s`',
async localized => {
const result = await action(
[{payload: testingDate}],
[testValue],
{localized},
{
...ctx,
Expand All @@ -84,7 +86,7 @@ describe('formatDateAction', () => {
expect(
(
await action(
[{payload: testingDate}],
[testValue],
{
universal: 'D/MMMM-YY HH:mm'
},
Expand All @@ -96,23 +98,23 @@ describe('formatDateAction', () => {

describe('edge cases', () => {
test('should fallback to empty localized param when neither params provided', async () => {
const resultWithoutParams = await action([{payload: testingDate}], {}, ctx);
const resultWithEmptyLocalizedParam = await action([{payload: testingDate}], {localized: '{}'}, ctx);
const resultWithoutParams = await action([testValue], {}, ctx);
const resultWithEmptyLocalizedParam = await action([testValue], {localized: '{}'}, ctx);
expect(resultWithoutParams).toEqual(resultWithEmptyLocalizedParam);
});
test('should return empty string on non numerical value in DB', async () => {
expect((await action([{payload: 'aaaa'}], {}, ctx)).values[0].payload).toBe('');
expect((await action([{payload: 'aaaa', raw_payload: 'aaa'}], {}, ctx)).values[0].payload).toBe('');
});
test('should return null on null value in DB', async () => {
expect((await action([{payload: null}], {}, ctx)).values[0].payload).toBe(null);
expect((await action([{payload: null, raw_payload: null}], {}, ctx)).values[0].payload).toBe(null);
});
});

test('localized override universal format', async () => {
expect(
(
await action(
[{payload: testingDate}],
[testValue],
{
universal: 'D/MMMM/YY',
localized: `{
Expand Down
20 changes: 15 additions & 5 deletions apps/core/src/domain/actions/formatDateAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import moment from 'moment';
import {ActionsListIOTypes, IActionsListFunction, IActionsListFunctionResult} from '../../_types/actionsList';
import {Errors} from '../../_types/errors';
import cloneDeep from 'lodash/cloneDeep';
import {TypeGuards} from '../../utils';

const defaultValueLocalizedParam = `{
"weekday": "long",
Expand Down Expand Up @@ -40,14 +42,23 @@ export default function (): IActionsListFunction<{localized: false; universal: f
action: (values, {localized, universal}, {lang}) => {
const errors: IActionsListFunctionResult['errors'] = [];

const formattedValues = values.map(elementValue => {
const formattedValues = cloneDeep(values).map(elementValue => {
if (!TypeGuards.isIStandardValue(elementValue)) {
errors.push({
errorType: Errors.INVALID_VALUES,
attributeValue: elementValue,
message: 'Non standard value received in formatDate.'
});
return elementValue;
}

if ('raw_value' in elementValue) {
elementValue.payload = elementValue.raw_value;
}
if (elementValue.payload === null) {
return elementValue;
}
const numberVal = Number(elementValue.payload);
const numberVal = Number(elementValue.raw_payload);

if (isNaN(numberVal)) {
elementValue.payload = '';
Expand All @@ -61,9 +72,8 @@ export default function (): IActionsListFunction<{localized: false; universal: f

let options: Intl.DateTimeFormatOptions = {};
try {
options = JSON.parse(localized ?? {});
options = JSON.parse(localized ?? '{}');
} catch (e) {
// TODO: rise error to inform user without break app
errors.push({
errorType: Errors.FORMAT_ERROR,
attributeValue: {payload: localized},
Expand All @@ -76,7 +86,7 @@ export default function (): IActionsListFunction<{localized: false; universal: f
return elementValue;
});

return {values: formattedValues, errors: []};
return {values: formattedValues, errors};
}
};
}
108 changes: 67 additions & 41 deletions apps/core/src/domain/actions/formatDateRangeAction.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright LEAV Solutions 2017
// This file is released under LGPL V3
// License text available at https://www.gnu.org/licenses/lgpl-3.0.txt
import {IStandardValue} from '_types/value';
import {AttributeFormats, AttributeTypes, IAttribute} from '../../_types/attribute';
import formatDateRangeAction from './formatDateRangeAction';

Expand All @@ -10,6 +11,7 @@ describe('formatDateRangeAction', () => {
const ctx = {attribute: attrText, userId: 'test_user'};

const testingRangeDate = {from: '2119477320', to: '2119477380'};
const testValue: IStandardValue = {payload: testingRangeDate, raw_payload: testingRangeDate};

describe('Localized format', () => {
test('with options', async () => {
Expand All @@ -21,37 +23,37 @@ describe('formatDateRangeAction', () => {
"minute": "2-digit"
}`;

expect(
(
await action(
[{payload: testingRangeDate}],
{localized},
{
...ctx,
lang: 'en-GB'
}
)
).values[0].payload
).toEqual({from: '28 February 37 at 23:42', to: '28 February 37 at 23:43'});
expect(
(
await action(
[{payload: testingRangeDate}],
{localized},
{
...ctx,
lang: 'ko-KR'
}
)
).values[0].payload
).toEqual({from: '37년 2월 28일 오후 11:42', to: '37년 2월 28일 오후 11:43'});
const resEnGb = await action(
[testValue],
{localized},
{
...ctx,
lang: 'en-GB'
}
);
expect(resEnGb.errors).toEqual([]);
expect(resEnGb.values[0].payload).toEqual({from: '28 February 37 at 23:42', to: '28 February 37 at 23:43'});

const resKoKr = await action(
[testValue],
{localized},
{
...ctx,
lang: 'ko-KR'
}
);
expect(resKoKr.errors).toEqual([]);
expect(resKoKr.values[0].payload).toEqual({
from: '37년 2월 28일 오후 11:42',
to: '37년 2월 28일 오후 11:43'
});
});

test.skip.each(['{', '{withoutDoubleQuote: true}', '', '{"params1": "long", "param2": "too many coma",}'])(
'auto should print default on invalid json format: `%s`',
async localized => {
const result = await action(
[{payload: testingRangeDate}],
[testValue],
{localized},
{
...ctx,
Expand All @@ -72,7 +74,7 @@ describe('formatDateRangeAction', () => {
});

test('Universal format', async () => {
const result = await action([{payload: testingRangeDate}], {universal: 'D/MMMM-YY HH:mm'}, ctx);
const result = await action([testValue], {universal: 'D/MMMM-YY HH:mm'}, ctx);
const formattedRangeDate = result.values[0].payload as {
from: string;
to: string;
Expand All @@ -83,30 +85,54 @@ describe('formatDateRangeAction', () => {

describe('edge cases', () => {
test('should return null value if properties are omitted', async () => {
expect((await action([{payload: 'aaaa'}], {}, ctx)).values[0].payload).toBe(null);
expect((await action([{payload: {}}], {}, ctx)).values[0].payload).toBe(null);
expect((await action([{payload: {unknownProperty: null}}], {}, ctx)).values[0].payload).toBe(null);
expect((await action([{payload: {from: '2119477320'}}], {}, ctx)).values[0].payload).toBe(null);
expect((await action([{payload: {to: '2119477320'}}], {}, ctx)).values[0].payload).toBe(null);
expect((await action([{payload: null}], {}, ctx)).values[0].payload).toBe(null);
expect((await action([{payload: 'aaaa', raw_payload: 'aaa'}], {}, ctx)).values[0].payload).toBe(null);
expect((await action([{payload: {}, raw_payload: {}}], {}, ctx)).values[0].payload).toBe(null);
expect(
(await action([{payload: {unknownProperty: null}, raw_payload: {unknownProperty: null}}], {}, ctx))
.values[0].payload
).toBe(null);
expect(
(await action([{payload: {from: '2119477320'}, raw_payload: {from: '2119477320'}}], {}, ctx)).values[0]
.payload
).toBe(null);
expect(
(await action([{payload: {to: '2119477320'}, raw_payload: {to: '2119477320'}}], {}, ctx)).values[0]
.payload
).toBe(null);
expect((await action([{payload: null, raw_payload: null}], {}, ctx)).values[0].payload).toBe(null);
});
test('should return empty string couple on non numerical value in DB', async () => {
expect((await action([{payload: {from: 'aaaa', to: '2119477320'}}], {}, ctx)).values[0].payload).toEqual([
'',
''
]);
expect((await action([{payload: {from: '2119477320', to: 'aaaa'}}], {}, ctx)).values[0].payload).toEqual([
'',
''
]);
expect(
(
await action(
[{payload: {from: 'aaaa', to: '2119477320'}, raw_payload: {from: 'aaaa', to: '2119477320'}}],
{},
ctx
)
).values[0].payload
).toEqual(['', '']);
expect(
(
await action(
[{payload: {from: '2119477320', to: 'aaaa'}, raw_payload: {from: '2119477320', to: 'aaaa'}}],
{},
ctx
)
).values[0].payload
).toEqual(['', '']);
});
});

test('localized override universal format', async () => {
expect(
(
await action(
[{payload: {from: '2119477320', to: '2119477380'}}],
[
{
payload: {from: '2119477320', to: '2119477380'},
raw_payload: {from: '2119477320', to: '2119477380'}
}
],
{
universal: 'D/MMMM/YY',
localized: `{
Expand Down
19 changes: 14 additions & 5 deletions apps/core/src/domain/actions/formatDateRangeAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import moment from 'moment';
import {IDateRangeValue} from '_types/value';
import {ActionsListIOTypes, IActionsListFunction, IActionsListFunctionResult} from '../../_types/actionsList';
import {Errors} from '../../_types/errors';
import cloneDeep from 'lodash/cloneDeep';
import {TypeGuards} from '../../utils/typeGuards';

const defaultValueLocalizedParam = `{
"weekday": "long",
Expand Down Expand Up @@ -41,8 +43,16 @@ export default function (): IActionsListFunction<{localized: false; universal: f
action: (values, {localized, universal}, {lang}) => {
const errors: IActionsListFunctionResult['errors'] = [];

const formattedValues = values.map(elementValue => {
const dateRangeValue = elementValue.payload as IDateRangeValue<number>;
const formattedValues = cloneDeep(values).map(elementValue => {
if (!TypeGuards.isIStandardValue(elementValue)) {
errors.push({
errorType: Errors.INVALID_VALUES,
attributeValue: elementValue,
message: 'Non standard value received in formatDateRange.'
});
return elementValue;
}
const dateRangeValue = elementValue.raw_payload as IDateRangeValue<number>;

if (dateRangeValue === null || !dateRangeValue.from || !dateRangeValue.to) {
return {...dateRangeValue, payload: null};
Expand All @@ -66,9 +76,8 @@ export default function (): IActionsListFunction<{localized: false; universal: f

let options: Intl.DateTimeFormatOptions = {};
try {
options = JSON.parse(localized ?? {});
options = JSON.parse(localized ?? '{}');
} catch (e) {
// TODO: rise error to inform user without break app
errors.push({
errorType: Errors.FORMAT_ERROR,
attributeValue: {payload: localized},
Expand All @@ -84,7 +93,7 @@ export default function (): IActionsListFunction<{localized: false; universal: f
return elementValue;
});

return {values: formattedValues, errors: []};
return {values: formattedValues, errors};
}
};
}
3 changes: 0 additions & 3 deletions apps/core/src/domain/record/recordDomain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1189,9 +1189,6 @@ export default function ({
];
}

const forceArray = options?.forceArray ?? false;

//TODO: fix "[object]" on input after edit
let formattedValues = await Promise.all(
values.map(async v => {
const formattedValue = await valueDomain.formatValue({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ describe('AttributeSimpleLinkRepo', () => {

expect(values[0]).toMatchObject({
id_value: null,
value: {
payload: {
id: 987654,
created_at: 1521475225,
modified_at: 1521475225
Expand Down Expand Up @@ -258,7 +258,7 @@ describe('AttributeSimpleLinkRepo', () => {

expect(values[0]).toMatchObject({
id_value: null,
value: {
payload: {
id: 987654,
created_at: 1521475225,
modified_at: 1521475225
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ export default function ({
.map(r => ({
id_value: null,
library: attribute.linked_library,
value: dbUtils.cleanup({...r, library: attribute.linked_library}),
payload: dbUtils.cleanup({...r, library: attribute.linked_library}),
created_by: null,
modified_by: null
}));
Expand Down
2 changes: 2 additions & 0 deletions libs/ui/src/__mocks__/common/form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ const formElementBase = {
{
value: 'My value formatted',
raw_value: 'my_raw_value',
payload: 'My value formatted',
raw_payload: 'my_raw_payload',
created_at: 123456789,
modified_at: 123456789,
created_by: mockModifier,
Expand Down
Loading

0 comments on commit 2fe80c7

Please sign in to comment.