public render()

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>
        );
    }