Skip to content

Migrating from ghl-ui DropdownTree to HighRise Dropdown

This guide will help you migrate from the ghl-ui DropdownTree component to the new HighRise Dropdown component with tree mode.

Component Implementation Changes

Import Changes

diff
- import { UIDropdownTree } from '@gohighlevel/ghl-ui'
+ import { HLDropdown, HLButton } from '@platform-ui/highrise'

Basic Usage Changes

diff
- <UIDropdownTree
-   v-model="selectedValue"
-   :options="options"
-   label="Select Option"
-   @on-select="handleSelect"
- />

+ <HLDropdown
+   id="tree-dropdown"
+   trigger="click"
+   placement="bottom"
+   :options="options"
+   tree-mode
+   show-search
+   @select="handleSelect"
+   :width="280"
+ >
+   <HLButton size="sm">Select Option</HLButton>
+ </HLDropdown>

Props Changes

ghl-ui PropHighRise PropNotes
v-model@select eventChanged to event-based selection
optionsoptionsEnhanced option structure
labelButton textMoved to trigger button
placeholderButton textMoved to trigger button
ididRequired for accessibility
triggertriggerNew prop for trigger type
placementplacementNew prop for dropdown position
widthwidthNew prop for dropdown width
tree-modetree-modeNew prop for tree structure
show-searchshow-searchNew prop for search functionality

Examples

Basic Tree Dropdown

vue
<template>
  <HLDropdown id="basic-tree-dropdown" trigger="click" placement="bottom" :options="options" tree-mode show-search :width="280">
    <HLButton size="sm">Basic Tree Dropdown</HLButton>
  </HLDropdown>
</template>

<script setup>
import { HLDropdown, HLButton } from '@platform-ui/highrise'

const options = [
  {
    key: 'locations',
    label: 'Locations',
    children: [
      {
        key: 'usa',
        label: 'USA',
        type: 'avatar',
        src: 'path/to/usa-flag.png',
        infoText: '+1',
      },
      {
        key: 'india',
        label: 'India',
        type: 'avatar',
        src: 'path/to/india-flag.png',
        infoText: '+91',
      },
    ],
  },
]
</script>

Tree Dropdown with Validation

vue
<template>
  <HLForm :model="formModel" :rules="rules">
    <HLFormItem label="Category" path="category" required>
      <HLDropdown
        id="category-dropdown"
        trigger="click"
        placement="bottom"
        :options="categories"
        tree-mode
        show-search
        :width="280"
        @select="handleSelect"
      >
        <HLButton size="sm">
          {{ formModel.category || 'Select Category' }}
        </HLButton>
      </HLDropdown>
    </HLFormItem>
  </HLForm>
</template>

<script setup>
import { ref } from 'vue'
import { HLForm, HLFormItem, HLDropdown, HLButton } from '@platform-ui/highrise'

const formModel = ref({
  category: '',
})

const categories = [
  {
    key: 'fruits',
    label: 'Fruits',
    children: [
      { key: 'apple', label: 'Apple', description: 'Red and sweet' },
      { key: 'banana', label: 'Banana', description: 'Yellow and creamy' },
      { key: 'orange', label: 'Orange', description: 'Citrus fruit' },
    ],
  },
  {
    key: 'vegetables',
    label: 'Vegetables',
    children: [
      { key: 'carrot', label: 'Carrot', description: 'Orange and crunchy' },
      { key: 'broccoli', label: 'Broccoli', description: 'Green and healthy' },
      { key: 'potato', label: 'Potato', description: 'Starchy vegetable' },
    ],
  },
]

const handleSelect = (key, option) => {
  formModel.value.category = option.label
}

const rules = {
  category: [
    {
      required: true,
      message: 'Please select a category',
      trigger: 'change',
    },
  ],
}
</script>
vue
<template>
  <HLDropdown
    id="custom-search-dropdown"
    trigger="click"
    placement="bottom"
    :options="filteredOptions"
    tree-mode
    show-search
    :width="280"
    @search="handleSearch"
  >
    <HLButton size="sm">Custom Search</HLButton>
  </HLDropdown>
</template>

<script setup>
import { ref, computed } from 'vue'
import { HLDropdown, HLButton } from '@platform-ui/highrise'

const searchQuery = ref('')
const isLoading = ref(false)

const allOptions = [
  {
    key: 'fruits',
    label: 'Fruits',
    children: [
      { key: 'apple', label: 'Apple', description: 'Red and sweet' },
      { key: 'banana', label: 'Banana', description: 'Yellow and creamy' },
      { key: 'orange', label: 'Orange', description: 'Citrus fruit' },
    ],
  },
  {
    key: 'vegetables',
    label: 'Vegetables',
    children: [
      { key: 'carrot', label: 'Carrot', description: 'Orange and crunchy' },
      { key: 'broccoli', label: 'Broccoli', description: 'Green and healthy' },
      { key: 'potato', label: 'Potato', description: 'Starchy vegetable' },
    ],
  },
]

const filteredOptions = computed(() => {
  if (!searchQuery.value) return allOptions

  const searchLower = searchQuery.value.toLowerCase()
  return allOptions
    .map(group => {
      const matchingChildren = group.children?.filter(
        item => item.label.toLowerCase().includes(searchLower) || item.description?.toLowerCase().includes(searchLower)
      )

      if (!matchingChildren?.length) return null

      return {
        ...group,
        children: matchingChildren,
      }
    })
    .filter(Boolean)
})

let searchTimeout
const handleSearch = value => {
  clearTimeout(searchTimeout)
  isLoading.value = true

  searchTimeout = setTimeout(() => {
    searchQuery.value = value
    isLoading.value = false
  }, 300)
}
</script>

Best Practices

  1. Always provide a unique id for accessibility
  2. Use appropriate trigger type for your use case
  3. Implement proper form validation
  4. Consider dropdown placement
  5. Handle search functionality appropriately
  6. Add proper ARIA labels
  7. Handle loading states appropriately
  8. Use descriptive labels
  9. Implement proper error handling
  10. Consider using tooltips for additional information
  11. Handle disabled states properly
  12. Use consistent spacing and alignment
  13. Consider option grouping
  14. Handle nested options appropriately
  15. Use appropriate icons and avatars
  16. Consider search performance

Additional Notes

  1. The component automatically handles focus states
  2. Error states are managed through validation status
  3. Supports keyboard navigation
  4. Maintains consistent styling with other form components
  5. Handles disabled states consistently
  6. Integrates seamlessly with form validation
  7. Maintains accessibility standards
  8. Supports custom styling through CSS variables
  9. Provides clear visual feedback for all states
  10. Works well on both desktop and mobile devices
  11. Supports both controlled and uncontrolled modes
  12. Includes built-in validation support
  13. Supports custom option rendering
  14. Handles option selection and deselection
  15. Supports custom search implementation
  16. Provides clear option validation
  17. Supports option grouping
  18. Handles nested navigation