Skip to content

Commit

Permalink
Merge branch 'master' into tabulator-feature-flag
Browse files Browse the repository at this point in the history
* master:
  `quilt_summarize.json` editor (#4254)
  • Loading branch information
nl0 committed Dec 17, 2024
2 parents 09fefa9 + 4a0a562 commit 365d4f3
Show file tree
Hide file tree
Showing 24 changed files with 2,192 additions and 68 deletions.
1 change: 1 addition & 0 deletions catalog/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ where verb is one of
## Changes

- [Added] Admin: Tabulator Settings (open query) ([#4255](https://github.com/quiltdata/quilt/pull/4255))
- [Added] Visual editor for `quilt_summarize.json` ([#4254](https://github.com/quiltdata/quilt/pull/4254))
- [Added] Support "html" type in `quilt_summarize.json` ([#4252](https://github.com/quiltdata/quilt/pull/4252))
- [Fixed] Resolve caching issues where changes in `.quilt/{workflows,catalog}` were not applied ([#4245](https://github.com/quiltdata/quilt/pull/4245))
- [Added] A shortcut to enable adding files to a package from the current bucket ([#4245](https://github.com/quiltdata/quilt/pull/4245))
Expand Down
2 changes: 2 additions & 0 deletions catalog/app/components/FileEditor/CreateFile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export function useCreateFileInBucket(bucket: string, path: string) {
const { urls } = NamedRoutes.use()
const history = RRDom.useHistory()

// TODO: put this into FileEditor/routes
const toFile = React.useCallback(
(name: string) => urls.bucketFile(bucket, join(path, name), { edit: true }),
[bucket, path, urls],
Expand All @@ -48,6 +49,7 @@ export function useCreateFileInPackage({ bucket, name }: PackageHandle, prefix?:
const { urls } = NamedRoutes.use()
const history = RRDom.useHistory()

// TODO: put this into FileEditor/routes
const toFile = React.useCallback(
(fileName: string) => {
const next = urls.bucketPackageDetail(bucket, name, { action: 'revisePackage' })
Expand Down
159 changes: 159 additions & 0 deletions catalog/app/components/FileEditor/FileEditor.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import * as React from 'react'
import renderer from 'react-test-renderer'
import { renderHook } from '@testing-library/react-hooks'

import AsyncResult from 'utils/AsyncResult'

import { useState } from './State'
import { Editor } from './FileEditor'

jest.mock('utils/AWS', () => ({ S3: { use: () => {} } }))

jest.mock('./Skeleton', () => () => <div id="Skeleton" />)

jest.mock('utils/NamedRoutes', () => ({
...jest.requireActual('utils/NamedRoutes'),
use: jest.fn(() => ({ urls: {} })),
}))

jest.mock(
'react-router-dom',
jest.fn(() => ({
...jest.requireActual('react-router-dom'),
useParams: jest.fn(() => ({ bucket: 'b', key: 'k' })),
useLocation: jest.fn(() => ({ search: '?edit=true' })),
})),
)

jest.mock(
'components/Preview/Display',
jest.fn(() => () => <div id="error" />),
)

const getObjectData = jest.fn((cases: any) =>
AsyncResult.case(cases, AsyncResult.Ok({ Body: 'body' })),
)

jest.mock(
'components/Preview/loaders/utils',
jest.fn(() => ({
...jest.requireActual('components/Preview/loaders/utils'),
useObjectGetter: () => ({
case: getObjectData,
}),
})),
)

jest.mock(
'./TextEditor',
jest.fn(() => ({ initialValue }: { initialValue: string }) => (
<div id="Text Editor">
<span id="initialValue">{initialValue}</span>
</div>
)),
)

jest.mock(
'constants/config',
jest.fn(() => ({})),
)

const loadMode = jest.fn(() => 'fulfilled')

jest.mock(
'./loader',
jest.fn(() => ({
loadMode: jest.fn(() => loadMode()),
detect: () => 'text',
useWriteData: () => {},
})),
)

describe('components/FileEditor/FileEditor', () => {
describe('Editor', () => {
const handle = { bucket: 'b', key: 'k' }
const hookData = renderHook(() => useState(handle))
const state = hookData.result.current
it('shows skeleton when loadMode is not resolved yet', () => {
loadMode.mockImplementationOnce(() => {
throw Promise.resolve(null)
})
const tree = renderer
.create(
<Editor
{...state}
className="root"
editing={{ brace: 'json' }}
handle={handle}
/>,
)
.toJSON()
expect(tree).toMatchSnapshot()
})

it('shows TextEditor', () => {
const tree = renderer
.create(
<Editor
{...state}
className="root"
editing={{ brace: 'json' }}
handle={handle}
/>,
)
.toJSON()
expect(tree).toMatchSnapshot()
})

it('shows an empty TextEditor', () => {
const tree = renderer
.create(
<Editor
{...state}
empty
className="root"
editing={{ brace: 'json' }}
handle={handle}
/>,
)
.toJSON()
expect(tree).toMatchSnapshot()
})

it('shows Skeleton while loading data', () => {
getObjectData.mockImplementationOnce((cases: any) =>
AsyncResult.case(cases, AsyncResult.Pending()),
)
const { result } = renderHook(() => useState(handle))
const tree = renderer
.create(
<Editor
{...result.current}
className="root"
editing={{ brace: 'json' }}
handle={handle}
/>,
)
.toJSON()
expect(tree).toMatchSnapshot()
})

it('shows Error when loading failed', () => {
getObjectData.mockImplementationOnce((cases: any) =>
AsyncResult.case(cases, AsyncResult.Err(new Error('Fail'))),
)
const { result } = renderHook(() => useState(handle))
const tree = renderer
.create(
<Editor
{...result.current}
className="root"
editing={{ brace: 'json' }}
handle={handle}
/>,
)
.toJSON()
expect(tree).toMatchSnapshot()
})
})
})
76 changes: 31 additions & 45 deletions catalog/app/components/FileEditor/FileEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import { EditorInputType } from './types'

export { detect, isSupportedFileType } from './loader'

const QuiltSummarize = React.lazy(() => import('./QuiltConfigEditor/QuiltSummarize'))

interface EditorProps extends EditorState {
className: string
editing: EditorInputType
Expand All @@ -26,39 +28,34 @@ interface EditorProps extends EditorState {

function EditorSuspended({
className,
saving,
saving: disabled,
empty,
error,
handle,
onChange,
editing,
}: EditorProps) {
const disabled = saving
if (editing.brace !== '__quiltConfig') {
if (editing.brace !== '__quiltConfig' && editing.brace !== '__quiltSummarize') {
loadMode(editing.brace || 'plain_text') // TODO: loaders#typeText.brace
}

const data = PreviewUtils.useObjectGetter(handle, { noAutoFetch: empty })
const initialProps = {
className,
disabled,
error,
onChange,
initialValue: '',
}
if (empty)
return editing.brace === '__quiltConfig' ? (
<QuiltConfigEditor
className={className}
handle={handle}
disabled={disabled}
error={error}
onChange={onChange}
initialValue=""
/>
) : (
<TextEditor
autoFocus
className={className}
error={error}
initialValue=""
onChange={onChange}
type={editing}
/>
)
switch (editing.brace) {
case '__quiltConfig':
return <QuiltConfigEditor {...initialProps} handle={handle} />
case '__quiltSummarize':
return <QuiltSummarize {...initialProps} />
default:
return <TextEditor {...initialProps} autoFocus type={editing} />
}
return data.case({
_: () => <Skeleton />,
Err: (
Expand All @@ -70,30 +67,19 @@ function EditorSuspended({
</div>
),
Ok: (response: { Body: Buffer }) => {
const value = response.Body.toString('utf-8')
if (editing.brace === '__quiltConfig') {
return (
<QuiltConfigEditor
className={className}
handle={handle}
disabled={disabled}
error={error}
onChange={onChange}
initialValue={value}
/>
)
const initialValue = response.Body.toString('utf-8')
const props = {
...initialProps,
initialValue,
}
switch (editing.brace) {
case '__quiltConfig':
return <QuiltConfigEditor {...props} handle={handle} />
case '__quiltSummarize':
return <QuiltSummarize {...props} />
default:
return <TextEditor {...props} autoFocus type={editing} />
}
return (
<TextEditor
autoFocus
className={className}
disabled={disabled}
error={error}
onChange={onChange}
type={editing}
initialValue={value}
/>
)
},
})
}
Expand Down
1 change: 1 addition & 0 deletions catalog/app/components/FileEditor/HelpLinks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import * as NamedRoutes from 'utils/NamedRoutes'
import StyledLink from 'utils/StyledLink'
import StyledTooltip from 'utils/StyledTooltip'

// TODO: put this into FileEditor/routes
function useRouteToEditFile(handle: Model.S3.S3ObjectLocation) {
const { urls } = NamedRoutes.use()
const { pathname, search } = RRDom.useLocation()
Expand Down
Loading

0 comments on commit 365d4f3

Please sign in to comment.