Skip to content

Commit

Permalink
change path to JSON pointer
Browse files Browse the repository at this point in the history
  • Loading branch information
LukasBoll committed Aug 7, 2023
1 parent e80a873 commit c2adb17
Show file tree
Hide file tree
Showing 39 changed files with 441 additions and 377 deletions.
13 changes: 6 additions & 7 deletions packages/angular-material/src/other/master-detail/master.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export const removeSchemaKeywords = (path: string) => {
path
.split('/')
.filter((s) => !some(keywords, (key) => key === s))
.join('.')
.join('/')
);
};

Expand Down Expand Up @@ -87,7 +87,7 @@ export const removeSchemaKeywords = (path: string) => {
<button
mat-icon-button
class="button hide"
(click)="onDeleteClick(i)"
(click)="onDeleteClick($event, i)"
[ngClass]="{ show: highlightedIdx == i }"
*ngIf="isEnabled()"
>
Expand Down Expand Up @@ -206,16 +206,14 @@ export class MasterListComponent
this.translations = props.translations;

const masterItems = (data || []).map((d: any, index: number) => {
const labelRefInstancePath =
controlElement.options?.labelRef &&
removeSchemaKeywords(controlElement.options.labelRef);
const labelRefInstancePath = controlElement.options?.labelRef;
const isPrimitive = d !== undefined && typeof d !== 'object';
const masterItem = {
label: isPrimitive
? d.toString()
: get(d, labelRefInstancePath ?? getFirstPrimitiveProp(schema)),
data: d,
path: `${path}.${index}`,
path: `${path}/${index}`,
schema,
uischema: detailUISchema,
};
Expand Down Expand Up @@ -266,7 +264,8 @@ export class MasterListComponent
this.addItem(this.propsPath, createDefaultValue(this.scopedSchema))();
}

onDeleteClick(item: number) {
onDeleteClick(e: any, item: number) {
e.stopPropagation();
this.removeItems(this.propsPath, [item])();
}

Expand Down
6 changes: 3 additions & 3 deletions packages/angular-material/test/master-detail.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ describe('Master detail', () => {
// delete 1st item
spyOn(component, 'removeItems').and.callFake(() => () => {
getJsonFormsService(component).updateCore(
Actions.update('orders', () => moreData.orders.slice(1))
Actions.update('/orders', () => moreData.orders.slice(1))
);
fixture.detectChanges();
});
Expand Down Expand Up @@ -273,7 +273,7 @@ describe('Master detail', () => {
const copy = moreData.orders.slice();
copy.splice(1, 1);
getJsonFormsService(component).updateCore(
Actions.update('orders', () => copy)
Actions.update('/orders', () => copy)
);
fixture.detectChanges();
});
Expand Down Expand Up @@ -388,7 +388,7 @@ describe('Master detail', () => {
customer: { name: 'ACME' },
title: 'Carrots',
},
path: 'orders.0',
path: '/orders/0',
schema: schema.definitions.order,
uischema: {
type: 'VerticalLayout',
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/i18n/i18nUtil.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { ErrorObject } from 'ajv';
import { isInternationalized, Labelable, UISchemaElement } from '../models';
import { getControlPath } from '../reducers';
import { formatErrorMessage } from '../util';
import { formatErrorMessage, toLodashPath } from '../util';
import type { i18nJsonSchema, ErrorTranslator, Translator } from './i18nTypes';
import {
ArrayDefaultTranslation,
Expand All @@ -28,7 +28,7 @@ export const getI18nKeyPrefixBySchema = (
*/
export const transformPathToI18nPrefix = (path: string): string => {
return (
path
toLodashPath(path)
?.split('.')
.filter((segment) => !/^\d+$/.test(segment))
.join('.') || 'root'
Expand Down Expand Up @@ -159,7 +159,7 @@ export const deriveLabelForUISchemaElement = (
const i18nKeyPrefix = getI18nKeyPrefixBySchema(undefined, uischema);
const i18nKey =
typeof i18nKeyPrefix === 'string'
? `${i18nKeyPrefix}.label`
? `${i18nKeyPrefix}/label`
: stringifiedLabel;
return t(i18nKey, stringifiedLabel, { uischema: uischema });
};
Expand Down
27 changes: 13 additions & 14 deletions packages/core/src/reducers/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,13 @@ import {
UPDATE_CORE,
UpdateCoreAction,
} from '../actions';
import { createAjv, decode, isOneOfEnumSchema, Reducer } from '../util';
import {
composePaths,
createAjv,
isOneOfEnumSchema,
Reducer,
toLodashSegments,
} from '../util';
import type { JsonSchema, UISchemaElement } from '../models';

export const validate = (
Expand Down Expand Up @@ -284,18 +290,19 @@ export const coreReducer: Reducer<JsonFormsCore, CoreActions> = (
errors,
};
} else {
const oldData: any = get(state.data, action.path);
const lodashDataPathSegments = toLodashSegments(action.path);
const oldData: any = get(state.data, lodashDataPathSegments);
const newData = action.updater(cloneDeep(oldData));
let newState: any;
if (newData !== undefined) {
newState = setFp(
action.path,
lodashDataPathSegments,
newData,
state.data === undefined ? {} : state.data
);
} else {
newState = unsetFp(
action.path,
lodashDataPathSegments,
state.data === undefined ? {} : state.data
);
}
Expand Down Expand Up @@ -369,19 +376,11 @@ export const getControlPath = (error: ErrorObject) => {
// With AJV v8 the property was renamed to 'instancePath'
let controlPath = (error as any).dataPath || error.instancePath || '';

// change '/' chars to '.'
controlPath = controlPath.replace(/\//g, '.');

const invalidProperty = getInvalidProperty(error);
if (invalidProperty !== undefined && !controlPath.endsWith(invalidProperty)) {
controlPath = `${controlPath}.${invalidProperty}`;
controlPath = composePaths(controlPath, invalidProperty);
}

// remove '.' chars at the beginning of paths
controlPath = controlPath.replace(/^./, '');

// decode JSON Pointer escape sequences
controlPath = decode(controlPath);
return controlPath;
};

Expand Down Expand Up @@ -479,5 +478,5 @@ export const errorAt = (instancePath: string, schema: JsonSchema) =>
getErrorsAt(instancePath, schema, (path) => path === instancePath);
export const subErrorsAt = (instancePath: string, schema: JsonSchema) =>
getErrorsAt(instancePath, schema, (path) =>
path.startsWith(instancePath + '.')
path.startsWith(instancePath + '/')
);
23 changes: 21 additions & 2 deletions packages/core/src/reducers/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,19 @@
import {
defaultErrorTranslator,
defaultTranslator,
ErrorTranslator,
JsonFormsI18nState,
Translator,
} from '../i18n';
import {
I18nActions,
SET_LOCALE,
SET_TRANSLATOR,
UPDATE_I18N,
} from '../actions';
import type { Reducer } from '../util';
import { Reducer, toLodashPath } from '../util';
import { ErrorObject } from 'ajv';
import { UISchemaElement } from '../models';

export const defaultJsonFormsI18nState: Required<JsonFormsI18nState> = {
locale: 'en',
Expand All @@ -53,7 +57,6 @@ export const i18nReducer: Reducer<JsonFormsI18nState, I18nActions> = (
action.translator ?? defaultJsonFormsI18nState.translate;
const translateError =
action.errorTranslator ?? defaultJsonFormsI18nState.translateError;

if (
locale !== state.locale ||
translate !== state.translate ||
Expand Down Expand Up @@ -84,6 +87,22 @@ export const i18nReducer: Reducer<JsonFormsI18nState, I18nActions> = (
}
};

export const wrapTranslateFunction = (translator: Translator): Translator => {
return (id: string, defaultMessage?: string | undefined, values?: any) => {
return translator(toLodashPath(id), defaultMessage, values);
};
};

export const wrapErrorTranslateFunction = (
translator: ErrorTranslator
): ErrorTranslator => {
return (
error: ErrorObject,
translate: Translator,
uischema?: UISchemaElement
) => translator(error, wrapTranslateFunction(translate), uischema);
};

export const fetchLocale = (state?: JsonFormsI18nState) => {
if (state === undefined) {
return undefined;
Expand Down
32 changes: 22 additions & 10 deletions packages/core/src/util/path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,17 @@ import range from 'lodash/range';
import { isScoped, Scopable } from '../models';

export const compose = (path1: string, path2: string) => {
let p1 = path1;
if (!isEmpty(path1) && !isEmpty(path2) && !path2.startsWith('[')) {
p1 = path1 + '.';
let p2 = path2;
if (!isEmpty(path2) && !path2.startsWith('[') && !path2.startsWith('/')) {
p2 = '/' + path2;
}

if (isEmpty(p1)) {
return path2;
if (isEmpty(path1)) {
return p2;
} else if (isEmpty(path2)) {
return p1;
return path1;
} else {
return `${p1}${path2}`;
return `${path1}${p2}`;
}
};

Expand Down Expand Up @@ -76,13 +76,13 @@ export const toDataPathSegments = (schemaPath: string): string[] => {
* Data paths can be used in field change event handlers like handleChange.
*
* @example
* toDataPath('#/properties/foo/properties/bar') === 'foo.bar')
* toDataPath('#/properties/foo/properties/bar') === '/foo/bar')
*
* @param {string} schemaPath the schema path to be converted
* @returns {string} the data path
*/
export const toDataPath = (schemaPath: string): string => {
return toDataPathSegments(schemaPath).join('.');
return '/' + toDataPathSegments(schemaPath).join('/');
};

export const composeWithUi = (scopableUi: Scopable, path: string): string => {
Expand All @@ -96,7 +96,19 @@ export const composeWithUi = (scopableUi: Scopable, path: string): string => {
return path ?? '';
}

return compose(path, segments.join('.'));
return compose(path, '/' + segments.join('/'));
};

export const toLodashSegments = (jsonPointer: string): string[] => {
let path = jsonPointer;
if (jsonPointer && jsonPointer.startsWith('/')) {
path = jsonPointer.substring(1);
}
return path ? path.split('/').map(decode) : [];
};

export const toLodashPath = (jsonPointer: string) => {
return toLodashSegments(jsonPointer).join('.');
};

/**
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/util/resolvers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export const resolveData = (instance: any, dataPath: string): any => {
if (isEmpty(dataPath)) {
return instance;
}
const dataPathSegments = dataPath.split('.');
const dataPathSegments = dataPath.split('/').slice(1);

return dataPathSegments.reduce((curInstance, decodedSegment) => {
if (
Expand Down
5 changes: 3 additions & 2 deletions packages/core/src/util/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,9 @@ export const Resolve: {
};

// Paths --
const fromScoped = (scopable: Scoped): string =>
toDataPathSegments(scopable.scope).join('.');
const fromScoped = (scopable: Scoped): string => {
return '/' + toDataPathSegments(scopable.scope).join('/');
};

export const Paths = {
compose: composePaths,
Expand Down
20 changes: 10 additions & 10 deletions packages/core/test/i18n/i18nUtil.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,19 @@ test('transformPathToI18nPrefix returns root when empty', (t) => {
});

test('transformPathToI18nPrefix does not modify non-array paths', (t) => {
t.is(transformPathToI18nPrefix('foo'), 'foo');
t.is(transformPathToI18nPrefix('foo.bar'), 'foo.bar');
t.is(transformPathToI18nPrefix('bar3.foo2'), 'bar3.foo2');
t.is(transformPathToI18nPrefix('/foo'), 'foo');
t.is(transformPathToI18nPrefix('/foo/bar'), 'foo.bar');
t.is(transformPathToI18nPrefix('/bar3/foo2'), 'bar3.foo2');
});

test('transformPathToI18nPrefix removes array indices', (t) => {
t.is(transformPathToI18nPrefix('foo.2.bar'), 'foo.bar');
t.is(transformPathToI18nPrefix('foo.234324234.bar'), 'foo.bar');
t.is(transformPathToI18nPrefix('foo.0.bar'), 'foo.bar');
t.is(transformPathToI18nPrefix('foo.0.bar.1.foobar'), 'foo.bar.foobar');
t.is(transformPathToI18nPrefix('3.foobar'), 'foobar');
t.is(transformPathToI18nPrefix('foobar.3'), 'foobar');
t.is(transformPathToI18nPrefix('foo1.23.b2ar3.1.5.foo'), 'foo1.b2ar3.foo');
t.is(transformPathToI18nPrefix('foo/2/bar'), 'foo.bar');
t.is(transformPathToI18nPrefix('foo/234324234/bar'), 'foo.bar');
t.is(transformPathToI18nPrefix('foo/0/bar'), 'foo.bar');
t.is(transformPathToI18nPrefix('foo/0/bar/1/foobar'), 'foo.bar.foobar');
t.is(transformPathToI18nPrefix('3/foobar'), 'foobar');
t.is(transformPathToI18nPrefix('foobar/3'), 'foobar');
t.is(transformPathToI18nPrefix('foo1/23/b2ar3/1/5/foo'), 'foo1.b2ar3.foo');
t.is(transformPathToI18nPrefix('3'), 'root');
});

Expand Down
Loading

0 comments on commit c2adb17

Please sign in to comment.