diff --git a/apps/design-system/src/pages/view-preview/view-settings.tsx b/apps/design-system/src/pages/view-preview/view-settings.tsx
index bc3738ee70..2e79f16b16 100644
--- a/apps/design-system/src/pages/view-preview/view-settings.tsx
+++ b/apps/design-system/src/pages/view-preview/view-settings.tsx
@@ -3,7 +3,7 @@ import { useLocation, useNavigate } from 'react-router-dom'
import { clsx } from 'clsx'
-import { Button, Icon, Select, SelectContent, SelectItem, Spacer } from '@harnessio/ui/components'
+import { Button, Icon, Select, Spacer } from '@harnessio/ui/components'
import css from './view-settings.module.css'
@@ -17,7 +17,7 @@ enum Themes {
DARK_PROT_STD = 'dark-prot-std',
DARK_STANDARD_HIGH = 'dark-std-high',
LIGHT = 'light-std-std',
- LIGHT_PROTO_STD = 'light-prot-std'
+ LIGHT_PROT_STD = 'light-prot-std'
}
const ViewSettings: FC = ({ routes }) => {
@@ -65,30 +65,30 @@ const ViewSettings: FC = ({ routes }) => {
{showSettings && (
<>
-
-
+
+
{routes.map(route => (
-
+
{route}
-
+
))}
-
-
+
+
- setCurrentTheme(newTheme)}
>
-
+
{Object.values(Themes).map(theme => (
-
+
{theme}
-
+
))}
-
-
+
+
>
)}
diff --git a/apps/portal/src/content/docs/components/select.mdx b/apps/portal/src/content/docs/components/select.mdx
new file mode 100644
index 0000000000..9c58432678
--- /dev/null
+++ b/apps/portal/src/content/docs/components/select.mdx
@@ -0,0 +1,378 @@
+---
+title: Select
+description: Select component
+---
+
+The `Select` component provides a way to create dropdown selections with various sub-components for customization.
+
+import { DocsPage } from "../../../components/docs-page";
+
+ {
+ const [color, setColor] = React.useState()
+ const [randomItem, setRandomItem] = React.useState()
+ const [searchRandomItems, setSearchRandomItems] = React.useState('')
+
+ const allRandomItems = React.useMemo(
+ () => [...new Array(300)].map((_, index) => ({value: index+1, label: \`Item \${index+1}\`})),
+ []
+ )
+
+ const filteredRandomItems = React.useMemo(
+ () => searchRandomItems.trim() ? allRandomItems.filter(item => item.label.toLowerCase().includes(searchRandomItems.toLowerCase())) : allRandomItems,
+ )
+
+ return (
+
+
+
With group
+
+
+ Standard
+ Accessible
+ Vivid
+
+
+
+
+ Custom label
+ Custom
+
+
+
+
+
+
+
+
+
With search
+
+
+ {filteredRandomItems.map(item => (
+
+ {item.label}
+
+ ))}
+
+
+
+
+ )
+
+}`}
+/>
+
+## Usage
+
+```typescript jsx
+import { Select } from '@harnessio/ui/components'
+
+//...
+
+const [color, setColor] = useState()
+
+return (
+
+
+ Standard
+ Accessible
+ Vivid
+
+
+
+
+ Custom label
+ Custom
+
+
+
+)
+```
+
+## Anatomy
+
+All parts of the `Select` component can be imported and composed as required.
+
+```typescript jsx
+
+
+
+
+
+
+
+
+
+
+```
+
+## API Reference
+
+### `Root`
+
+The `Root` component for the Select can be either controlled or uncontrolled. A controlled Select component takes a `value`
+and an `onValueChange` handler as props, and will only update the value when the user makes a selection. An uncontrolled
+Select component will update the value whenever a user makes a selection, and will not re-render when the value is
+changed from outside the component.
+
+```typescript jsx
+
+ {/* Select content */}
+
+```
+
+ void",
+ },
+ ]}
+/>
+
+### `Content`
+
+The content of the select dropdown. This is a required element. The `defaultValue` prop sets the default value of the
+select. The `withSearch` prop enables search support in the dropdown. This is optional and can be used to search the
+available options. The `searchProps` prop is used to pass additional props to the search input when `withSearch` is
+true. This can be used to customize the search input, such as changing the placeholder text.
+
+```typescript jsx
+
+ {/* SelectItem components */}
+
+```
+
+ void }",
+ },
+ ]}
+/>
+
+### `Item`
+
+The `Item` component is used to create an individual item in the select dropdown. Each item requires a `value` prop
+which is the value of the item. If the value is not suitable for typeahead search, you can use the `textValue` prop to
+provide a searchable text string for typeahead search. For example, if the value is JSON-encoded object, `textValue` can
+be used to provide a searchable label for the object.
+
+```typescript jsx
+
+ Name
+
+```
+
+
+
+### `Separator`
+
+The `Separator` component is used to create a separator in the select dropdown.
+
+```typescript jsx
+
+```
+
+
+
+### `Group`
+
+The `Group` component is used to create a group of items in the select dropdown.
+
+```typescript jsx
+
+ Custom label
+ Custom
+
+```
+
+
+
+### `Label`
+
+The `Label` component is used to create a label for a group of items in the select dropdown.
+
+```typescript jsx
+
+ Custom label
+
+```
+
+
diff --git a/packages/ui/src/components/select.tsx b/packages/ui/src/components/select.tsx
index a2e61f7d19..17dea81556 100644
--- a/packages/ui/src/components/select.tsx
+++ b/packages/ui/src/components/select.tsx
@@ -1,4 +1,13 @@
-import { Children, ComponentPropsWithoutRef, ElementRef, FC, forwardRef, PropsWithChildren, ReactNode } from 'react'
+import {
+ Children,
+ ComponentPropsWithoutRef,
+ ElementRef,
+ FC,
+ forwardRef,
+ HTMLAttributes,
+ PropsWithChildren,
+ ReactNode
+} from 'react'
import { Caption, Icon, Label, Message, MessageTheme, SearchBox } from '@/components'
import { usePortal } from '@/context'
@@ -6,8 +15,8 @@ import { useDebounceSearch } from '@hooks/use-debounce-search'
import * as SelectPrimitive from '@radix-ui/react-select'
import { cn } from '@utils/cn'
-interface SelectProps
- extends Omit>, 'defaultValue'>, 'dir'>,
+interface SelectRootProps
+ extends Omit>, 'defaultValue'>, 'dir'>,
SelectPrimitive.SelectProps {
label?: string
error?: string
@@ -20,12 +29,14 @@ interface SelectProps
/**
* A customizable select component that supports labels, error states, and captions
* @example
- *
- * Option 1
- * Option 2
- *
+ *
+ *
+ * Option 1
+ * Option 2
+ *
+ *
*/
-const Select: FC = ({
+const SelectRoot: FC = ({
name,
label,
error,
@@ -60,7 +71,7 @@ const Select: FC = ({
{caption && {caption} }
)
-Select.displayName = SelectPrimitive.Root.displayName
+SelectRoot.displayName = SelectPrimitive.Root.displayName
const SelectGroup = SelectPrimitive.Group
@@ -209,4 +220,13 @@ const SelectSeparator = forwardRef<
))
SelectSeparator.displayName = SelectPrimitive.Separator.displayName
-export { Select, SelectGroup, SelectValue, SelectTrigger, SelectContent, SelectLabel, SelectItem, SelectSeparator }
+const Select = {
+ Root: SelectRoot,
+ Group: SelectGroup,
+ Content: SelectContent,
+ Label: SelectLabel,
+ Item: SelectItem,
+ Separator: SelectSeparator
+}
+
+export { Select }
diff --git a/packages/ui/src/components/theme-selector-v2/theme-dialog.tsx b/packages/ui/src/components/theme-selector-v2/theme-dialog.tsx
index f1b1308bba..fcabce90cd 100644
--- a/packages/ui/src/components/theme-selector-v2/theme-dialog.tsx
+++ b/packages/ui/src/components/theme-selector-v2/theme-dialog.tsx
@@ -1,6 +1,6 @@
import { FC, useEffect, useState } from 'react'
-import { Dialog, Icon, Select, SelectContent, SelectItem, Separator } from '@/components'
+import { Dialog, Icon, Select, Separator } from '@/components'
import darkModeImage from '@/svgs/theme-dark.png'
import lightModeImage from '@/svgs/theme-light.png'
import { cn } from '@/utils/cn'
@@ -95,7 +95,7 @@ const ThemeDialog: FC = ({ defaultTheme, theme, open, onOpenCh
High contrast improves readability, Dimmer mode reduces glare.
- {
@@ -104,14 +104,14 @@ const ThemeDialog: FC = ({ defaultTheme, theme, open, onOpenCh
}}
placeholder="Select"
>
-
+
{Object.values(Contrast).map(item => (
-
+
{item}
-
+
))}
-
-
+
+
@@ -124,7 +124,7 @@ const ThemeDialog: FC = ({ defaultTheme, theme, open, onOpenCh
Adjust colors for different types of color blindness.
- {
@@ -133,14 +133,14 @@ const ThemeDialog: FC = ({ defaultTheme, theme, open, onOpenCh
}}
placeholder="Select"
>
-
+
{Object.values(ColorAdjustment).map(item => (
-
+
{item}
-
+
))}
-
-
+
+
diff --git a/packages/ui/src/components/theme-selector/color-select.tsx b/packages/ui/src/components/theme-selector/color-select.tsx
index 42fbcf125b..3f02efabfe 100644
--- a/packages/ui/src/components/theme-selector/color-select.tsx
+++ b/packages/ui/src/components/theme-selector/color-select.tsx
@@ -1,4 +1,5 @@
-import { Label, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectSeparator } from '..'
+import { Label, Select } from '@/components'
+
import { ColorType, ContrastType, FullTheme, ModeType } from './types'
export function ColorSelect({
@@ -15,7 +16,7 @@ export function ColorSelect({
return (
Color
-
-
- Standard
-
-
- Vision Assistive
- Tritanopia
- Protanopia & Deuteranopia
-
-
-
+
+ Standard
+
+
+ Vision Assistive
+ Tritanopia
+ Protanopia & Deuteranopia
+
+
+
)
}
diff --git a/packages/ui/src/components/theme-selector/contrast-select.tsx b/packages/ui/src/components/theme-selector/contrast-select.tsx
index 8ee5236439..628dcaffa8 100644
--- a/packages/ui/src/components/theme-selector/contrast-select.tsx
+++ b/packages/ui/src/components/theme-selector/contrast-select.tsx
@@ -1,4 +1,5 @@
-import { Label, Select, SelectContent, SelectItem } from '..'
+import { Label, Select } from '@/components'
+
import { ColorType, ContrastType, FullTheme, ModeType } from './types'
export function ContrastSelect({
@@ -15,7 +16,7 @@ export function ContrastSelect({
return (
Contrast
-
-
- Standard
- Low
- High
-
-
+
+ Standard
+ Low
+ High
+
+
)
}
diff --git a/packages/ui/src/components/theme-selector/mode-select.tsx b/packages/ui/src/components/theme-selector/mode-select.tsx
index 25e830f087..7454488990 100644
--- a/packages/ui/src/components/theme-selector/mode-select.tsx
+++ b/packages/ui/src/components/theme-selector/mode-select.tsx
@@ -1,4 +1,5 @@
-import { Label, Select, SelectContent, SelectItem } from '..'
+import { Label, Select } from '@/components'
+
import { ColorType, ContrastType, FullTheme, ModeType } from './types'
export function ModeSelect({
@@ -15,7 +16,7 @@ export function ModeSelect({
return (
Mode
-
-
- Light
- Dark
- System
-
-
+
+ Light
+ Dark
+ System
+
+
)
}
diff --git a/packages/ui/src/views/labels/components/label-form-color-and-name-group.tsx b/packages/ui/src/views/labels/components/label-form-color-and-name-group.tsx
index 6bb83d4ca3..369f12d7ee 100644
--- a/packages/ui/src/views/labels/components/label-form-color-and-name-group.tsx
+++ b/packages/ui/src/views/labels/components/label-form-color-and-name-group.tsx
@@ -1,7 +1,7 @@
import { FC } from 'react'
import { UseFormRegister } from 'react-hook-form'
-import { Button, Icon, Input, Select, SelectContent, SelectItem } from '@/components'
+import { Button, Icon, Input, Select } from '@/components'
import { ColorsEnum, CreateLabelFormFields, TranslationStore } from '@/views'
const SelectColorMarker = {
@@ -50,18 +50,18 @@ export const LabelFormColorAndNameGroup: FC = (
return (
-
-
+
+
{Object.values(ColorsEnum).map(color => (
-
+
-
+
))}
-
-
+
+
= (props:
Branch:
-
-
+
+
{branches?.map(branch => (
-
+
{branch}
-
+
))}
-
-
+
+
) : null}
diff --git a/packages/ui/src/views/pipelines/create-pipeline-dialog/create-pipeline-dialog.tsx b/packages/ui/src/views/pipelines/create-pipeline-dialog/create-pipeline-dialog.tsx
index a4ba326946..e9aa15ee3e 100644
--- a/packages/ui/src/views/pipelines/create-pipeline-dialog/create-pipeline-dialog.tsx
+++ b/packages/ui/src/views/pipelines/create-pipeline-dialog/create-pipeline-dialog.tsx
@@ -1,18 +1,7 @@
import { useEffect, useState } from 'react'
import { useForm } from 'react-hook-form'
-import {
- Alert,
- Button,
- ControlGroup,
- Dialog,
- Fieldset,
- FormWrapper,
- Input,
- Select,
- SelectContent,
- SelectItem
-} from '@/components'
+import { Alert, Button, ControlGroup, Dialog, Fieldset, FormWrapper, Input, Select } from '@/components'
import { zodResolver } from '@hookform/resolvers/zod'
import { z } from 'zod'
@@ -116,7 +105,7 @@ export function CreatePipelineDialog(props: CreatePipelineDialogProps) {
-
-
+
{branchNames?.map(branchName => (
-
+
{branchName}
-
+
))}
-
-
+
+
diff --git a/packages/ui/src/views/profile-settings/components/profile-settings-token-create-dialog.tsx b/packages/ui/src/views/profile-settings/components/profile-settings-token-create-dialog.tsx
index 2238023a16..86a49839cb 100644
--- a/packages/ui/src/views/profile-settings/components/profile-settings-token-create-dialog.tsx
+++ b/packages/ui/src/views/profile-settings/components/profile-settings-token-create-dialog.tsx
@@ -1,18 +1,7 @@
import { FC, useEffect } from 'react'
import { SubmitHandler, useForm } from 'react-hook-form'
-import {
- Alert,
- Button,
- CopyButton,
- Dialog,
- Fieldset,
- FormWrapper,
- Input,
- Select,
- SelectContent,
- SelectItem
-} from '@/components'
+import { Alert, Button, CopyButton, Dialog, Fieldset, FormWrapper, Input, Select } from '@/components'
import { zodResolver } from '@hookform/resolvers/zod'
import { TranslationStore } from '@views/repo'
import { z } from 'zod'
@@ -170,23 +159,23 @@ export const ProfileSettingsTokenCreateDialog: FC
- handleSelectChange('lifetime', value)}
label={t('views:profileSettings.expiration', 'Expiration')}
placeholder={t('views:profileSettings.select', 'Select')}
error={errors.lifetime?.message?.toString()}
>
-
+
{expirationOptions.map(expirationOption => {
return (
-
+
{expirationOption.label}
-
+
)
})}
-
-
+
+
{isValid && (
{watch('lifetime') === 'never' ? (
diff --git a/packages/ui/src/views/project/project-import.tsx b/packages/ui/src/views/project/project-import.tsx
index 335e2e14a6..f5879fac32 100644
--- a/packages/ui/src/views/project/project-import.tsx
+++ b/packages/ui/src/views/project/project-import.tsx
@@ -12,8 +12,6 @@ import {
Input,
Option,
Select,
- SelectContent,
- SelectItem,
Spacer,
Text
} from '@/components'
@@ -104,18 +102,18 @@ export function ImportProjectPage({ onFormSubmit, onFormCancel, isLoading, apiEr
{/* provider */}
- handleSelectChange('provider', value)}
placeholder="Select"
label="Git provider"
>
-
+
{ProviderOptionsEnum &&
Object.values(ProviderOptionsEnum)?.map(option => {
return (
-
{option}
-
+
)
})}
-
-
+
+
{watch('provider') === ProviderOptionsEnum.GITHUB_ENTERPRISE && (
diff --git a/packages/ui/src/views/project/project-members/components/invite-member-dialog.tsx b/packages/ui/src/views/project/project-members/components/invite-member-dialog.tsx
index 8299ea5cad..19eb8c3890 100644
--- a/packages/ui/src/views/project/project-members/components/invite-member-dialog.tsx
+++ b/packages/ui/src/views/project/project-members/components/invite-member-dialog.tsx
@@ -1,18 +1,7 @@
import { FC, forwardRef, useEffect, useMemo, useState } from 'react'
import { useForm } from 'react-hook-form'
-import {
- Alert,
- Avatar,
- Button,
- ControlGroup,
- Dialog,
- Fieldset,
- FormWrapper,
- Select,
- SelectContent,
- SelectItem
-} from '@/components'
+import { Alert, Avatar, Button, ControlGroup, Dialog, Fieldset, FormWrapper, Select } from '@/components'
import { PrincipalType } from '@/types'
import { InviteMemberDialogProps, InviteMemberFormFields } from '@/views'
import { zodResolver } from '@hookform/resolvers/zod'
@@ -122,7 +111,7 @@ export const InviteMemberDialog: FC = ({
- = ({
!!invitedMemberFullModel &&
}
>
- = ({
}}
>
{principals.map(principal => (
-
+
-
+
))}
-
-
+
+
- handleSelectChange('role', value)}
@@ -161,17 +150,17 @@ export const InviteMemberDialog: FC = ({
!!selectedRoleFullModel && {selectedRoleFullModel.label}
}
>
-
+
{roleOptions.map(option => (
-
+
{option.label}
{option.description}
-
+
))}
-
-
+
+
diff --git a/packages/ui/src/views/repo/pull-request/details/components/conversation/pull-request-status-select-button.tsx b/packages/ui/src/views/repo/pull-request/details/components/conversation/pull-request-status-select-button.tsx
index db738b0a63..459e499487 100644
--- a/packages/ui/src/views/repo/pull-request/details/components/conversation/pull-request-status-select-button.tsx
+++ b/packages/ui/src/views/repo/pull-request/details/components/conversation/pull-request-status-select-button.tsx
@@ -1,6 +1,6 @@
import { useEffect, useMemo, useState } from 'react'
-import { Select, SelectContent, SelectItem } from '@components/index'
+import { Select } from '@/components'
import { useEmitCodeCommentStatus } from '@views/repo/pull-request/hooks/useEmitCodeCommentStatus'
import { TypesPullReq } from '@views/repo/pull-request/pull-request.types'
@@ -19,12 +19,12 @@ interface StatusButtonProps {
}
const StatusButton = ({ codeCommentStatus, onChange }: StatusButtonProps) => (
-
-
- Active
- Resolved
-
-
+
+
+ Active
+ Resolved
+
+
)
StatusButton.displayName = 'StatusButton'
diff --git a/packages/ui/src/views/repo/repo-branch/components/create-branch-dialog.tsx b/packages/ui/src/views/repo/repo-branch/components/create-branch-dialog.tsx
index 652d2d4b75..e0a56cfb19 100644
--- a/packages/ui/src/views/repo/repo-branch/components/create-branch-dialog.tsx
+++ b/packages/ui/src/views/repo/repo-branch/components/create-branch-dialog.tsx
@@ -1,19 +1,7 @@
import { useEffect, useMemo } from 'react'
import { useForm } from 'react-hook-form'
-import {
- Alert,
- Button,
- ControlGroup,
- Dialog,
- Fieldset,
- FormWrapper,
- Icon,
- Input,
- Select,
- SelectContent,
- SelectItem
-} from '@/components'
+import { Alert, Button, ControlGroup, Dialog, Fieldset, FormWrapper, Icon, Input, Select } from '@/components'
import { zodResolver } from '@hookform/resolvers/zod'
import { z } from 'zod'
@@ -118,7 +106,7 @@ export function CreateBranchDialog({
- handleSelectChange('target', value)}
@@ -131,7 +119,7 @@ export function CreateBranchDialog({
}
disabled={isLoadingBranches || !branches?.length}
>
-
branch?.name && (
-
+
{branch.name}
-
+
)
)}
-
-
+
+
diff --git a/packages/ui/src/views/repo/repo-create/index.tsx b/packages/ui/src/views/repo/repo-create/index.tsx
index e1536a8a54..4d5f8f3956 100644
--- a/packages/ui/src/views/repo/repo-create/index.tsx
+++ b/packages/ui/src/views/repo/repo-create/index.tsx
@@ -15,8 +15,6 @@ import {
RadioButton,
RadioGroup,
Select,
- SelectContent,
- SelectItem,
Spacer,
StyledLink,
Text,
@@ -154,7 +152,7 @@ export function RepoCreatePage({
{/* GITIGNORE */}
- handleSelectChange('gitignore', value)}
@@ -163,20 +161,20 @@ export function RepoCreatePage({
error={errors.gitignore?.message?.toString()}
caption="Choose which files not to track from a list of templates."
>
-
+
{!!gitIgnoreOptions &&
gitIgnoreOptions.map(option => (
-
+
{option}
-
+
))}
-
-
+
+
{/* LICENSE */}
- handleSelectChange('license', value)}
@@ -185,15 +183,15 @@ export function RepoCreatePage({
error={errors.license?.message?.toString()}
caption="A license tells others what they can and can't do with your code."
>
-
+
{licenseOptions &&
licenseOptions?.map(option => (
-
+
{option.label}
-
+
))}
-
-
+
+
diff --git a/packages/ui/src/views/repo/repo-import/repo-import-mulitple.tsx b/packages/ui/src/views/repo/repo-import/repo-import-mulitple.tsx
index da709acd11..fc3a70aabb 100644
--- a/packages/ui/src/views/repo/repo-import/repo-import-mulitple.tsx
+++ b/packages/ui/src/views/repo/repo-import/repo-import-mulitple.tsx
@@ -11,8 +11,6 @@ import {
Input,
Option,
Select,
- SelectContent,
- SelectItem,
Spacer,
Text
} from '@/components'
@@ -163,24 +161,24 @@ export function RepoImportMultiplePage({
{/* provider */}
- handleSelectChange('provider', value)}
placeholder="Select"
label="Git provider"
>
-
+
{ProviderOptionsEnum &&
Object.values(ProviderOptionsEnum)?.map(option => {
return (
-
+
{option}
-
+
)
})}
-
-
+
+
diff --git a/packages/ui/src/views/repo/repo-import/repo-import.tsx b/packages/ui/src/views/repo/repo-import/repo-import.tsx
index 192c0c99c0..fd00daae1e 100644
--- a/packages/ui/src/views/repo/repo-import/repo-import.tsx
+++ b/packages/ui/src/views/repo/repo-import/repo-import.tsx
@@ -12,8 +12,6 @@ import {
Input,
Option,
Select,
- SelectContent,
- SelectItem,
Spacer,
Text,
Textarea
@@ -163,24 +161,24 @@ export function RepoImportPage({
{/* provider */}
- handleSelectChange('provider', value)}
placeholder="Select"
label="Provider"
>
-
+
{ProviderOptionsEnum &&
Object.values(ProviderOptionsEnum)?.map(option => {
return (
-
+
{option}
-
+
)
})}
-
-
+
+