Add multiple ModelView child items at once (#14047)

This commit is contained in:
Charles Gagnon
2021-01-26 15:33:45 -08:00
committed by GitHub
parent d828a1f042
commit e79a80590a
10 changed files with 120 additions and 83 deletions

View File

@@ -91,7 +91,12 @@ export interface IComponent extends IDisposable {
layout(): void; layout(): void;
registerEventHandler(handler: (event: IComponentEventArgs) => void): IDisposable; registerEventHandler(handler: (event: IComponentEventArgs) => void): IDisposable;
clearContainer?: () => void; clearContainer?: () => void;
addToContainer?: (componentDescriptor: IComponentDescriptor, config: any, index?: number) => void; /**
* Called when child components are added to this component
* @param items The list of items to add. Each item consists of a descriptor for identifying the component,
* the config defined and an optional index to insert it at
*/
addToContainer?: (items: { componentDescriptor: IComponentDescriptor, config: any, index?: number }[]) => void;
removeFromContainer?: (componentDescriptor: IComponentDescriptor) => void; removeFromContainer?: (componentDescriptor: IComponentDescriptor) => void;
setLayout?: (layout: any) => void; setLayout?: (layout: any) => void;
setItemLayout?: (componentDescriptor: IComponentDescriptor, config: any) => void; setItemLayout?: (componentDescriptor: IComponentDescriptor, config: any) => void;

View File

@@ -33,7 +33,12 @@ export interface IModelViewEventArgs extends IComponentEventArgs {
export interface IModelView extends IView { export interface IModelView extends IView {
initializeModel(rootComponent: IComponentShape, validationCallback?: (componentId: string) => Thenable<boolean>): void; initializeModel(rootComponent: IComponentShape, validationCallback?: (componentId: string) => Thenable<boolean>): void;
clearContainer(componentId: string): void; clearContainer(componentId: string): void;
addToContainer(containerId: string, item: IItemConfig, index?: number): void; /**
* Adds the specified items as children of the specified parent container
* @param containerId The ID of the container component to add the items to
* @param items The list of items to add to the container
*/
addToContainer(containerId: string, items: { itemConfig: IItemConfig, index?: number }[]): void;
removeFromContainer(containerId: string, item: IItemConfig): void; removeFromContainer(containerId: string, item: IItemConfig): void;
setLayout(componentId: string, layout: any, initial?: boolean): void; setLayout(componentId: string, layout: any, initial?: boolean): void;
setItemLayout(componentId: string, item: IItemConfig): void; setItemLayout(componentId: string, item: IItemConfig): void;

View File

@@ -51,9 +51,9 @@ export class MainThreadModelView extends Disposable implements MainThreadModelVi
return this.execModelViewAction(handle, (modelView) => modelView.clearContainer(componentId)); return this.execModelViewAction(handle, (modelView) => modelView.clearContainer(componentId));
} }
$addToContainer(handle: number, containerId: string, item: IItemConfig, index?: number): Thenable<void> { $addToContainer(handle: number, containerId: string, items: { itemConfig: IItemConfig, index?: number }[]): Thenable<void> {
return this.execModelViewAction(handle, return this.execModelViewAction(handle,
(modelView) => modelView.addToContainer(containerId, item, index)); (modelView) => modelView.addToContainer(containerId, items));
} }
$removeFromContainer(handle: number, containerId: string, item: IItemConfig): Thenable<void> { $removeFromContainer(handle: number, containerId: string, item: IItemConfig): Thenable<void> {

View File

@@ -707,9 +707,12 @@ class ComponentWrapper implements azdata.Component {
} }
public addItems(items: Array<azdata.Component>, itemLayout?: any): void { public addItems(items: Array<azdata.Component>, itemLayout?: any): void {
for (let item of items) { const itemConfigs = items.map(item => {
this.addItem(item, itemLayout); return {
} itemConfig: this.createAndAddItemConfig(item, itemLayout).toIItemConfig()
};
});
this._proxy.$addToContainer(this._handle, this.id, itemConfigs).then(undefined, (err) => this.handleError(err));
} }
public removeItemAt(index: number): boolean { public removeItemAt(index: number): boolean {
@@ -735,11 +738,22 @@ class ComponentWrapper implements azdata.Component {
} }
public addItem(item: azdata.Component, itemLayout?: any, index?: number): void { public addItem(item: azdata.Component, itemLayout?: any, index?: number): void {
let itemImpl = item as ComponentWrapper; const config = this.createAndAddItemConfig(item, itemLayout, index);
this._proxy.$addToContainer(this._handle, this.id, [{ itemConfig: config.toIItemConfig(), index }]).then(undefined, (err) => this.handleError(err));
}
/**
* Creates the internal item config for the component and adds it to the list of child configs for this component.
* @param item The child component to add
* @param itemLayout The optional layout to apply to the child component
* @param index The optional index to insert the child component at
*/
private createAndAddItemConfig(item: azdata.Component, itemLayout?: any, index?: number): InternalItemConfig {
const itemImpl = item as ComponentWrapper;
if (!itemImpl) { if (!itemImpl) {
throw new Error(nls.localize('unknownComponentType', "Unknown component type. Must use ModelBuilder to create objects")); throw new Error(nls.localize('unknownComponentType', "Unknown component type. Must use ModelBuilder to create objects"));
} }
let config = new InternalItemConfig(itemImpl, itemLayout); const config = new InternalItemConfig(itemImpl, itemLayout);
if (index !== undefined && index >= 0 && index <= this.items.length) { if (index !== undefined && index >= 0 && index <= this.items.length) {
this.itemConfigs.splice(index, 0, config); this.itemConfigs.splice(index, 0, config);
} else if (!index) { } else if (!index) {
@@ -747,7 +761,7 @@ class ComponentWrapper implements azdata.Component {
} else { } else {
throw new Error(nls.localize('invalidIndex', "The index {0} is invalid.", index)); throw new Error(nls.localize('invalidIndex', "The index {0} is invalid.", index));
} }
this._proxy.$addToContainer(this._handle, this.id, config.toIItemConfig(), index).then(undefined, (err) => this.handleError(err)); return config;
} }
public setLayout(layout: any): Thenable<void> { public setLayout(layout: any): Thenable<void> {
@@ -1591,14 +1605,16 @@ class DeclarativeTableWrapper extends ComponentWrapper implements azdata.Declara
// data property though since the caller would still expect that to contain // data property though since the caller would still expect that to contain
// the Component objects they created // the Component objects they created
const properties = assign({}, this.properties); const properties = assign({}, this.properties);
const componentsToAdd: ComponentWrapper[] = [];
if (properties.data?.length > 0) { if (properties.data?.length > 0) {
properties.data = properties.data.map((row: any[]) => row.map(cell => { properties.data = properties.data.map((row: any[]) => row.map(cell => {
if (cell instanceof ComponentWrapper) { if (cell instanceof ComponentWrapper) {
// First ensure that we register the component using addItem // First ensure that we register the component using addItem
// such that it gets added to the ModelStore. We don't want to // such that it gets added to the ModelStore. We don't want to
// make the table component an actual container since that exposes // make the table component an actual container since that exposes
// a lot of functionality we don't need. // a lot of functionality we don't need.
this.addItem(cell); componentsToAdd.push(cell);
return cell.id; return cell.id;
} }
return cell; return cell;
@@ -1611,13 +1627,14 @@ class DeclarativeTableWrapper extends ComponentWrapper implements azdata.Declara
// such that it gets added to the ModelStore. We don't want to // such that it gets added to the ModelStore. We don't want to
// make the table component an actual container since that exposes // make the table component an actual container since that exposes
// a lot of functionality we don't need. // a lot of functionality we don't need.
this.addItem(cell.value); componentsToAdd.push(cell.value);
return { value: cell.value.id, ariaLabel: cell.ariaLabel, style: cell.style }; return { value: cell.value.id, ariaLabel: cell.ariaLabel, style: cell.style };
} }
return cell; return cell;
})); }));
} }
} }
this.addItems(componentsToAdd);
return properties; return properties;
} }
} }

