Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ui): interface for the experiment flags #1724

Merged
merged 8 commits into from
Mar 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
'use client'

import { useEnableCodeBrowserQuickActionBar } from '@/lib/experiment-flags'
import { Switch } from '@/components/ui/switch'

export default function FeatureList() {
const [quickActionBar, toggleQuickActionBar] =
useEnableCodeBrowserQuickActionBar()
return (
<>
{!quickActionBar.loading && (
<div className="flex items-center space-x-4 rounded-md border p-4">
<div className="flex-1 space-y-1">
<p className="text-sm font-medium leading-none">
{quickActionBar.title}
</p>
<p className="text-sm text-muted-foreground">
{quickActionBar.description}
</p>
</div>
<Switch
checked={quickActionBar.value}
onCheckedChange={toggleQuickActionBar}
/>
</div>
)}
</>
)
}
18 changes: 18 additions & 0 deletions ee/tabby-ui/app/(dashboard)/experiments/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Metadata } from 'next'

import FeatureList from './components/feature-list'

export const metadata: Metadata = {
title: 'Experiment Flags'
}

export default function IndexPage() {
return (
<div className="mx-auto flex max-w-xl flex-col gap-3">
<h3 className="scroll-m-20 text-2xl font-semibold tracking-tight">
Experiment Flags
</h3>
<FeatureList />
</div>
)
}
2 changes: 1 addition & 1 deletion ee/tabby-ui/components/ui/switch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const Switch = React.forwardRef<
>(({ className, ...props }, ref) => (
<SwitchPrimitives.Root
className={cn(
'peer inline-flex h-[24px] w-[44px] shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input',
'peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input',
className
)}
{...props}
Expand Down
56 changes: 47 additions & 9 deletions ee/tabby-ui/lib/experiment-flags.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,32 @@
import useLocalStorage from 'use-local-storage'
import { useEffect, useState } from 'react'

const useLocalStorageForExperimentFlag = (
storageKey: string,
defaultValue: boolean
): [boolean, (value: boolean) => void, boolean] => {
const [storageValue, setStorageValue] = useState(defaultValue)
const [loading, setLoading] = useState(true)

useEffect(() => {
const value = localStorage.getItem(storageKey)
if (value) {
setStorageValue(JSON.parse(value))
}
setLoading(false)
}, [])

const upateStorageValue = (newValue: boolean) => {
setStorageValue(newValue)
localStorage.setItem(storageKey, JSON.stringify(newValue))
}

return [storageValue, upateStorageValue, loading]
}

class ExperimentFlag {
constructor(
private storageKey: string,
readonly title: string,
readonly description: string,
readonly defaultValue: boolean
) {}
Expand All @@ -21,36 +45,49 @@ class ExperimentFlag {

class ExperimentFlagFactory {
private storageKey: string
private title: string
private description: string
private defaultValue: boolean

constructor(storageKey: string, description: string, defaultValue?: boolean) {
constructor(
storageKey: string,
title: string,
description: string,
defaultValue?: boolean
) {
this.storageKey = `EXP_${storageKey}`
this.title = title
this.description = description
this.defaultValue = defaultValue ?? false
}

defineGlobalVar() {
return new ExperimentFlag(
this.storageKey,
this.title,
this.description,
this.defaultValue
)
}

defineHook() {
return (): [{ value: boolean; description: string }, () => void] => {
wwayne marked this conversation as resolved.
Show resolved Hide resolved
const [storageValue, setStorageValue] = useLocalStorage(
this.storageKey,
this.defaultValue
)
return (): [
{ value: boolean; title: string; description: string; loading: boolean },
() => void
] => {
const [storageValue, setStorageValue, loading] =
useLocalStorageForExperimentFlag(this.storageKey, this.defaultValue)

const toggleFlag = () => {
setStorageValue(!storageValue)
}

return [
{
value: storageValue,
description: this.description
title: this.title,
description: this.description,
loading
},
toggleFlag
]
Expand All @@ -60,7 +97,8 @@ class ExperimentFlagFactory {

const enableCodeBrowserQuickActionBarFactory = new ExperimentFlagFactory(
'enable_code_browser_quick_action_bar',
'Show a quick action popup upon selecting code snippets',
'Quick Action Bar',
'Enable Quick Action Bar to display a convenient toolbar when you select code, offering options to explain the code, add unit tests, and more.',
false
)

Expand Down
Loading