Skip to content
Draft
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,49 @@
import type { Meta, StoryObj } from 'storybook'

import OnboardingFlagsTable, {
OnboardingFlagRow,
} from 'components/pages/onboarding/OnboardingFlagsTable'

const demoFlag: OnboardingFlagRow = {
description: 'Controls the demo button shown to your users',
enabled: true,
name: 'show_demo_button',
}

const meta: Meta<typeof OnboardingFlagsTable> = {
args: {
flags: [demoFlag],
onToggle: () => {},
status: 'connected',
},
component: OnboardingFlagsTable,
parameters: {
docs: {
description: {
component:
'The "Your flags" card from the onboarding flow, reusing the product FeatureName / Tag / Switch. Prop-driven: the page owns the flag data and the persisted Dev toggle. `connected` lifts the card with the accent border and glow; `waiting` dims it until the first evaluation arrives.',
},
},
layout: 'padded',
},
title: 'Pages/Onboarding/OnboardingFlagsTable',
}
export default meta

type Story = StoryObj<typeof OnboardingFlagsTable>

export const Connected: Story = {}

export const Waiting: Story = {
args: { status: 'waiting' },
}

export const Off: Story = {
args: { flags: [{ ...demoFlag, enabled: false }] },
}

export const WithTag: Story = {
args: {
flags: [{ ...demoFlag, tags: [{ color: '#6837FC', label: 'demo' }] }],
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import type { Meta, StoryObj } from 'storybook'

import OnboardingTerminal from 'components/pages/onboarding/OnboardingTerminal'

const meta: Meta<typeof OnboardingTerminal> = {
args: {
connected: false,
featureName: 'show_demo_button',
installCopied: false,
snippetCopied: false,
},
component: OnboardingTerminal,
parameters: {
docs: {
description: {
component:
'The onboarding verify console. The checklist ticks as the user acts (copy install, copy snippet), and the first evaluation flips the badge to LIVE and prints the connection receipt. Always dark, since a terminal reads the same in light and dark mode.',
},
},
layout: 'padded',
},
title: 'Pages/Onboarding/OnboardingTerminal',
}
export default meta

type Story = StoryObj<typeof OnboardingTerminal>

export const Listening: Story = {}

export const InstallCopied: Story = {
args: { installCopied: true },
}

export const SnippetsCopied: Story = {
args: { installCopied: true, snippetCopied: true },
}

export const Connected: Story = {
args: { connected: true, installCopied: true, snippetCopied: true },
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ const GhostInput = ({
onKeyDown={onKeyDown}
aria-label={ariaLabel}
spellCheck={false}
// Opt out of browser autofill + password-manager overlays (1Password,
// LastPass); their icons would overlap the trailing edit pencil.
autoComplete='off'
data-1p-ignore
data-lpignore='true'
style={{ width: inputWidth }}
/>
</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,18 @@ export type CodeCardProps = {
language: string
// Left side of the card header (e.g. the language label or npm/yarn pills).
headerLeft: ReactNode
// Fires when the user copies the code (drives the verify checklist).
onCopy?: () => void
}

// Owns its own "Copied" feedback so each card is independent. Highlight escapes
// the body for display; Copy uses the raw string.
const CodeCard: FC<CodeCardProps> = ({ code, headerLeft, language }) => {
const CodeCard: FC<CodeCardProps> = ({
code,
headerLeft,
language,
onCopy,
}) => {
const { copied, copy } = useCopyFeedback()

return (
Expand All @@ -25,7 +32,10 @@ const CodeCard: FC<CodeCardProps> = ({ code, headerLeft, language }) => {
theme='primary'
size='small'
className='ms-auto'
onClick={() => copy(code)}
onClick={() => {
copy(code)
onCopy?.()
}}
>
<span
className='d-inline-flex align-items-center gap-1'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,18 @@ const PACKAGE_MANAGERS: PackageManager[] = ['npm', 'yarn']
export type ConnectYourCodePanelProps = {
environmentKey: string
featureName: string
onCopyInstall?: () => void
onCopyWire?: () => void
}

// "Connect your code" tab: pick an SDK, then copy the install + wire snippets,
// pre-filled with the real env key and flag this onboarding created.
// pre-filled with the real env key and flag this onboarding created. The copy
// actions feed the verify checklist.
const ConnectYourCodePanel: FC<ConnectYourCodePanelProps> = ({
environmentKey,
featureName,
onCopyInstall,
onCopyWire,
}) => {
const [sdkLang, setSdkLang] = useState<SdkLang>(SDK_LANGS[0])
const [installPm, setInstallPm] = useState<PackageManager>('npm')
Expand All @@ -40,6 +45,7 @@ const ConnectYourCodePanel: FC<ConnectYourCodePanelProps> = ({
<CodeCard
code={installCode}
language='bash'
onCopy={onCopyInstall}
headerLeft={
sdkSnippet.installYarn ? (
<div className='onboarding-connect__pm d-inline-flex'>
Expand Down Expand Up @@ -74,6 +80,7 @@ const ConnectYourCodePanel: FC<ConnectYourCodePanelProps> = ({
<CodeCard
code={sdkSnippet.wire}
language={sdkSnippet.language}
onCopy={onCopyWire}
headerLeft={
<span className='onboarding-connect__codecard-lang'>
{sdkLang.label}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ const CONNECT_TABS: OnboardingTab<ConnectTab>[] = [
export type OnboardingConnectPanelProps = {
environmentKey: string
featureName: string
onCopyInstall?: () => void
onCopyWire?: () => void
}

// Two ways to connect an app to the pre-created flag: paste an agent-agnostic
Expand All @@ -37,6 +39,8 @@ export type OnboardingConnectPanelProps = {
const OnboardingConnectPanel: FC<OnboardingConnectPanelProps> = ({
environmentKey,
featureName,
onCopyInstall,
onCopyWire,
}) => {
const [tab, setTab] = useState<ConnectTab>('manual')

Expand Down Expand Up @@ -68,6 +72,8 @@ const OnboardingConnectPanel: FC<OnboardingConnectPanelProps> = ({
<ConnectYourCodePanel
environmentKey={environmentKey}
featureName={featureName}
onCopyInstall={onCopyInstall}
onCopyWire={onCopyWire}
/>
</OnboardingTabPanel>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// "Your flags" card. A normal light/dark surface (unlike the terminal), so it
// uses the semantic tokens. Connected lifts it with the action/purple border +
// glow to draw the eye; waiting dims it until the first evaluation lands.
.onboarding-flags {
display: flex;
flex-direction: column;
align-items: center;
gap: 14px;

&__title {
margin: 0;
color: var(--color-text-default);
font-size: 16px;
font-weight: 700;
}

&__table {
width: 100%;
max-width: 760px;
overflow: hidden;
border-radius: var(--radius-xl);
background: var(--color-surface-default);
// Accent border + purple glow to lift the connected card (matches the mock).
// Derived from the action colour with oklch alpha so it tracks the theme.
border: 1px solid oklch(from var(--color-border-action) l c h / 0.4);
box-shadow: 0 18px 44px 2px oklch(from var(--color-border-action) l c h / 0.2),
var(--shadow-md);
transition: opacity var(--duration-fast) var(--easing-standard),
box-shadow var(--duration-fast) var(--easing-standard),
border-color var(--duration-fast) var(--easing-standard);

// Until the SDK connects the flag is real but quiet: no accent, dimmed.
&--waiting {
border-color: var(--color-border-default);
box-shadow: none;
opacity: 0.7;
}
}

&__head {
display: flex;
align-items: center;
gap: 12px;
padding: 8px 20px;
border-bottom: 1px solid var(--color-border-default);
}

&__col {
color: var(--color-text-secondary);
font-size: 11px;
font-weight: 700;
letter-spacing: 0.5px;

&--feature {
flex: 1;
}
&--enabled {
width: 96px;
}
}

&__row {
display: flex;
align-items: center;
gap: 12px;
padding: 14px 20px;
}

&__feature {
display: flex;
flex: 1;
flex-direction: column;
gap: 4px;
}

&__name-row {
display: flex;
align-items: center;
gap: 8px;
}

&__desc {
margin: 0;
color: var(--color-text-secondary);
font-size: 12px;
}

// Match the ENABLED header column width so the toggle lines up under it.
&__toggle {
width: 96px;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import React, { FC } from 'react'
import classNames from 'classnames'
import { Tag as TTag } from 'common/types/responses'
import FeatureName from 'components/feature-summary/FeatureName'
import Tag from 'components/tags/Tag'
import Switch from 'components/Switch'
import './OnboardingFlagsTable.scss'

export type OnboardingFlagsTableStatus = 'waiting' | 'connected'

export type OnboardingFlagRow = {
name: string
description?: string
tags?: Partial<TTag>[]
enabled: boolean
}

export type OnboardingFlagsTableProps = {
status: OnboardingFlagsTableStatus
flags: OnboardingFlagRow[]
onToggle: (flag: OnboardingFlagRow, enabled: boolean) => void
// Name of the flag whose toggle is mid-flight, so its Switch disables.
togglingFlag?: string | null
}

// The "Your flags" card from the onboarding design: the pre-created flag(s) in a
// real-looking table that reuses the product FeatureName / Tag / Switch. Prop
// driven (the page owns the data and the persisted toggle, see
// useUpdateFeatureStateMutation). `connected` lifts the card with the accent
// border + glow and enables the toggle; `waiting` dims it until the first
// evaluation arrives.
const OnboardingFlagsTable: FC<OnboardingFlagsTableProps> = ({
flags,
onToggle,
status,
togglingFlag,
}) => {
const waiting = status === 'waiting'
return (
<section className='onboarding-flags'>
<h3 className='onboarding-flags__title'>Your flags</h3>
<div
className={classNames('onboarding-flags__table', {
'onboarding-flags__table--waiting': waiting,
})}
>
<div className='onboarding-flags__head'>
<span className='onboarding-flags__col onboarding-flags__col--feature'>
FEATURE
</span>
<span className='onboarding-flags__col onboarding-flags__col--enabled'>
ENABLED
</span>
</div>
{flags.map((flag) => (
<div className='onboarding-flags__row' key={flag.name}>
<div className='onboarding-flags__feature'>
<div className='onboarding-flags__name-row'>
<FeatureName name={flag.name} />
{flag.tags?.map((tag) => (
<Tag key={tag.id ?? tag.label} tag={tag} />
))}
</div>
{flag.description && (
<p className='onboarding-flags__desc'>{flag.description}</p>
)}
</div>
<div className='onboarding-flags__toggle'>
<Switch
checked={flag.enabled}
disabled={togglingFlag === flag.name}
onChange={(enabled) => onToggle(flag, enabled)}
/>
</div>
</div>
))}
</div>
</section>
)
}

export default OnboardingFlagsTable
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export { default } from './OnboardingFlagsTable'
export type {
OnboardingFlagRow,
OnboardingFlagsTableProps,
OnboardingFlagsTableStatus,
} from './OnboardingFlagsTable'
Loading
Loading