View File

@@ -777,7 +777,7 @@ export interface MainThreadModelViewShape extends IDisposable {
$registerProvider(id: string): void; $registerProvider(id: string): void;
$initializeModel(handle: number, rootComponent: IComponentShape): Thenable<void>; $initializeModel(handle: number, rootComponent: IComponentShape): Thenable<void>;
$clearContainer(handle: number, componentId: string): Thenable<void>; $clearContainer(handle: number, componentId: string): Thenable<void>;
$addToContainer(handle: number, containerId: string, item: IItemConfig, index?: number): Thenable<void>; $addToContainer(handle: number, containerId: string, items: { itemConfig: IItemConfig, index?: number }[]): Thenable<void>;
$removeFromContainer(handle: number, containerId: string, item: IItemConfig): Thenable<void>; $removeFromContainer(handle: number, containerId: string, item: IItemConfig): Thenable<void>;
$setLayout(handle: number, componentId: string, layout: any): Thenable<void>; $setLayout(handle: number, componentId: string, layout: any): Thenable<void>;
$setItemLayout(handle: number, componentId: string, item: IItemConfig): Thenable<void>; $setItemLayout(handle: number, componentId: string, item: IItemConfig): Thenable<void>;

View File

@@ -289,32 +289,34 @@ export abstract class ContainerBase<T, TPropertyBag extends azdata.ComponentProp
} }
/// IComponent container-related implementation /// IComponent container-related implementation
public addToContainer(componentDescriptor: IComponentDescriptor, config: any, index?: number): void { public addToContainer(items: { componentDescriptor: IComponentDescriptor, config: any, index?: number }[]): void {
this.logService.debug(`Adding component ${componentDescriptor.id} to container ${this.descriptor.id}`); items.forEach(newItem => {
if (!componentDescriptor) { this.logService.debug(`Adding component ${newItem.componentDescriptor.id} to container ${this.descriptor.id}`);
return; if (!newItem.componentDescriptor) {
} return;
if (this.items.some(item => item.descriptor.id === componentDescriptor.id && item.descriptor.type === componentDescriptor.type)) { }
return; if (this.items.some(item => item.descriptor.id === newItem.componentDescriptor.id && item.descriptor.type === newItem.componentDescriptor.type)) {
} return;
if (index !== undefined && index !== null && index >= 0 && index <= this.items.length) { }
this.items.splice(index, 0, new ItemDescriptor(componentDescriptor, config)); if (newItem.index !== undefined && newItem.index !== null && newItem.index >= 0 && newItem.index <= this.items.length) {
} else if (!index) { this.items.splice(newItem.index, 0, new ItemDescriptor(newItem.componentDescriptor, newItem.config));
this.items.push(new ItemDescriptor(componentDescriptor, config)); } else if (!newItem.index) {
} else { this.items.push(new ItemDescriptor(newItem.componentDescriptor, newItem.config));
throw new Error(nls.localize('invalidIndex', "The index {0} is invalid.", index)); } else {
} throw new Error(nls.localize('invalidIndex', "The index {0} is invalid.", newItem.index));
}
this.logService.debug(`Queueing up action to register validation event handler on component ${componentDescriptor.id} in container ${this.descriptor.id}`); this.logService.debug(`Queueing up action to register validation event handler on component ${newItem.componentDescriptor.id} in container ${this.descriptor.id}`);
this.modelStore.eventuallyRunOnComponent(componentDescriptor.id, component => { this.modelStore.eventuallyRunOnComponent(newItem.componentDescriptor.id, component => {
this.logService.debug(`Registering validation event handler on component ${componentDescriptor.id} in container ${this.descriptor.id}`); this.logService.debug(`Registering validation event handler on component ${newItem.componentDescriptor.id} in container ${this.descriptor.id}`);
component.registerEventHandler(async event => { component.registerEventHandler(async event => {
if (event.eventType === ComponentEventType.validityChanged) { if (event.eventType === ComponentEventType.validityChanged) {
this.logService.debug(`Running validation on container ${this.descriptor.id} because validity of child component ${componentDescriptor.id} changed`); this.logService.debug(`Running validation on container ${this.descriptor.id} because validity of child component ${newItem.componentDescriptor.id} changed`);
this.validate().catch(onUnexpectedError); this.validate().catch(onUnexpectedError);
} }
}); });
}, true); }, true);
});
this._changeRef.detectChanges(); this._changeRef.detectChanges();
this.onItemsUpdated(); this.onItemsUpdated();
return; return;

View File

@@ -93,8 +93,8 @@ export default class LoadingComponent extends ComponentBase<azdata.LoadingCompon
return this.getPropertyOrDefault<string>((props) => props.loadingCompletedText, localize('loadingCompletedMessage', "Loading completed")); return this.getPropertyOrDefault<string>((props) => props.loadingCompletedText, localize('loadingCompletedMessage', "Loading completed"));
} }
public addToContainer(componentDescriptor: IComponentDescriptor): void { public addToContainer(items: { componentDescriptor: IComponentDescriptor }[]): void {
this._component = componentDescriptor; this._component = items[0].componentDescriptor;
this.layout(); this.layout();
} }

