-
Notifications
You must be signed in to change notification settings - Fork 2
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: Recording selector draft #36
Changes from 2 commits
6f794bd
33d22fe
d346e06
9f102f0
56cdc3a
7304ae4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { useEffect } from 'react' | ||
|
||
const defaultTitle = 'k6 studio' | ||
|
||
export function useWindowTitle(title: string) { | ||
going-confetti marked this conversation as resolved.
Show resolved
Hide resolved
|
||
useEffect(() => { | ||
document.title = `${defaultTitle} - ${title}` | ||
|
||
return () => { | ||
document.title = defaultTitle | ||
} | ||
}, [title]) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,12 +29,11 @@ export function AllowList() { | |
|
||
return ( | ||
<> | ||
<Button | ||
onClick={() => setShowAllowListDialog(true)} | ||
disabled={requests.length === 0} | ||
> | ||
Allowed hosts [{allowList.length}/{hosts.length}] | ||
</Button> | ||
{requests.length > 0 && ( | ||
<Button onClick={() => setShowAllowListDialog(true)}> | ||
Allowed hosts [{allowList.length}/{hosts.length}] | ||
</Button> | ||
)} | ||
Comment on lines
+32
to
+36
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not needed until we have the recording |
||
{/* Radix does not unmount dialog on close, this is need to clear local state */} | ||
{showAllowListDialog && ( | ||
<AllowListDialog | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,39 +4,28 @@ import { useEffect } from 'react' | |
|
||
import { exportScript, saveScript, saveGenerator } from './Generator.utils' | ||
import { PageHeading } from '@/components/Layout/PageHeading' | ||
import { harToProxyData } from '@/utils/harToProxyData' | ||
import { GeneratorDrawer } from './GeneratorDrawer' | ||
import { GeneratorSidebar } from './GeneratorSidebar' | ||
import { useGeneratorStore } from '@/hooks/useGeneratorStore' | ||
import { useGeneratorStore, useHasRecording } from '@/hooks/useGeneratorStore' | ||
import { TestRuleContainer } from './TestRuleContainer' | ||
import { AllowList } from './AllowList/AllowList' | ||
import { RecordingSelector } from './RecordingSelector' | ||
import { useWindowTitle } from '@/hooks/useWindowTitle' | ||
|
||
export function Generator() { | ||
const { | ||
rules, | ||
setRecording, | ||
resetRecording, | ||
filteredRequests, | ||
setRecordingPath, | ||
} = useGeneratorStore() | ||
|
||
const hasRecording = filteredRequests.length > 0 | ||
const rules = useGeneratorStore((store) => store.rules) | ||
const name = useGeneratorStore((store) => store.name) | ||
const resetRecording = useGeneratorStore((store) => store.resetRecording) | ||
const filteredRequests = useGeneratorStore((store) => store.filteredRequests) | ||
const hasRecording = useHasRecording() | ||
useWindowTitle(name) | ||
|
||
useEffect(() => { | ||
return () => { | ||
resetRecording() | ||
} | ||
}, [resetRecording]) | ||
|
||
const handleImport = async () => { | ||
const harFile = await window.studio.har.openFile() | ||
if (!harFile) return | ||
|
||
const proxyData = harToProxyData(harFile.content) | ||
setRecording(proxyData) | ||
setRecordingPath(harFile.path) | ||
} | ||
|
||
const handleExport = async () => { | ||
const script = await exportScript(filteredRequests, rules) | ||
|
||
|
@@ -46,12 +35,10 @@ export function Generator() { | |
return ( | ||
<> | ||
<PageHeading text="Generator"> | ||
<Button onClick={saveGenerator}>Save</Button> | ||
<Button onClick={handleImport}>Import HAR</Button> | ||
<RecordingSelector /> | ||
<AllowList /> | ||
<Button onClick={handleExport} disabled={!hasRecording}> | ||
Export script | ||
</Button> | ||
{hasRecording && <Button onClick={saveGenerator}>Save</Button>} | ||
{hasRecording && <Button onClick={handleExport}>Export script</Button>} | ||
Comment on lines
-49
to
+41
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Took the liberty of rearranging the buttons to have the most important ones on the right + hiding save, export, and allow list until there's an actual recording. |
||
</PageHeading> | ||
<Allotment defaultSizes={[3, 1]}> | ||
<Allotment.Pane minSize={400}> | ||
|
@@ -64,7 +51,7 @@ export function Generator() { | |
</Allotment.Pane> | ||
</Allotment> | ||
</Allotment.Pane> | ||
<Allotment.Pane minSize={300}> | ||
<Allotment.Pane minSize={300} visible={hasRecording}> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar reasoning as with the header buttons: no need to show the sidebar if it's empty |
||
<GeneratorSidebar requests={filteredRequests} /> | ||
</Allotment.Pane> | ||
</Allotment> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import { useGeneratorStore, useHasRecording } from '@/hooks/useGeneratorStore' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Some unused imports in this file, maybe we could add this plugin to clear imports automatically (we already run eslint --fix on commit) |
||
import { harToProxyData } from '@/utils/harToProxyData' | ||
import { css } from '@emotion/react' | ||
import { ArrowDownIcon, CaretDownIcon } from '@radix-ui/react-icons' | ||
import { Button, Flex, IconButton, Popover, Text } from '@radix-ui/themes' | ||
import { startRecording } from '../Recorder/Recorder.utils' | ||
import { Link } from 'react-router-dom' | ||
|
||
export function RecordingSelector() { | ||
const recordingPath = useGeneratorStore((store) => store.recordingPath) | ||
const setRecording = useGeneratorStore((store) => store.setRecording) | ||
const setRecordingPath = useGeneratorStore((store) => store.setRecordingPath) | ||
const hasRecording = useHasRecording() | ||
|
||
const fileName = recordingPath?.split('/').pop() | ||
|
||
const handleImport = async () => { | ||
const harFile = await window.studio.har.openFile() | ||
if (!harFile) return | ||
|
||
const proxyData = harToProxyData(harFile.content) | ||
setRecording(proxyData) | ||
setRecordingPath(harFile.path) | ||
} | ||
|
||
return ( | ||
<Flex align="center" gap="2"> | ||
<Text>{fileName || 'Select recording'}</Text> | ||
<Popover.Root> | ||
<Popover.Trigger> | ||
<IconButton variant="outline"> | ||
<CaretDownIcon /> | ||
</IconButton> | ||
</Popover.Trigger> | ||
<Popover.Content size="1" align="end"> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will have a scrollable/searchable list of saved recordings once we implement filesystem integration and all that |
||
<Flex direction="column" gap="2"> | ||
<Text>You don{"'"}t have saved recordings</Text> | ||
<Flex gap="2"> | ||
<Popover.Close> | ||
<Button variant="outline" asChild> | ||
<Link to="/recorder">Start recording</Link> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Currently, just redirects to the recorder page, but should also start a new recording in future |
||
</Button> | ||
</Popover.Close> | ||
<Popover.Close> | ||
<Button onClick={handleImport}>Import HAR</Button> | ||
</Popover.Close> | ||
</Flex> | ||
</Flex> | ||
</Popover.Content> | ||
</Popover.Root> | ||
</Flex> | ||
) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@e-fisher what's your favorite way of defining reusable selectors? Maybe the should be callbacks that we then import and pass to
useGeneratorStore
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pmndrs/zustand#387
would this approach work well ? Seems like a clean way to have a file defining selectors, importing them and using with the

useGeneratorStore
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes! That's what I meant by
callbacks that we then import and pass to useGeneratorStore
, I should've been more specificThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd probably vote for the callback approach just because it's a bit more explicit -
useHasRecording - hook named this way could be doing anything
useGeneratorStore(selectHasRecording) - indicates that value is read from store