Skip to content

Commit

Permalink
feat(admin-ui): Support "required" & "defaultValue" in ConfigArgs
Browse files Browse the repository at this point in the history
Relates to #643
  • Loading branch information
michaelbromley committed Jan 27, 2021
1 parent 92ae819 commit 6e5e482
Show file tree
Hide file tree
Showing 9 changed files with 63 additions and 45 deletions.
6 changes: 5 additions & 1 deletion packages/admin-ui/src/lib/core/src/common/generated-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2689,6 +2689,8 @@ export type ConfigArgDefinition = {
name: Scalars['String'];
type: Scalars['String'];
list: Scalars['Boolean'];
required: Scalars['Boolean'];
defaultValue?: Maybe<Scalars['String']>;
label?: Maybe<Scalars['String']>;
description?: Maybe<Scalars['String']>;
ui?: Maybe<Scalars['JSON']>;
Expand All @@ -2715,6 +2717,7 @@ export type DeletionResponse = {

export type ConfigArgInput = {
name: Scalars['String'];
/** A JSON stringified representation of the actual value */
value: Scalars['String'];
};

Expand Down Expand Up @@ -3728,6 +3731,7 @@ export type OrderAddress = {
country?: Maybe<Scalars['String']>;
countryCode?: Maybe<Scalars['String']>;
phoneNumber?: Maybe<Scalars['String']>;
customFields?: Maybe<Scalars['JSON']>;
};

export type OrderList = PaginatedList & {
Expand Down Expand Up @@ -7441,7 +7445,7 @@ export type ConfigurableOperationDefFragment = (
& Pick<ConfigurableOperationDefinition, 'code' | 'description'>
& { args: Array<(
{ __typename?: 'ConfigArgDefinition' }
& Pick<ConfigArgDefinition, 'name' | 'type' | 'list' | 'ui' | 'label'>
& Pick<ConfigArgDefinition, 'name' | 'type' | 'required' | 'defaultValue' | 'list' | 'ui' | 'label'>
)> }
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,28 +77,28 @@ export function toConfigurableOperationInput(
};
}

export function configurableOperationValueIsValid(
def?: ConfigurableOperationDefinition,
value?: { code: string; args: { [key: string]: string } },
) {
if (!def || !value) {
return false;
}
if (def.code !== value.code) {
return false;
}
for (const argDef of def.args) {
const argVal = value.args[argDef.name];
if (argDef.required && (argVal == null || argVal === '' || argVal === '0')) {
return false;
}
}
return true;
}

/**
* Returns a default value based on the type of the config arg.
*/
export function getDefaultConfigArgValue(arg: ConfigArgDefinition): any {
return arg.list ? [] : getDefaultConfigArgSingleValue(arg.type as ConfigArgType);
}

export function getDefaultConfigArgSingleValue(type: ConfigArgType | CustomFieldType): any {
switch (type) {
case 'boolean':
return 'false';
case 'int':
case 'float':
return '0';
case 'ID':
return '';
case 'string':
case 'localeString':
return '';
case 'datetime':
return new Date();
default:
assertNever(type);
}
return arg.list ? [] : arg.defaultValue || null; // getDefaultConfigArgSingleValue(arg.type as ConfigArgType);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { interpolateDescription } from './interpolate-description';
describe('interpolateDescription()', () => {
it('works for single argument', () => {
const operation: Partial<ConfigurableOperationDefinition> = {
args: [{ name: 'foo', type: 'string', list: false }],
args: [{ name: 'foo', type: 'string', list: false, required: false }],
description: 'The value is { foo }',
};
const result = interpolateDescription(operation as any, { foo: 'val' });
Expand All @@ -16,8 +16,8 @@ describe('interpolateDescription()', () => {
it('works for multiple arguments', () => {
const operation: Partial<ConfigurableOperationDefinition> = {
args: [
{ name: 'foo', type: 'string', list: false },
{ name: 'bar', type: 'string', list: false },
{ name: 'foo', type: 'string', list: false, required: false },
{ name: 'bar', type: 'string', list: false, required: false },
],
description: 'The value is { foo } and { bar }',
};
Expand All @@ -28,7 +28,7 @@ describe('interpolateDescription()', () => {

it('is case-insensitive', () => {
const operation: Partial<ConfigurableOperationDefinition> = {
args: [{ name: 'foo', type: 'string', list: false }],
args: [{ name: 'foo', type: 'string', list: false, required: false }],
description: 'The value is { FOo }',
};
const result = interpolateDescription(operation as any, { foo: 'val' });
Expand All @@ -39,8 +39,8 @@ describe('interpolateDescription()', () => {
it('ignores whitespaces in interpolation', () => {
const operation: Partial<ConfigurableOperationDefinition> = {
args: [
{ name: 'foo', type: 'string', list: false },
{ name: 'bar', type: 'string', list: false },
{ name: 'foo', type: 'string', list: false, required: false },
{ name: 'bar', type: 'string', list: false, required: false },
],
description: 'The value is {foo} and { bar }',
};
Expand All @@ -51,7 +51,15 @@ describe('interpolateDescription()', () => {

it('formats currency-form-input value as a decimal', () => {
const operation: Partial<ConfigurableOperationDefinition> = {
args: [{ name: 'price', type: 'int', list: false, ui: { component: 'currency-form-input' } }],
args: [
{
name: 'price',
type: 'int',
list: false,
ui: { component: 'currency-form-input' },
required: false,
},
],
description: 'The price is { price }',
};
const result = interpolateDescription(operation as any, { price: 1234 });
Expand All @@ -61,7 +69,7 @@ describe('interpolateDescription()', () => {

it('formats Date object as human-readable', () => {
const operation: Partial<ConfigurableOperationDefinition> = {
args: [{ name: 'date', type: 'datetime', list: false }],
args: [{ name: 'date', type: 'datetime', list: false, required: false }],
description: 'The date is { date }',
};
const date = new Date('2017-09-15 00:00:00');
Expand All @@ -72,7 +80,7 @@ describe('interpolateDescription()', () => {

it('formats date string object as human-readable', () => {
const operation: Partial<ConfigurableOperationDefinition> = {
args: [{ name: 'date', type: 'datetime', list: false }],
args: [{ name: 'date', type: 'datetime', list: false, required: false }],
description: 'The date is { date }',
};
const date = '2017-09-15';
Expand All @@ -83,7 +91,7 @@ describe('interpolateDescription()', () => {

it('correctly interprets falsy-looking values', () => {
const operation: Partial<ConfigurableOperationDefinition> = {
args: [{ name: 'foo', type: 'int', list: false }],
args: [{ name: 'foo', type: 'int', list: false, required: false }],
description: 'The value is { foo }',
};
const result = interpolateDescription(operation as any, { foo: 0 });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ export const CONFIGURABLE_OPERATION_DEF_FRAGMENT = gql`
args {
name
type
required
defaultValue
list
ui
label
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ export class ConfigurableInputComponent implements OnChanges, OnDestroy, Control
if (value === undefined) {
value = getDefaultConfigArgValue(arg);
}
const validators = arg.list ? undefined : Validators.required;
const validators = arg.list ? undefined : arg.required ? Validators.required : undefined;
this.form.addControl(arg.name, new FormControl(value, validators));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
SimpleChanges,
} from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Observable } from 'rxjs';
import { BehaviorSubject, combineLatest, Observable } from 'rxjs';
import { map } from 'rxjs/operators';

import { DataService } from '../../../data/providers/data.service';
Expand Down Expand Up @@ -41,20 +41,21 @@ export class CurrencyInputComponent implements ControlValueAccessor, OnInit, OnC
onChange: (val: any) => void;
onTouch: () => void;
_decimalValue: string;
private currencyCode$ = new BehaviorSubject<string>('');

constructor(private dataService: DataService, private changeDetectorRef: ChangeDetectorRef) {}

ngOnInit() {
const languageCode$ = this.dataService.client.uiState().mapStream(data => data.uiState.language);
const shouldPrefix$ = languageCode$.pipe(
map(languageCode => {
if (!this.currencyCode) {
const shouldPrefix$ = combineLatest(languageCode$, this.currencyCode$).pipe(
map(([languageCode, currencyCode]) => {
if (!currencyCode) {
return '';
}
const locale = languageCode.replace(/_/g, '-');
const localised = new Intl.NumberFormat(locale, {
style: 'currency',
currency: this.currencyCode,
currency: currencyCode,
currencyDisplay: 'symbol',
}).format(undefined as any);
return localised.indexOf('NaN') > 0;
Expand All @@ -68,6 +69,9 @@ export class CurrencyInputComponent implements ControlValueAccessor, OnInit, OnC
if ('value' in changes) {
this.writeValue(changes['value'].currentValue);
}
if ('currencyCode' in changes) {
this.currencyCode$.next(this.currencyCode);
}
}

registerOnChange(fn: any) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,7 @@ import { switchMap, take, takeUntil } from 'rxjs/operators';

import { FormInputComponent } from '../../../common/component-registry-types';
import { ConfigArgDefinition, CustomFieldConfig } from '../../../common/generated-types';
import {
getConfigArgValue,
getDefaultConfigArgSingleValue,
} from '../../../common/utilities/configurable-operation-utils';
import { getConfigArgValue } from '../../../common/utilities/configurable-operation-utils';
import { ComponentRegistryService } from '../../../providers/component-registry/component-registry.service';

type InputListItem = {
Expand Down Expand Up @@ -211,7 +208,7 @@ export class DynamicFormInputComponent
}
this.listItems.push({
id: this.listId++,
control: new FormControl(getDefaultConfigArgSingleValue(this.def.type as ConfigArgType)),
control: new FormControl((this.def as ConfigArgDefinition).defaultValue ?? null),
});
this.renderList$.next();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
configurableDefinitionToInstance,
ConfigurableOperation,
ConfigurableOperationDefinition,
configurableOperationValueIsValid,
DataService,
Dialog,
FulfillOrderInput,
Expand Down Expand Up @@ -77,8 +78,10 @@ export class FulfillOrderDialogComponent implements Dialog<FulfillOrderInput>, O
0,
);
const formIsValid =
this.fulfillmentHandlerDef?.args.length === 0 ||
(this.fulfillmentHandlerControl.valid && this.fulfillmentHandlerControl.touched);
configurableOperationValueIsValid(
this.fulfillmentHandlerDef,
this.fulfillmentHandlerControl.value,
) && this.fulfillmentHandlerControl.valid;
return formIsValid && 0 < totalCount;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export class ShippingMethodDetailComponent
code: ['', Validators.required],
name: ['', Validators.required],
description: '',
fulfillmentHandler: '',
fulfillmentHandler: ['', Validators.required],
checker: {},
calculator: {},
customFields: this.formBuilder.group(
Expand Down

0 comments on commit 6e5e482

Please sign in to comment.