Skip to content
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(contentful-apps): Stepper editor and Step editor apps #16435

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 155 additions & 0 deletions apps/contentful-apps/components/EntryListSearch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import { useRef, useState } from 'react'
import { useDebounce } from 'react-use'
import {
CollectionProp,
EntryProps,
KeyValueMap,
QueryOptions,
} from 'contentful-management'
import {
Box,
EntryCard,
Pagination,
Spinner,
Stack,
Text,
TextInput,
} from '@contentful/f36-components'
import { useCMA, useSDK } from '@contentful/react-apps-toolkit'

import { DEFAULT_LOCALE } from '../constants'

const SEARCH_DEBOUNCE_TIME_IN_MS = 300

interface EntryListSearchProps {
contentTypeId: string
contentTypeLabel: string
contentTypeTitleField: string
itemsPerPage?: number
onEntryClick?: (entry: EntryProps) => void
query?: QueryOptions
}

export const EntryListSearch = ({
itemsPerPage = 4,
contentTypeId,
contentTypeLabel,
contentTypeTitleField,
onEntryClick,
query,
}: EntryListSearchProps) => {
const sdk = useSDK()
const cma = useCMA()

const searchValueRef = useRef('')
const [searchValue, setSearchValue] = useState('')
const [listItemResponse, setListItemResponse] =
useState<CollectionProp<EntryProps<KeyValueMap>>>()
const [loading, setLoading] = useState(false)
const [page, setPage] = useState(0)
const pageRef = useRef(0)
const [counter, setCounter] = useState(0)

const skip = itemsPerPage * page

useDebounce(
async () => {
setLoading(true)
try {
const response = await cma.entry.getMany({
query: {
content_type: contentTypeId,
limit: itemsPerPage,
skip,
[`fields.${contentTypeTitleField}[match]`]: searchValue,
'sys.archivedAt[exists]': false,
...query,
},
})
if (
searchValueRef.current === searchValue &&
pageRef.current === page
) {
setListItemResponse(response)
}
} finally {
setLoading(false)
}
},
SEARCH_DEBOUNCE_TIME_IN_MS,
[page, searchValue, counter],
)

return (
<Box style={{ display: 'flex', flexFlow: 'column nowrap', gap: '24px' }}>
<TextInput
placeholder="Search for an entry"
value={searchValue}
onChange={(ev) => {
searchValueRef.current = ev.target.value
setSearchValue(ev.target.value)
setPage(0)
pageRef.current = 0
}}
/>

<Box
style={{
display: 'flex',
justifyContent: 'center',
visibility: loading ? 'visible' : 'hidden',
}}
>
<Spinner />
</Box>

{listItemResponse?.items?.length > 0 && (
<>
<Box style={{ minHeight: '440px' }}>
<Stack flexDirection="column" spacing="spacingL">
{listItemResponse.items.map((item) => (
<EntryCard
key={item.sys.id}
contentType={contentTypeLabel}
title={
item.fields[contentTypeTitleField]?.[DEFAULT_LOCALE] ||
'Untitled'
}
onClick={() => {
if (onEntryClick) {
onEntryClick(item)
return
}

sdk.navigator
.openEntry(item.sys.id, {
slideIn: { waitForClose: true },
})
.then(() => {
setCounter((c) => c + 1)
})
}}
/>
))}
</Stack>
</Box>
<Pagination
activePage={page}
itemsPerPage={itemsPerPage}
totalItems={listItemResponse.total}
onPageChange={(newPage) => {
pageRef.current = newPage
setPage(newPage)
}}
/>
</>
)}

{listItemResponse?.items?.length === 0 && (
<Box style={{ display: 'flex', justifyContent: 'center' }}>
<Text>No entry was found</Text>
</Box>
)}
</Box>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,11 @@ import { useDebounce } from 'react-use'
import { CollectionProp, EntryProps, KeyValueMap } from 'contentful-management'
import dynamic from 'next/dynamic'
import { EditorExtensionSDK } from '@contentful/app-sdk'
import {
Box,
Button,
EntryCard,
FormControl,
Pagination,
Spinner,
Stack,
Text,
TextInput,
} from '@contentful/f36-components'
import { Box, Button, FormControl } from '@contentful/f36-components'
import { PlusIcon } from '@contentful/f36-icons'
import { useCMA, useSDK } from '@contentful/react-apps-toolkit'

import { EntryListSearch } from '../../../EntryListSearch'
import { mapLocalesToFieldApis } from '../../utils'

const SEARCH_DEBOUNCE_TIME_IN_MS = 300
Expand Down Expand Up @@ -189,70 +180,11 @@ export const GenericListEditor = () => {
<Button startIcon={<PlusIcon />}>Add item</Button>
</Box>
</Box>
<Box style={{ display: 'flex', flexFlow: 'column nowrap', gap: '24px' }}>
<TextInput
placeholder="Search for a list item"
value={searchValue}
onChange={(ev) => {
searchValueRef.current = ev.target.value
setSearchValue(ev.target.value)
setPage(0)
pageRef.current = 0
}}
/>

<Box
style={{
display: 'flex',
justifyContent: 'center',
visibility: loading ? 'visible' : 'hidden',
}}
>
<Spinner />
</Box>

{listItemResponse?.items?.length > 0 && (
<>
<Box style={{ minHeight: '440px' }}>
<Stack flexDirection="column" spacing="spacingL">
{listItemResponse.items.map((item) => (
<EntryCard
key={item.sys.id}
contentType="List Item"
title={
item.fields.internalTitle?.[defaultLocale] ?? 'Untitled'
}
onClick={() => {
sdk.navigator
.openEntry(item.sys.id, {
slideIn: { waitForClose: true },
})
.then(() => {
setCounter((c) => c + 1)
})
}}
/>
))}
</Stack>
</Box>
<Pagination
activePage={page}
itemsPerPage={LIST_ITEMS_PER_PAGE}
totalItems={listItemResponse.total}
onPageChange={(newPage) => {
pageRef.current = newPage
setPage(newPage)
}}
/>
</>
)}

{listItemResponse?.items?.length === 0 && (
<Box style={{ display: 'flex', justifyContent: 'center' }}>
<Text>No item was found</Text>
</Box>
)}
</Box>
<EntryListSearch
contentTypeId="genericListItem"
contentTypeLabel="List Item"
contentTypeTitleField="internalTitle"
/>
</Box>
)
}
Loading