build() {
    const options = []
    const { values, value, placeholder, type, inline, other, toggle, ...data } = this.config
    const optionType = type.replace('-group', '')
    const isSelect = type === 'select'
    if (data.multiple || type === 'checkbox-group') {
      data.name = data.name + '[]'
    }

    if ((type === 'checkbox-group' || type === 'radio-group') && data.required) {
      const self = this
      const defaultOnRender = this.onRender.bind(this)
      this.onRender = function() {
        defaultOnRender()
        self.groupRequired()
      }
    }

    delete data.title

    if (values) {
      // if a placeholder is specified, add it to the top of the option list
      if (placeholder && isSelect) {
        options.push(
          this.markup('option', placeholder, {
            disabled: true,
            selected: true,
            value: '',
          }),
        )
      }

      // process the rest of the options
      for (let i = 0; i < values.length; i++) {
        let option = values[i]
        if (typeof option === 'string') {
          option = { label: option, value: option }
        }
        const { label = '', ...optionAttrs } = option
        optionAttrs.id = `${data.id}-${i}`

        // don't select this option if a placeholder is defined
        if (!optionAttrs.selected || placeholder) {
          delete optionAttrs.selected
        }

        // if a value is defined at select level, select this attribute
        if (typeof value !== 'undefined' && optionAttrs.value === value) {
          optionAttrs.selected = true
        }

        if (isSelect) {
          const o = this.markup('option', document.createTextNode(label), optionAttrs)
          options.push(o)
        } else {
          const labelContents = [label]
          let wrapperClass = `formbuilder-${optionType}`
          if (inline) {
            wrapperClass += '-inline'
          }
          optionAttrs.type = optionType
          if (optionAttrs.selected) {
            optionAttrs.checked = 'checked'
            delete optionAttrs.selected
          }
          const input = this.markup('input', null, Object.assign({}, data, optionAttrs))
          const labelAttrs = { for: optionAttrs.id }
          let output = [input, this.markup('label', labelContents, labelAttrs)]
          if (toggle) {
            delete labelAttrs.for
            labelAttrs.className = 'kc-toggle'
            labelContents.unshift(input, this.markup('span'))
            output = this.markup('label', labelContents, labelAttrs)
          }

          const wrapper = this.markup('div', output, { className: wrapperClass })
          options.push(wrapper)
        }
      }

      // if configured to display an 'other' option, prepare the elements
      if (!isSelect && other) {
        const otherOptionAttrs = {
          id: `${data.id}-other`,
          className: `${data.className ?? ''} other-option`,
          value: '',
        }

        let wrapperClass = `formbuilder-${optionType}`
        if (inline) {
          wrapperClass += '-inline'
        }

        const optionAttrs = Object.assign({}, data, otherOptionAttrs)
        optionAttrs.type = optionType

        const otherValAttrs = {
          type: 'text',
          events: {
            input: evt => {
              const otherInput = evt.target
              const other = otherInput.parentElement.previousElementSibling
              other.value = otherInput.value
            },
          },
          id: `${otherOptionAttrs.id}-value`,
          className: 'other-val',
        }
        const primaryInput = this.markup('input', null, optionAttrs)
        const otherInputs = [document.createTextNode(control.mi18n('other')), this.markup('input', null, otherValAttrs)]
        const inputLabel = this.markup('label', otherInputs, { for: optionAttrs.id })
        const wrapper = this.markup('div', [primaryInput, inputLabel], { className: wrapperClass })
        options.push(wrapper)
      }
    }

    // build & return the DOM elements
    if (type == 'select') {
      this.dom = this.markup(optionType, options, trimObj(data, true))
    } else {
      this.dom = this.markup('div', options, { className: type })
    }
    return this.dom
  }
build a select DOM element, supporting other jquery text form-control's
@return {Object} DOM Element to be injected into the form.