in Tools/WinMLDashboard/src/components/KeyValueEditor.tsx [38:136]
public render() {
const properties = this.props.schema.properties || {};
const knownKeys = Object.keys(properties).filter(
// Don't suggesting adding keys that already exist
(x) => Object.keys(this.props.keyValueObject || {}).every((key) => !this.areSameKeys(key, x)));
const options = knownKeys.map((key: string) => ({ key, text: key }));
let rows: Array<React.ReactElement<HTMLDivElement>> = [];
if (this.props.keyValueObject) {
const [keyErrors, valueErrors] = this.validateSchema(this.state.caseInsensitiveSchema, this.props.keyValueObject);
const schemaEntries = Object.entries(properties);
rows = Object.keys(this.props.keyValueObject!).map((x: string) => {
const lowerCaseKey = x.toLowerCase();
const keyChangedCallback = (option?: IComboBoxOption, index?: number, value?: string) => {
const key = value || option!.text;
if (Object.keys(this.props.keyValueObject!).includes(key)) {
this.setState((prevState: IComponentState, props: IComponentProperties) => (
{
keyErrors: {
[lowerCaseKey]: prevState.keyErrors[lowerCaseKey] ? `${prevState.keyErrors[lowerCaseKey]} ` : 'duplicate key'
},
}
));
} else {
this.props.updateKeyValueObject!(this.copyRenameKey(this.props.keyValueObject, x, key));
}
};
const valueChangedCallback = (option?: IComboBoxOption, index?: number, value?: string) => {
const newValue = value || option!.text;
this.props.updateKeyValueObject!({ ...this.props.keyValueObject!, [x]: newValue });
};
const removeCallback = () => {
const newObject = { ...this.props.keyValueObject! };
delete newObject[x];
this.props.updateKeyValueObject!(newObject);
}
let knownValues = [];
const property = schemaEntries.find((prop) => this.areSameKeys(prop[0], x)) as [string, any];
if (property && property[1].enum) {
knownValues = property[1].enum.map((key: string) => ({ key, text: key }));
}
const keyErrorsState = this.state.keyErrors[lowerCaseKey];
return (
<div key={`${x}${keyErrorsState}`} className='DisplayFlex'>
{ this.props.actionCreator &&
<Icon className='RemoveIcon' iconName='Cancel' onClick={removeCallback} />
}
<ComboBox
className='KeyValueBox'
allowFreeform={true}
text={x}
errorMessage={keyErrorsState || keyErrors[lowerCaseKey]}
options={options}
disabled={!this.props.actionCreator}
onChanged={keyChangedCallback}
/>
<span className='KeyValueSeparator'>=</span>
<ComboBox
className='KeyValueBox'
allowFreeform={true}
text={this.props.keyValueObject![x]}
errorMessage={valueErrors[lowerCaseKey]}
options={knownValues}
disabled={!this.props.actionCreator}
onChanged={valueChangedCallback}
/>
</div>
);
});
}
const addButtonDisabled = !(this.props.keyValueObject && Object.keys(this.props.keyValueObject).every((x) => !!x));
const getSplitButtonClassNames = () => ({ splitButtonContainer: '.KeyValueEditorButtonContainer' });
return (
<div>
{rows}
{ this.props.actionCreator &&
<DefaultButton
className='KeyValueEditorButtonContainer'
iconProps={{ iconName: 'Add' }}
disabled={addButtonDisabled}
onClick={this.addProp}
split={!!knownKeys.length}
menuProps={knownKeys.length ? {
items: options,
onItemClick: this.addProp,
} : undefined}
// The Office UI includes a spam element in it when split = true, which is not of display type block
// and ignores the parent width. We do a hack to get it to 100% of the parent width.
getSplitButtonClassNames={getSplitButtonClassNames}
/>
}
</div>
);
}