Skip to content

Commit

Permalink
feat: Improve group creation (#171)
Browse files Browse the repository at this point in the history
  • Loading branch information
e-fisher authored Sep 20, 2024
1 parent 72211ae commit 4a55d6c
Show file tree
Hide file tree
Showing 16 changed files with 184 additions and 234 deletions.
32 changes: 32 additions & 0 deletions src/components/NoRequestsMessage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { css } from '@emotion/react'
import { Flex, Text } from '@radix-ui/themes'
import { ReactNode } from 'react'
import grotIllustration from '@/assets/grot.svg'

interface NoRequestsMessageProps {
noRequestsMessage?: ReactNode
}

export function NoRequestsMessage({
noRequestsMessage = 'Your requests will appear here.',
}: NoRequestsMessageProps) {
return (
<Flex direction="column" align="center" gap="4" pt="8">
<img
src={grotIllustration}
role="presentation"
css={css`
width: 50%;
max-width: 300px;
`}
/>
{typeof noRequestsMessage === 'string' ? (
<Text color="gray" size="1">
{noRequestsMessage}
</Text>
) : (
noRequestsMessage
)}
</Flex>
)
}
37 changes: 20 additions & 17 deletions src/components/WebLogView/Group.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,33 @@ import {
CrossCircledIcon,
Pencil1Icon,
} from '@radix-ui/react-icons'
import { useState, KeyboardEvent, MouseEvent, useRef } from 'react'
import { KeyboardEvent, MouseEvent, useRef } from 'react'
import { Group as GroupType } from '@/types'
import { useForm } from 'react-hook-form'
import { FieldError } from '../Form'
import { mergeRefs } from '@/utils/react'
import { ErrorMessage } from '@hookform/error-message'
import { Collapsible } from '../Collapsible'
import { useOnClickOutside } from '@/utils/dom'
import styled from '@emotion/styled'
import { useClickAway } from 'react-use'

interface GroupProps {
group: GroupType
groups?: GroupType[]
length: number
children: React.ReactNode
onRename?: (group: GroupType) => void
onUpdate?: (group: GroupType) => void
}

export function Group({
group,
groups = [],
length,
children,
onRename,
onUpdate,
}: GroupProps) {
const headerRef = useRef<HTMLDivElement | null>(null)

const [isEditing, setIsEditing] = useState(false)
const canEdit = onUpdate !== undefined

const {
formState: { errors, isValid },
Expand All @@ -47,6 +46,13 @@ export function Group({
mode: 'onChange',
})

const setIsEditing = (value: boolean) => {
onUpdate?.({
...group,
isEditing: value,
})
}

const handleInputMount = (el: HTMLInputElement | null) => {
if (document.activeElement !== el) {
el?.focus()
Expand Down Expand Up @@ -79,18 +85,17 @@ export function Group({
}

const submit = ({ name }: { name: string }) => {
onRename?.({
onUpdate?.({
...group,
name,
isEditing: false,
})

setIsEditing(false)
}

useOnClickOutside({
ref: headerRef,
handler: handleSubmit(submit),
enabled: isEditing,
useClickAway(headerRef, () => {
if (!group.isEditing) return

handleSubmit(submit)()
})

const isValidName = (value: string) => {
Expand All @@ -105,8 +110,6 @@ export function Group({
return true
}

const canEdit = onRename !== undefined

const { ref: formRef, ...nameProps } = register('name', {
validate: isValidName,
})
Expand All @@ -115,7 +118,7 @@ export function Group({
<Box>
<Collapsible.Root defaultOpen>
<Collapsible.Header ref={headerRef}>
{isEditing && (
{group.isEditing && (
<Collapsible.Heading>
<InlineForm onSubmit={handleSubmit(submit)}>
<TextField.Root
Expand Down Expand Up @@ -156,7 +159,7 @@ export function Group({
</InlineForm>
</Collapsible.Heading>
)}
{!isEditing && (
{!group.isEditing && (
<Collapsible.Trigger>
<Collapsible.Heading>
<span
Expand Down
47 changes: 8 additions & 39 deletions src/components/WebLogView/WebLogView.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,30 @@
import { ReactNode } from 'react'
import { css } from '@emotion/react'
import { Flex, Text, Box } from '@radix-ui/themes'
import { isEmpty } from 'lodash-es'
import { Box } from '@radix-ui/themes'

import { Group as GroupType, ProxyData } from '@/types'
import { Row } from './Row'
import { Group } from './Group'
import grotIllustration from '@/assets/grot.svg'
import { Table } from '@/components/Table'
import { NoRequestsMessage } from '../NoRequestsMessage'

interface WebLogViewProps {
requests: ProxyData[]
groups?: GroupType[]
activeGroup?: string
selectedRequestId?: string
noRequestsMessage?: ReactNode
noRequestsMessage?: string
onSelectRequest: (data: ProxyData | null) => void
onRenameGroup?: (group: GroupType) => void
onUpdateGroup?: (group: GroupType) => void
}

export function WebLogView({
requests,
groups,
selectedRequestId,
noRequestsMessage,
onSelectRequest,
onRenameGroup,
onUpdateGroup,
noRequestsMessage,
}: WebLogViewProps) {
if (isEmpty(requests)) {
if (requests.length === 0) {
return <NoRequestsMessage noRequestsMessage={noRequestsMessage} />
}

Expand All @@ -47,7 +44,7 @@ export function WebLogView({
group={item.group}
groups={groups}
length={item.requests.length}
onRename={onRenameGroup}
onUpdate={onUpdateGroup}
>
<RequestList
requests={item.requests}
Expand Down Expand Up @@ -103,31 +100,3 @@ function RequestList({
</Table.Root>
)
}

interface NoRequestsMessageProps {
noRequestsMessage?: ReactNode
}

function NoRequestsMessage({
noRequestsMessage = 'Your requests will appear here.',
}: NoRequestsMessageProps) {
return (
<Flex direction="column" align="center" gap="4" pt="8">
<img
src={grotIllustration}
role="presentation"
css={css`
width: 50%;
max-width: 300px;
`}
/>
{typeof noRequestsMessage === 'string' ? (
<Text color="gray" size="1">
{noRequestsMessage}
</Text>
) : (
noRequestsMessage
)}
</Flex>
)
}
1 change: 1 addition & 0 deletions src/constants/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const DEFAULT_GROUP_NAME = 'Default group'
19 changes: 19 additions & 0 deletions src/hooks/useProxyDataGroups.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { DEFAULT_GROUP_NAME } from '@/constants'
import { ProxyData } from '@/types'
import { useMemo } from 'react'

/** Returns an array of unique group names from the given proxy data */
export function useProxyDataGroups(proxyData: ProxyData[]) {
return useMemo(() => {
const names = new Set(
proxyData.map((data) => data.group ?? DEFAULT_GROUP_NAME)
)

return Array.from(names).map((name) => {
return {
id: name,
name,
}
})
}, [proxyData])
}
2 changes: 1 addition & 1 deletion src/store/generator/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export function selectIsRulePreviewable(state: GeneratorStore) {
}

export function selectHasRecording(state: GeneratorStore) {
return state.requests.length > 0
return selectFilteredRequests(state).length > 0
}

export function selectFilteredRequests(state: GeneratorStore) {
Expand Down
1 change: 1 addition & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export interface K6Check {
export interface Group {
id: string
name: string
isEditing?: boolean
}

export type GroupedProxyData = Record<string, ProxyData[]>
Expand Down
40 changes: 0 additions & 40 deletions src/utils/dom.ts

This file was deleted.

3 changes: 2 additions & 1 deletion src/utils/groups.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { DEFAULT_GROUP_NAME } from '@/constants'
import { ProxyData } from '@/types'
import { groupBy } from 'lodash-es'

export function groupProxyData(requests: ProxyData[]) {
return groupBy(requests, (item) => item.group || 'Default')
return groupBy(requests, (item) => item.group || DEFAULT_GROUP_NAME)
}
3 changes: 2 additions & 1 deletion src/utils/harToProxyData.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { DEFAULT_GROUP_NAME } from '@/constants'
import { Method, ProxyData, Request, Response } from '@/types'
import { HarWithOptionalResponse } from '@/types/har'
import type { Entry } from 'har-format'
Expand All @@ -7,7 +8,7 @@ export function harToProxyData(har: HarWithOptionalResponse): ProxyData[] {
id: self.crypto.randomUUID(),
request: parseRequest(entry.request),
response: entry.response ? parseResponse(entry.response) : undefined,
group: entry.pageref || 'default',
group: entry.pageref || DEFAULT_GROUP_NAME,
}))
}

Expand Down
3 changes: 2 additions & 1 deletion src/views/Recorder/ClearRequestsButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ export function ClearRequestsButton({
<AlertDialog.Content size="2" maxWidth="450px">
<AlertDialog.Title size="3">Clear requests</AlertDialog.Title>
<AlertDialog.Description size="2">
Are you sure? This will deleted the recorded requests so far.
Are you sure? This will deleted all the recorded requests and created
groups.
</AlertDialog.Description>

<Flex gap="3" mt="4" justify="end">
Expand Down
Loading

0 comments on commit 4a55d6c

Please sign in to comment.