View File

@@ -68,9 +68,14 @@ export abstract class ViewBase extends AngularDisposable implements IModelView {
this.setLayout(component.id, component.layout, true); this.setLayout(component.id, component.layout, true);
this.registerEvent(component.id, true); this.registerEvent(component.id, true);
if (component.itemConfigs) { if (component.itemConfigs) {
for (let item of component.itemConfigs) { const items = component.itemConfigs.map(itemConfig => {
this.addToContainer(component.id, item, undefined, true); return {
} itemConfig,
index: undefined,
initial: true
};
});
this.addToContainer(component.id, items);
} }
return descriptor; return descriptor;
@@ -97,17 +102,26 @@ export abstract class ViewBase extends AngularDisposable implements IModelView {
}); });
} }
addToContainer(containerId: string, itemConfig: IItemConfig, index?: number, initial: boolean = false): void { addToContainer(containerId: string, items: { itemConfig: IItemConfig, index?: number }[], initial?: boolean): void {
this.logService.debug(`Queueing action to add component ${itemConfig.componentShape.id} to container ${containerId}`); const itemNames = items.map(item => item.itemConfig.componentShape.id).join(',');
this.logService.debug(`Queueing action to add components ${itemNames} to container ${containerId}`);
// Do not return the promise as this should be non-blocking // Do not return the promise as this should be non-blocking
this.queueAction(containerId, (component) => { this.queueAction(containerId, (component) => {
if (!component.addToContainer) { if (!component.addToContainer) {
this.logService.warn(`Container ${containerId} is trying to add component ${itemConfig.componentShape.id} but does not implement addToContainer!`); this.logService.warn(`Container ${containerId} is trying to add components ${itemNames} but does not implement addToContainer!`);
return; return;
} }
this.logService.debug(`Adding component ${itemConfig.componentShape.id} to container ${containerId}`); this.logService.debug(`Adding components ${itemNames} to container ${containerId}`);
let childDescriptor = this.defineComponent(itemConfig.componentShape); const itemConfigs = items.map(item => {
component.addToContainer(childDescriptor, itemConfig.config, index); const componentDescriptor = this.defineComponent(item.itemConfig.componentShape);
return {
componentDescriptor,
config: item.itemConfig.config,
index: item.index
};
});
component.addToContainer(itemConfigs);
}, initial); }, initial);
} }

