fix(ux): add back replay project notice, add right slot to tabs (#39167)
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 126 KiB After Width: | Height: | Size: 116 KiB |
|
Before Width: | Height: | Size: 128 KiB After Width: | Height: | Size: 118 KiB |
BIN
frontend/__snapshots__/lemon-ui-lemon-tabs--right-slot--dark.png
Normal file
|
After Width: | Height: | Size: 7.8 KiB |
|
After Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 6.1 KiB |
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.1 KiB |
@@ -1,5 +1,4 @@
|
||||
.LemonTabs {
|
||||
--lemon-tabs-gap: 2rem;
|
||||
--lemon-tabs-margin-bottom: 1rem;
|
||||
--lemon-tabs-content-padding: 0.75rem 0;
|
||||
|
||||
@@ -9,7 +8,6 @@
|
||||
align-self: stretch;
|
||||
|
||||
&--small {
|
||||
--lemon-tabs-gap: 1rem;
|
||||
--lemon-tabs-margin-bottom: 0.5rem;
|
||||
--lemon-tabs-content-padding: 0.375rem 0;
|
||||
}
|
||||
@@ -25,8 +23,8 @@
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-shrink: 0;
|
||||
gap: var(--lemon-tabs-gap);
|
||||
align-items: stretch;
|
||||
justify-content: space-between;
|
||||
margin-bottom: var(--lemon-tabs-margin-bottom);
|
||||
overflow-x: auto;
|
||||
list-style: none;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Meta, StoryFn, StoryObj } from '@storybook/react'
|
||||
import { useState } from 'react'
|
||||
|
||||
import { LemonButton } from '../LemonButton'
|
||||
import { LemonTab, LemonTabs as LemonTabsComponent } from './LemonTabs'
|
||||
|
||||
type Story = StoryObj<typeof LemonTabsComponent>
|
||||
@@ -55,3 +56,12 @@ Default.args = {}
|
||||
|
||||
export const Small: Story = Template.bind({})
|
||||
Small.args = { size: 'small' }
|
||||
|
||||
export const RightSlot: Story = Template.bind({})
|
||||
RightSlot.args = {
|
||||
rightSlot: (
|
||||
<LemonButton type="secondary" size="small">
|
||||
Right slot
|
||||
</LemonButton>
|
||||
),
|
||||
}
|
||||
|
||||
@@ -38,6 +38,8 @@ export interface LemonTabsProps<T extends string | number> {
|
||||
barClassName?: string
|
||||
className?: string
|
||||
sceneInset?: boolean
|
||||
/** Pass in JSX to be sticky to the right of the tabs. */
|
||||
rightSlot?: React.ReactNode
|
||||
}
|
||||
|
||||
interface LemonTabsCSSProperties extends React.CSSProperties {
|
||||
@@ -54,6 +56,7 @@ export function LemonTabs<T extends string | number>({
|
||||
className,
|
||||
'data-attr': dataAttr,
|
||||
sceneInset = false,
|
||||
rightSlot,
|
||||
}: LemonTabsProps<T>): JSX.Element {
|
||||
const { containerRef, selectionRef, sliderWidth, sliderOffset, transitioning } = useSliderPositioning<
|
||||
HTMLUListElement,
|
||||
@@ -83,50 +86,62 @@ export function LemonTabs<T extends string | number>({
|
||||
data-attr={dataAttr}
|
||||
>
|
||||
<ul className={cn('LemonTabs__bar', barClassName)} role="tablist" ref={containerRef}>
|
||||
{realTabs.map((tab) => {
|
||||
const content = (
|
||||
<>
|
||||
{tab.label}
|
||||
{tab.tooltip && <IconInfo className="ml-1 text-base shrink-0" />}
|
||||
</>
|
||||
)
|
||||
return (
|
||||
<Tooltip
|
||||
key={tab.key}
|
||||
title={tab.tooltip}
|
||||
placement="top"
|
||||
offset={0}
|
||||
docLink={tab.tooltipDocLink}
|
||||
>
|
||||
<li
|
||||
className={cn('LemonTabs__tab', tab.key === activeKey && 'LemonTabs__tab--active')}
|
||||
onClick={onChange ? () => onChange(tab.key) : undefined}
|
||||
role="tab"
|
||||
aria-selected={tab.key === activeKey}
|
||||
tabIndex={0}
|
||||
onKeyDown={
|
||||
onChange
|
||||
? (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
onChange(tab.key)
|
||||
}
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
ref={tab.key === activeKey ? selectionRef : undefined}
|
||||
data-attr={tab['data-attr']}
|
||||
<div
|
||||
className={cn('flex gap-x-4 md:gap-x-8', {
|
||||
'gap-x-2': size === 'small',
|
||||
'pr-4': rightSlot,
|
||||
})}
|
||||
>
|
||||
{realTabs.map((tab) => {
|
||||
const content = (
|
||||
<>
|
||||
{tab.label}
|
||||
{tab.tooltip && <IconInfo className="ml-1 text-base shrink-0" />}
|
||||
</>
|
||||
)
|
||||
return (
|
||||
<Tooltip
|
||||
key={tab.key}
|
||||
title={tab.tooltip}
|
||||
placement="top"
|
||||
offset={0}
|
||||
docLink={tab.tooltipDocLink}
|
||||
>
|
||||
{tab.link ? (
|
||||
<Link className="LemonTabs__tab-content" to={tab.link}>
|
||||
{content}
|
||||
</Link>
|
||||
) : (
|
||||
<div className="LemonTabs__tab-content">{content}</div>
|
||||
)}
|
||||
</li>
|
||||
</Tooltip>
|
||||
)
|
||||
})}
|
||||
<li
|
||||
className={cn('LemonTabs__tab', tab.key === activeKey && 'LemonTabs__tab--active')}
|
||||
onClick={onChange ? () => onChange(tab.key) : undefined}
|
||||
role="tab"
|
||||
aria-selected={tab.key === activeKey}
|
||||
tabIndex={0}
|
||||
onKeyDown={
|
||||
onChange
|
||||
? (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
onChange(tab.key)
|
||||
}
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
ref={tab.key === activeKey ? selectionRef : undefined}
|
||||
data-attr={tab['data-attr']}
|
||||
>
|
||||
{tab.link ? (
|
||||
<Link className="LemonTabs__tab-content" to={tab.link}>
|
||||
{content}
|
||||
</Link>
|
||||
) : (
|
||||
<div className="LemonTabs__tab-content">{content}</div>
|
||||
)}
|
||||
</li>
|
||||
</Tooltip>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
{rightSlot && (
|
||||
<div className="mb-[1px] flex gap-x-2 shrink-0 items-center justify-end sticky right-0 bg-primary pr-4">
|
||||
{rightSlot}
|
||||
</div>
|
||||
)}
|
||||
</ul>
|
||||
{activeTab && 'content' in activeTab && (
|
||||
<div className={cn('LemonTabs__content', sceneInset && 'p-4')} key={activeKey}>
|
||||
|
||||
@@ -279,7 +279,6 @@ export const sceneConfigurations: Record<Scene | string, SceneConfig> = {
|
||||
name: 'Settings',
|
||||
activityScope: ActivityScope.REPLAY,
|
||||
defaultDocsPath: '/docs/session-replay',
|
||||
hideProjectNotice: true,
|
||||
},
|
||||
[Scene.ReplaySingle]: {
|
||||
projectBased: true,
|
||||
@@ -292,7 +291,6 @@ export const sceneConfigurations: Record<Scene | string, SceneConfig> = {
|
||||
name: 'Session replay',
|
||||
activityScope: ActivityScope.REPLAY,
|
||||
defaultDocsPath: '/docs/session-replay',
|
||||
hideProjectNotice: true,
|
||||
layout: 'app-full-scene-height',
|
||||
},
|
||||
[Scene.RevenueAnalytics]: {
|
||||
|
||||
@@ -251,12 +251,11 @@ export function SessionRecordingsPageTabs(): JSX.Element {
|
||||
|
||||
return (
|
||||
// TRICKY @adamleithp: since session replay doesn't want a scene title section, we need to add our SceneActions to the top of the page
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex flex-col gap-2 relative">
|
||||
<LemonTabs
|
||||
activeKey={tab}
|
||||
className={cn('flex -mt-4')}
|
||||
// TRICKY @adamleithp: we need to add a right padding to the tabs bar to account for the SceneActions
|
||||
barClassName="mb-0 pr-48"
|
||||
barClassName="mb-0"
|
||||
onChange={(t) => router.actions.push(urls.replay(t as ReplayTabs))}
|
||||
sceneInset
|
||||
tabs={ReplayPageTabs.map((replayTab): LemonTab<string> => {
|
||||
@@ -275,11 +274,8 @@ export function SessionRecordingsPageTabs(): JSX.Element {
|
||||
'data-attr': replayTab['data-attr'],
|
||||
}
|
||||
})}
|
||||
rightSlot={<Header />}
|
||||
/>
|
||||
{/* TRICKY @adamleithp: position the actions to the right of the tabs bar absolutely */}
|
||||
<div className="absolute right-0 top-0 pt-[6px] pr-4 bg-primary flex gap-x-2 shrink-0">
|
||||
<Header />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||