Skip to content

Commit 7235e66

Browse files
authored
feat: add support for viewing application local state (#456)
* feat: add support for viewing localstate * chore: extend the app state integration test * chore: pr feedback
1 parent dc60075 commit 7235e66

22 files changed

+635
-219
lines changed

src/features/app-interfaces/pages/edit-app-interface-page.test.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { EditAppInterfacePage } from './edit-app-interface-page'
1010
import { fireEvent, render, waitFor } from '@/tests/testing-library'
1111
import { useParams } from 'react-router-dom'
1212
import { getButton } from '@/tests/utils/get-button'
13+
import { NO_RESULTS_TABLE_MESSAGE } from '@/features/common/constants'
1314

1415
describe('edit-app-interface', () => {
1516
it('can edit an existing app spec', async () => {
@@ -138,7 +139,7 @@ describe('edit-app-interface', () => {
138139
const confirmButton = await getButton(component, 'Confirm')
139140
await user.click(confirmButton)
140141

141-
expect(await component.findByText('No results.')).toBeInTheDocument()
142+
expect(await component.findByText(NO_RESULTS_TABLE_MESSAGE)).toBeInTheDocument()
142143
}
143144
)
144145
})

src/features/applications/components/application-details.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,13 @@ import {
2121
applicationNameLabel,
2222
applicationBoxesTabId,
2323
applicationGlobalStateTabId,
24+
applicationLocalStateTabId,
2425
applicationGlobalStateLabel,
26+
applicationLocalStateLabel,
2527
applicationAbiMethodDefinitionsLabel,
2628
} from './labels'
27-
import { ApplicationGlobalStateTable } from './application-global-state-table'
29+
import { ApplicationStateTable } from './application-state-table'
30+
import { ApplicationLocalState } from './application-local-state'
2831
import { ApplicationBoxes } from './application-boxes'
2932
import { OverflowAutoTabsContent, Tabs, TabsList, TabsTrigger } from '@/features/common/components/tabs'
3033
import { ApplicationLiveTransactions } from './application-live-transactions'
@@ -157,12 +160,18 @@ export function ApplicationDetails({ application }: Props) {
157160
<TabsTrigger className="w-fit px-4" value={applicationGlobalStateTabId}>
158161
{applicationGlobalStateLabel}
159162
</TabsTrigger>
163+
<TabsTrigger className="w-fit px-4" value={applicationLocalStateTabId}>
164+
{applicationLocalStateLabel}
165+
</TabsTrigger>
160166
<TabsTrigger className="w-fit px-4" value={applicationBoxesTabId}>
161167
{applicationBoxesLabel}
162168
</TabsTrigger>
163169
</TabsList>
164170
<OverflowAutoTabsContent value={applicationGlobalStateTabId}>
165-
<ApplicationGlobalStateTable application={application} />
171+
<ApplicationStateTable data={application.globalState ?? []} />
172+
</OverflowAutoTabsContent>
173+
<OverflowAutoTabsContent value={applicationLocalStateTabId}>
174+
<ApplicationLocalState application={application} />
166175
</OverflowAutoTabsContent>
167176
<OverflowAutoTabsContent value={applicationBoxesTabId}>
168177
<ApplicationBoxes application={application} />
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { useCallback } from 'react'
2+
import { ApplicationStateTable } from './application-state-table'
3+
import { useApplicationLocalStateSearch } from '../data/application-local-state'
4+
import { Application } from '../models'
5+
import { XIcon } from 'lucide-react'
6+
import { MagnifyingGlassIcon } from '@radix-ui/react-icons'
7+
import { Button } from '@/features/common/components/button'
8+
import { enterAddressToViewLocalStateMessage } from './labels'
9+
10+
type Props = {
11+
application: Application
12+
}
13+
14+
export function ApplicationLocalState({ application }: Props) {
15+
const [address, setAddress, loadableResults] = useApplicationLocalStateSearch(application.id)
16+
17+
const handleInput = useCallback(
18+
(e: React.ChangeEvent<HTMLInputElement>) => {
19+
setAddress(e.target.value.trim())
20+
},
21+
[setAddress]
22+
)
23+
24+
const handleClear = useCallback(() => address && setAddress(''), [setAddress, address])
25+
26+
const data = address
27+
? loadableResults
28+
: ({ state: 'hasError', error: new Error(enterAddressToViewLocalStateMessage) } satisfies typeof loadableResults)
29+
30+
return (
31+
<div className="space-y-4 overflow-hidden">
32+
<div className="max-w-[35rem] rounded-md border border-input bg-popover text-popover-foreground">
33+
<div className="flex items-center px-3">
34+
<MagnifyingGlassIcon className="mr-2 size-4 shrink-0 opacity-50" />
35+
<input
36+
placeholder="Search by Address or NFD"
37+
type="text"
38+
aria-label="local-state-address"
39+
id="local-state-address"
40+
value={address}
41+
onChange={handleInput}
42+
className="flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50"
43+
/>
44+
<Button
45+
onClick={handleClear}
46+
variant="no-style"
47+
size="icon"
48+
aria-label="Clear local state address search"
49+
className="size-4 text-muted-foreground"
50+
>
51+
<XIcon />
52+
</Button>
53+
</div>
54+
</div>
55+
<ApplicationStateTable data={data} />
56+
</div>
57+
)
58+
}

0 commit comments

Comments
 (0)