View File

@@ -37,7 +37,7 @@ suite('ExtHostModelView Validation Tests', () => {
$registerProvider: (id: string) => undefined, $registerProvider: (id: string) => undefined,
$initializeModel: (handle: number, rootComponent: IComponentShape) => undefined, $initializeModel: (handle: number, rootComponent: IComponentShape) => undefined,
$clearContainer: (handle: number, componentId: string) => undefined, $clearContainer: (handle: number, componentId: string) => undefined,
$addToContainer: (handle: number, containerId: string, item: IItemConfig) => undefined, $addToContainer: (handle: number, containerId: string, items: { itemConfig: IItemConfig }[]) => undefined,
$removeFromContainer: (handle: number, containerId: string, item: IItemConfig) => undefined, $removeFromContainer: (handle: number, containerId: string, item: IItemConfig) => undefined,
$setLayout: (handle: number, componentId: string, layout: any) => undefined, $setLayout: (handle: number, componentId: string, layout: any) => undefined,
$setProperties: (handle: number, componentId: string, properties: { [key: string]: any }) => undefined, $setProperties: (handle: number, componentId: string, properties: { [key: string]: any }) => undefined,
@@ -127,7 +127,7 @@ suite('ExtHostModelView Validation Tests', () => {
}); });
test('Setting a form component as required initializes the model with the component required', () => { test('Setting a form component as required initializes the model with the component required', () => {
mockProxy.setup(x => x.$addToContainer(It.isAny(), It.isAny(), It.isAny(), undefined)).returns(() => Promise.resolve()); mockProxy.setup(x => x.$addToContainer(It.isAny(), It.isAny(), It.isAny())).returns(() => Promise.resolve());
// Set up the input component with required initially set to false // Set up the input component with required initially set to false
let inputComponent = modelView.modelBuilder.inputBox().component(); let inputComponent = modelView.modelBuilder.inputBox().component();
@@ -152,7 +152,7 @@ suite('ExtHostModelView Validation Tests', () => {
// Set up the mock proxy to save the component that gets initialized so that it can be verified // Set up the mock proxy to save the component that gets initialized so that it can be verified
let rootComponent: IComponentShape; let rootComponent: IComponentShape;
mockProxy.setup(x => x.$initializeModel(It.isAny(), It.isAny())).callback((handle, componentShape) => rootComponent = componentShape); mockProxy.setup(x => x.$initializeModel(It.isAny(), It.isAny())).callback((handle, componentShape) => rootComponent = componentShape);
mockProxy.setup(x => x.$addToContainer(It.isAny(), It.isAny(), It.isAny(), undefined)).returns(() => Promise.resolve()); mockProxy.setup(x => x.$addToContainer(It.isAny(), It.isAny(), It.isAny())).returns(() => Promise.resolve());
// Set up the form with a top level component and a group // Set up the form with a top level component and a group
let topLevelList = modelView.modelBuilder.listBox().component(); let topLevelList = modelView.modelBuilder.listBox().component();
@@ -211,8 +211,7 @@ suite('ExtHostModelView Validation Tests', () => {
test('Inserting and removing components from a container should work correctly', () => { test('Inserting and removing components from a container should work correctly', () => {
mockProxy.setup(x => x.$initializeModel(It.isAny(), It.isAny())); mockProxy.setup(x => x.$initializeModel(It.isAny(), It.isAny()));
mockProxy.setup(x => x.$addToContainer(It.isAny(), It.isAny(), It.isAny(), undefined)).returns(() => Promise.resolve()); mockProxy.setup(x => x.$addToContainer(It.isAny(), It.isAny(), It.isAny())).returns(() => Promise.resolve());
mockProxy.setup(x => x.$addToContainer(It.isAny(), It.isAny(), It.isAny(), It.isAny())).returns(() => Promise.resolve());
mockProxy.setup(x => x.$removeFromContainer(It.isAny(), It.isAny(), It.isAny())).returns(() => Promise.resolve()); mockProxy.setup(x => x.$removeFromContainer(It.isAny(), It.isAny(), It.isAny())).returns(() => Promise.resolve());
// Set up the form with a top level component and a group // Set up the form with a top level component and a group
let listBox = modelView.modelBuilder.listBox().component(); let listBox = modelView.modelBuilder.listBox().component();
@@ -234,8 +233,7 @@ suite('ExtHostModelView Validation Tests', () => {
test('Inserting component give negative number fails', () => { test('Inserting component give negative number fails', () => {
mockProxy.setup(x => x.$initializeModel(It.isAny(), It.isAny())).callback((handle, componentShape) => { }); mockProxy.setup(x => x.$initializeModel(It.isAny(), It.isAny())).callback((handle, componentShape) => { });
mockProxy.setup(x => x.$addToContainer(It.isAny(), It.isAny(), It.isAny(), undefined)).returns(() => Promise.resolve()); mockProxy.setup(x => x.$addToContainer(It.isAny(), It.isAny(), It.isAny())).returns(() => Promise.resolve());
mockProxy.setup(x => x.$addToContainer(It.isAny(), It.isAny(), It.isAny(), It.isAny())).returns(() => Promise.resolve());
mockProxy.setup(x => x.$removeFromContainer(It.isAny(), It.isAny(), It.isAny())).returns(() => Promise.resolve()); mockProxy.setup(x => x.$removeFromContainer(It.isAny(), It.isAny(), It.isAny())).returns(() => Promise.resolve());
// Set up the form with a top level component and a group // Set up the form with a top level component and a group
@@ -253,8 +251,7 @@ suite('ExtHostModelView Validation Tests', () => {
test('Inserting component give wrong number fails', () => { test('Inserting component give wrong number fails', () => {
mockProxy.setup(x => x.$initializeModel(It.isAny(), It.isAny())).callback((handle, componentShape) => { }); mockProxy.setup(x => x.$initializeModel(It.isAny(), It.isAny())).callback((handle, componentShape) => { });
mockProxy.setup(x => x.$addToContainer(It.isAny(), It.isAny(), It.isAny(), undefined)).returns(() => Promise.resolve()); mockProxy.setup(x => x.$addToContainer(It.isAny(), It.isAny(), It.isAny())).returns(() => Promise.resolve());
mockProxy.setup(x => x.$addToContainer(It.isAny(), It.isAny(), It.isAny(), It.isAny())).returns(() => Promise.resolve());
mockProxy.setup(x => x.$removeFromContainer(It.isAny(), It.isAny(), It.isAny())).returns(() => Promise.resolve()); mockProxy.setup(x => x.$removeFromContainer(It.isAny(), It.isAny(), It.isAny())).returns(() => Promise.resolve());
// Set up the form with a top level component and a group // Set up the form with a top level component and a group
@@ -272,8 +269,7 @@ suite('ExtHostModelView Validation Tests', () => {
test('Inserting component give end of the list succeeds', () => { test('Inserting component give end of the list succeeds', () => {
mockProxy.setup(x => x.$initializeModel(It.isAny(), It.isAny())).callback((handle, componentShape) => { }); mockProxy.setup(x => x.$initializeModel(It.isAny(), It.isAny())).callback((handle, componentShape) => { });
mockProxy.setup(x => x.$addToContainer(It.isAny(), It.isAny(), It.isAny(), undefined)).returns(() => Promise.resolve()); mockProxy.setup(x => x.$addToContainer(It.isAny(), It.isAny(), It.isAny())).returns(() => Promise.resolve());
mockProxy.setup(x => x.$addToContainer(It.isAny(), It.isAny(), It.isAny(), It.isAny())).returns(() => Promise.resolve());
mockProxy.setup(x => x.$removeFromContainer(It.isAny(), It.isAny(), It.isAny())).returns(() => Promise.resolve()); mockProxy.setup(x => x.$removeFromContainer(It.isAny(), It.isAny(), It.isAny())).returns(() => Promise.resolve());
// Set up the form with a top level component and a group // Set up the form with a top level component and a group
@@ -293,8 +289,7 @@ suite('ExtHostModelView Validation Tests', () => {
test('Removing a component that does not exist does not fail', () => { test('Removing a component that does not exist does not fail', () => {
// Set up the mock proxy to save the component that gets initialized so that it can be verified // Set up the mock proxy to save the component that gets initialized so that it can be verified
mockProxy.setup(x => x.$initializeModel(It.isAny(), It.isAny())); mockProxy.setup(x => x.$initializeModel(It.isAny(), It.isAny()));
mockProxy.setup(x => x.$addToContainer(It.isAny(), It.isAny(), It.isAny(), undefined)).returns(() => Promise.resolve()); mockProxy.setup(x => x.$addToContainer(It.isAny(), It.isAny(), It.isAny())).returns(() => Promise.resolve());
mockProxy.setup(x => x.$addToContainer(It.isAny(), It.isAny(), It.isAny(), It.isAny())).returns(() => Promise.resolve());
// Set up the form with a top level component and a group // Set up the form with a top level component and a group
let listBox = modelView.modelBuilder.listBox().component(); let listBox = modelView.modelBuilder.listBox().component();
@@ -314,8 +309,7 @@ suite('ExtHostModelView Validation Tests', () => {
test('Inserting and removing component in a form should work correctly', () => { test('Inserting and removing component in a form should work correctly', () => {
mockProxy.setup(x => x.$initializeModel(It.isAny(), It.isAny())).callback((handle, componentShape) => { }); mockProxy.setup(x => x.$initializeModel(It.isAny(), It.isAny())).callback((handle, componentShape) => { });
mockProxy.setup(x => x.$addToContainer(It.isAny(), It.isAny(), It.isAny(), undefined)).returns(() => Promise.resolve()); mockProxy.setup(x => x.$addToContainer(It.isAny(), It.isAny(), It.isAny())).returns(() => Promise.resolve());
mockProxy.setup(x => x.$addToContainer(It.isAny(), It.isAny(), It.isAny(), It.isAny())).returns(() => Promise.resolve());
mockProxy.setup(x => x.$removeFromContainer(It.isAny(), It.isAny(), It.isAny())).returns(() => Promise.resolve()); mockProxy.setup(x => x.$removeFromContainer(It.isAny(), It.isAny(), It.isAny())).returns(() => Promise.resolve());
// Set up the form with a top level component and a group // Set up the form with a top level component and a group
@@ -367,8 +361,7 @@ suite('ExtHostModelView Validation Tests', () => {
suite('Declarative table', () => { suite('Declarative table', () => {
setup(done => { setup(done => {
mockProxy.setup(x => x.$addToContainer(It.isAny(), It.isAny(), It.isAny(), undefined)).returns(() => Promise.resolve()); mockProxy.setup(x => x.$addToContainer(It.isAny(), It.isAny(), It.isAny())).returns(() => Promise.resolve());
mockProxy.setup(x => x.$addToContainer(It.isAny(), It.isAny(), It.isAny(), It.isAny())).returns(() => Promise.resolve());
mockProxy.setup(x => x.$removeFromContainer(It.isAny(), It.isAny(), It.isAny())).returns(() => Promise.resolve()); mockProxy.setup(x => x.$removeFromContainer(It.isAny(), It.isAny(), It.isAny())).returns(() => Promise.resolve());
extHostModelView = new ExtHostModelView(mainContext, undefined, undefined); extHostModelView = new ExtHostModelView(mainContext, undefined, undefined);
@@ -439,12 +432,13 @@ suite('ExtHostModelView Validation Tests', () => {
rootComponent.properties.data && rootComponent.properties.data &&
rootComponent.properties.data[0][0] === button.id; rootComponent.properties.data[0][0] === button.id;
})), Times.once()); })), Times.once());
mockProxy.verify(x => x.$addToContainer(It.isAny(), It.isAny(), It.is(item => { mockProxy.verify(x => x.$addToContainer(It.isAny(), It.isAny(), It.is(items => {
const item = items[0].itemConfig;
return item.componentShape.id === declarativeTable.id && return item.componentShape.id === declarativeTable.id &&
item.componentShape.properties && item.componentShape.properties &&
item.componentShape.properties.data && item.componentShape.properties.data &&
item.componentShape.properties.data[0][0] === button.id; item.componentShape.properties.data[0][0] === button.id;
}), undefined), Times.once()); })), Times.once());
}); });
}); });
}); });

View File

@@ -98,7 +98,7 @@ suite('ComponentBase Tests', () => {
test('Container validation reflects child component validity', () => { test('Container validation reflects child component validity', () => {
assert.equal(testContainer.valid, true, 'Test container validity did not default to true'); assert.equal(testContainer.valid, true, 'Test container validity did not default to true');
testContainer.addToContainer(testComponent.descriptor, undefined); testContainer.addToContainer([{ componentDescriptor: testComponent.descriptor, config: undefined }]);
testComponent.addValidation(() => false); testComponent.addValidation(() => false);
return testComponent.validate().then(() => { return testComponent.validate().then(() => {
return testContainer.validate().then(valid => { return testContainer.validate().then(valid => {
@@ -121,47 +121,47 @@ suite('ComponentBase Tests', () => {
} }
}); });
testComponent.addValidation(() => false); testComponent.addValidation(() => false);
testContainer.addToContainer(testComponent.descriptor, undefined); testContainer.addToContainer([{ componentDescriptor: testComponent.descriptor, config: undefined }]);
testComponent.validate(); testComponent.validate();
}); });
test('Inserting a component to a container adds the component to the right place', () => { test('Inserting a component to a container adds the component to the right place', () => {
testContainer.addToContainer(testComponent.descriptor, undefined); testContainer.addToContainer([{ componentDescriptor: testComponent.descriptor, config: undefined }]);
assert.equal(testContainer.TestItems.length, 1, `Unexpected number of items. Expected 1 got ${testContainer.TestItems.length} : ${JSON.stringify(testContainer.TestItems)}`); assert.equal(testContainer.TestItems.length, 1, `Unexpected number of items. Expected 1 got ${testContainer.TestItems.length} : ${JSON.stringify(testContainer.TestItems)}`);
testContainer.addToContainer(testComponent2.descriptor, undefined, 0); testContainer.addToContainer([{ componentDescriptor: testComponent2.descriptor, config: undefined, index: 0 }]);
assert.equal(testContainer.TestItems.length, 2, `Unexpected number of items. Expected 2 got ${testContainer.TestItems.length} : ${JSON.stringify(testContainer.TestItems)}`); assert.equal(testContainer.TestItems.length, 2, `Unexpected number of items. Expected 2 got ${testContainer.TestItems.length} : ${JSON.stringify(testContainer.TestItems)}`);
assert.equal(testContainer.TestItems[0].descriptor.id, testComponent2.descriptor.id); assert.equal(testContainer.TestItems[0].descriptor.id, testComponent2.descriptor.id);
}); });
test('Inserting a component to a container given negative index fails', () => { test('Inserting a component to a container given negative index fails', () => {
testContainer.addToContainer(testComponent.descriptor, undefined); testContainer.addToContainer([{ componentDescriptor: testComponent.descriptor, config: undefined }]);
assert.equal(testContainer.TestItems.length, 1, `Unexpected number of items. Expected 1 got ${testContainer.TestItems.length} : ${JSON.stringify(testContainer.TestItems)}`); assert.equal(testContainer.TestItems.length, 1, `Unexpected number of items. Expected 1 got ${testContainer.TestItems.length} : ${JSON.stringify(testContainer.TestItems)}`);
assert.throws(() => testContainer.addToContainer(testComponent2.descriptor, undefined, -1)); assert.throws(() => testContainer.addToContainer([{ componentDescriptor: testComponent2.descriptor, config: undefined, index: -1 }]));
}); });
test('Inserting a component to a container given wrong index fails', () => { test('Inserting a component to a container given wrong index fails', () => {
testContainer.addToContainer(testComponent.descriptor, undefined); testContainer.addToContainer([{ componentDescriptor: testComponent.descriptor, config: undefined }]);
assert.equal(testContainer.TestItems.length, 1, `Unexpected number of items. Expected 1 got ${testContainer.TestItems.length} : ${JSON.stringify(testContainer.TestItems)}`); assert.equal(testContainer.TestItems.length, 1, `Unexpected number of items. Expected 1 got ${testContainer.TestItems.length} : ${JSON.stringify(testContainer.TestItems)}`);
assert.throws(() => testContainer.addToContainer(testComponent2.descriptor, undefined, 10)); assert.throws(() => testContainer.addToContainer([{ componentDescriptor: testComponent2.descriptor, config: undefined, index: 10 }]));
}); });
test('Inserting a component to a container given end of list succeeds', () => { test('Inserting a component to a container given end of list succeeds', () => {
testContainer.addToContainer(testComponent.descriptor, undefined); testContainer.addToContainer([{ componentDescriptor: testComponent.descriptor, config: undefined }]);
assert.equal(testContainer.TestItems.length, 1, `Unexpected number of items. Expected 1 got ${testContainer.TestItems.length} : ${JSON.stringify(testContainer.TestItems)}`); assert.equal(testContainer.TestItems.length, 1, `Unexpected number of items. Expected 1 got ${testContainer.TestItems.length} : ${JSON.stringify(testContainer.TestItems)}`);
testContainer.addToContainer(testComponent2.descriptor, undefined, 1); testContainer.addToContainer([{ componentDescriptor: testComponent2.descriptor, config: undefined, index: 1 }]);
assert.equal(testContainer.TestItems.length, 2, `Unexpected number of items. Expected 2 got ${testContainer.TestItems.length} : ${JSON.stringify(testContainer.TestItems)}`); assert.equal(testContainer.TestItems.length, 2, `Unexpected number of items. Expected 2 got ${testContainer.TestItems.length} : ${JSON.stringify(testContainer.TestItems)}`);
}); });
test('Removing a component the does not exist does not make change in the items', () => { test('Removing a component the does not exist does not make change in the items', () => {
testContainer.addToContainer(testComponent.descriptor, undefined); testContainer.addToContainer([{ componentDescriptor: testComponent.descriptor, config: undefined }]);
assert.equal(testContainer.TestItems.length, 1, `Unexpected number of items. Expected 1 got ${testContainer.TestItems.length} : ${JSON.stringify(testContainer.TestItems)}`); assert.equal(testContainer.TestItems.length, 1, `Unexpected number of items. Expected 1 got ${testContainer.TestItems.length} : ${JSON.stringify(testContainer.TestItems)}`);
testContainer.removeFromContainer(testComponent2.descriptor); testContainer.removeFromContainer(testComponent2.descriptor);
assert.equal(testContainer.TestItems.length, 1, `Unexpected number of items. Expected 1 got ${testContainer.TestItems.length} : ${JSON.stringify(testContainer.TestItems)}`); assert.equal(testContainer.TestItems.length, 1, `Unexpected number of items. Expected 1 got ${testContainer.TestItems.length} : ${JSON.stringify(testContainer.TestItems)}`);
}); });
test('Removing a component removes it from items', () => { test('Removing a component removes it from items', () => {
testContainer.addToContainer(testComponent.descriptor, undefined); testContainer.addToContainer([{ componentDescriptor: testComponent.descriptor, config: undefined }]);
testContainer.addToContainer(testComponent2.descriptor, undefined); testContainer.addToContainer([{ componentDescriptor: testComponent2.descriptor, config: undefined }]);
assert.equal(testContainer.TestItems.length, 2, `Unexpected number of items. Expected 2 got ${testContainer.TestItems.length} : ${JSON.stringify(testContainer.TestItems)}`); assert.equal(testContainer.TestItems.length, 2, `Unexpected number of items. Expected 2 got ${testContainer.TestItems.length} : ${JSON.stringify(testContainer.TestItems)}`);
testContainer.removeFromContainer(testComponent.descriptor); testContainer.removeFromContainer(testComponent.descriptor);
assert.equal(testContainer.TestItems.length, 1, `Unexpected number of items. Expected 1 got ${testContainer.TestItems.length} : ${JSON.stringify(testContainer.TestItems)}`); assert.equal(testContainer.TestItems.length, 1, `Unexpected number of items. Expected 1 got ${testContainer.TestItems.length} : ${JSON.stringify(testContainer.TestItems)}`);
@@ -169,9 +169,9 @@ suite('ComponentBase Tests', () => {
}); });
test('Container dost not add same component twice', () => { test('Container dost not add same component twice', () => {
testContainer.addToContainer(testComponent.descriptor, undefined); testContainer.addToContainer([{ componentDescriptor: testComponent.descriptor, config: undefined }]);
assert.equal(testContainer.TestItems.length, 1, `Unexpected number of items. Expected 1 got ${testContainer.TestItems.length} : ${JSON.stringify(testContainer.TestItems)}`); assert.equal(testContainer.TestItems.length, 1, `Unexpected number of items. Expected 1 got ${testContainer.TestItems.length} : ${JSON.stringify(testContainer.TestItems)}`);
testContainer.addToContainer(testComponent.descriptor, 0); testContainer.addToContainer([{ componentDescriptor: testComponent.descriptor, config: undefined, index: 0 }]);
assert.equal(testContainer.TestItems.length, 1, `Unexpected number of items. Expected 1 got ${testContainer.TestItems.length} : ${JSON.stringify(testContainer.TestItems)}`); assert.equal(testContainer.TestItems.length, 1, `Unexpected number of items. Expected 1 got ${testContainer.TestItems.length} : ${JSON.stringify(testContainer.TestItems)}`);
}); });
}); });