Style part page
This commit is contained in:
@@ -16,6 +16,7 @@ export const schema = gql`
|
|||||||
type Query {
|
type Query {
|
||||||
parts: [Part!]!
|
parts: [Part!]!
|
||||||
part(id: String!): Part
|
part(id: String!): Part
|
||||||
|
partByUserAndTitle(userName: String! partTitle: String!): Part
|
||||||
}
|
}
|
||||||
|
|
||||||
input CreatePartInput {
|
input CreatePartInput {
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ export const schema = gql`
|
|||||||
updatedAt: DateTime!
|
updatedAt: DateTime!
|
||||||
image: String
|
image: String
|
||||||
bio: String
|
bio: String
|
||||||
Part: [Part]!
|
Parts: [Part]!
|
||||||
|
Part(partTitle: String!): Part
|
||||||
Reaction: [PartReaction]!
|
Reaction: [PartReaction]!
|
||||||
Comment: [Comment]!
|
Comment: [Comment]!
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,21 @@ export const part = ({ id }) => {
|
|||||||
where: { id },
|
where: { id },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
export const partByUserAndTitle = async ({ userName, partTitle }) => {
|
||||||
|
const user = await db.user.findOne({
|
||||||
|
where: {
|
||||||
|
userName
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return db.part.findOne({
|
||||||
|
where: {
|
||||||
|
title_userId: {
|
||||||
|
title: partTitle,
|
||||||
|
userId: user.id,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export const createPart = async ({ input }) => {
|
export const createPart = async ({ input }) => {
|
||||||
return db.part.create({
|
return db.part.create({
|
||||||
|
|||||||
@@ -54,7 +54,11 @@ export const deleteUser = ({ id }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const User = {
|
export const User = {
|
||||||
Part: (_obj, { root }) => db.user.findOne({ where: { id: root.id } }).Part(),
|
Parts: (_obj, { root }) => db.user.findOne({ where: { id: root.id } }).Part(),
|
||||||
|
Part: (_obj, { root, ...rest }) => db.part.findOne({where: { title_userId: {
|
||||||
|
title: _obj.partTitle,
|
||||||
|
userId: root.id,
|
||||||
|
}}}),
|
||||||
Reaction: (_obj, { root }) =>
|
Reaction: (_obj, { root }) =>
|
||||||
db.user.findOne({ where: { id: root.id } }).Reaction(),
|
db.user.findOne({ where: { id: root.id } }).Reaction(),
|
||||||
Comment: (_obj, { root }) =>
|
Comment: (_obj, { root }) =>
|
||||||
|
|||||||
@@ -17,25 +17,34 @@ const Routes = () => {
|
|||||||
<Route notfound page={NotFoundPage} />
|
<Route notfound page={NotFoundPage} />
|
||||||
|
|
||||||
<Route path="/u/{userName}" page={User2Page} name="user2" />
|
<Route path="/u/{userName}" page={User2Page} name="user2" />
|
||||||
|
<Route path="/u/{userName}/{partTitle}" page={Part2Page} name="part2" />
|
||||||
|
{/* <Route path="/u/{userName}/{partTitle}/ide" page={Part2Page} name="part2" /> */}
|
||||||
|
|
||||||
|
{/* Ownership enforced routes */}
|
||||||
<Route path="/u/{userName}/edit" page={EditUser2Page} name="editUser2" />
|
<Route path="/u/{userName}/edit" page={EditUser2Page} name="editUser2" />
|
||||||
|
{/* <Route path="/u/{userName}/{partTitle}/edit" page={Part2Page} name="part2" /> */}
|
||||||
|
{/* End ownership enforced routes */}
|
||||||
|
|
||||||
{/* GENERATED ROUTES BELOW, probably going to clean these up and delete most of them, but the CRUD functionality is useful for now */}
|
{/* GENERATED ROUTES BELOW, probably going to clean these up and delete most of them, but the CRUD functionality is useful for now */}
|
||||||
<Route path="/part-reactions/new" page={NewPartReactionPage} name="newPartReaction" />
|
{/* All private by default for safety and because the routes that are left after clean up will probably be admin pages */}
|
||||||
<Route path="/part-reactions/{id}/edit" page={EditPartReactionPage} name="editPartReaction" />
|
<Private unauthenticated="home" role="admin">
|
||||||
<Route path="/part-reactions/{id}" page={PartReactionPage} name="partReaction" />
|
<Route path="/part-reactions/new" page={NewPartReactionPage} name="newPartReaction" />
|
||||||
<Route path="/part-reactions" page={PartReactionsPage} name="partReactions" />
|
<Route path="/part-reactions/{id}/edit" page={EditPartReactionPage} name="editPartReaction" />
|
||||||
<Route path="/parts/new" page={NewPartPage} name="newPart" />
|
<Route path="/part-reactions/{id}" page={PartReactionPage} name="partReaction" />
|
||||||
<Route path="/parts/{id}/edit" page={EditPartPage} name="editPart" />
|
<Route path="/part-reactions" page={PartReactionsPage} name="partReactions" />
|
||||||
<Route path="/parts/{id}" page={PartPage} name="part" />
|
<Route path="/parts/new" page={NewPartPage} name="newPart" />
|
||||||
<Route path="/parts" page={PartsPage} name="parts" />
|
<Route path="/parts/{id}/edit" page={EditPartPage} name="editPart" />
|
||||||
<Route path="/comments/new" page={NewCommentPage} name="newComment" />
|
<Route path="/parts/{id}" page={PartPage} name="part" />
|
||||||
<Route path="/comments/{id}/edit" page={EditCommentPage} name="editComment" />
|
<Route path="/parts" page={PartsPage} name="parts" />
|
||||||
<Route path="/comments/{id}" page={CommentPage} name="comment" />
|
<Route path="/comments/new" page={NewCommentPage} name="newComment" />
|
||||||
<Route path="/comments" page={CommentsPage} name="comments" />
|
<Route path="/comments/{id}/edit" page={EditCommentPage} name="editComment" />
|
||||||
<Route path="/users/new" page={NewUserPage} name="newUser" />
|
<Route path="/comments/{id}" page={CommentPage} name="comment" />
|
||||||
<Route path="/users/{id}/edit" page={EditUserPage} name="editUser" />
|
<Route path="/comments" page={CommentsPage} name="comments" />
|
||||||
<Route path="/users/{id}" page={UserPage} name="user" />
|
<Route path="/users/new" page={NewUserPage} name="newUser" />
|
||||||
<Route path="/users" page={UsersPage} name="users" />
|
<Route path="/users/{id}/edit" page={EditUserPage} name="editUser" />
|
||||||
|
<Route path="/users/{id}" page={UserPage} name="user" />
|
||||||
|
<Route path="/users" page={UsersPage} name="users" />
|
||||||
|
</Private>
|
||||||
</Router>
|
</Router>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
18
web/src/components/Breadcrumb/Breadcrumb.js
Normal file
18
web/src/components/Breadcrumb/Breadcrumb.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { getActiveClasses } from "get-active-classes"
|
||||||
|
|
||||||
|
const Breadcrumb = ({ userName, partTitle, className }) => {
|
||||||
|
return (
|
||||||
|
<h3 className={getActiveClasses("text-2xl font-roboto", className)}>
|
||||||
|
<div className="w-1 inline-block text-indigo-800 bg-indigo-800 mr-2">.</div>
|
||||||
|
<span className="text-gray-500">
|
||||||
|
{userName}
|
||||||
|
</span>
|
||||||
|
<div className="w-1 inline-block bg-gray-400 text-gray-400 mx-3 transform -skew-x-20" >.</div>
|
||||||
|
<span className="text-indigo-800">
|
||||||
|
{partTitle}
|
||||||
|
</span>
|
||||||
|
</h3>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Breadcrumb
|
||||||
7
web/src/components/Breadcrumb/Breadcrumb.stories.js
Normal file
7
web/src/components/Breadcrumb/Breadcrumb.stories.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import Breadcrumb from './Breadcrumb'
|
||||||
|
|
||||||
|
export const generated = () => {
|
||||||
|
return <Breadcrumb />
|
||||||
|
}
|
||||||
|
|
||||||
|
export default { title: 'Components/Breadcrumb' }
|
||||||
11
web/src/components/Breadcrumb/Breadcrumb.test.js
Normal file
11
web/src/components/Breadcrumb/Breadcrumb.test.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { render } from '@redwoodjs/testing'
|
||||||
|
|
||||||
|
import Breadcrumb from './Breadcrumb'
|
||||||
|
|
||||||
|
describe('Breadcrumb', () => {
|
||||||
|
it('renders successfully', () => {
|
||||||
|
expect(() => {
|
||||||
|
render(<Breadcrumb />)
|
||||||
|
}).not.toThrow()
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -1,16 +1,19 @@
|
|||||||
|
import { getActiveClasses } from 'get-active-classes'
|
||||||
import Svg from 'src/components/Svg'
|
import Svg from 'src/components/Svg'
|
||||||
|
|
||||||
const Button = ({onClick, iconName, children}) => {
|
const Button = ({onClick, iconName, children, className, shouldAnimateHover}) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
|
||||||
<button
|
<button
|
||||||
className="flex items-center bg-indigo-200 bg-opacity-50 rounded-xl p-2 px-6 text-indigo-600"
|
className={getActiveClasses(
|
||||||
|
"flex items-center bg-opacity-50 rounded-xl p-2 px-6 text-indigo-600",
|
||||||
|
{'mx-px transform hover:-translate-y-px transition-all duration-150': shouldAnimateHover},
|
||||||
|
className
|
||||||
|
)}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
<Svg className="w-6 ml-4" name={iconName} />
|
<Svg className="w-6 ml-4" name={iconName} />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,19 @@
|
|||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import Fab from '@material-ui/core/Fab'
|
import { getActiveClasses } from "get-active-classes"
|
||||||
import IconButton from '@material-ui/core/IconButton'
|
|
||||||
import Popover from '@material-ui/core/Popover'
|
import Popover from '@material-ui/core/Popover'
|
||||||
|
|
||||||
import Svg from 'src/components/Svg'
|
import Svg from 'src/components/Svg'
|
||||||
|
|
||||||
const emojiMenu = ['🏆', '❤️', '👍', '😊', '😄', '🚀', '👏', '🙌']
|
const emojiMenu = ['❤️', '👍', '😄', '🙌']
|
||||||
|
// const emojiMenu = ['🏆', '❤️', '👍', '😊', '😄', '🚀', '👏', '🙌']
|
||||||
|
const noEmotes =[{
|
||||||
|
emoji: '❤️',
|
||||||
|
count: 0,
|
||||||
|
}]
|
||||||
|
|
||||||
const EmojiReaction = ({ emotes, callback = () => {} }) => {
|
const textShadow = {textShadow: '0 4px 6px rgba(0, 0, 0, 0.3)'}
|
||||||
|
|
||||||
|
const EmojiReaction = ({ emotes, onEmote = () => {}, className }) => {
|
||||||
const [isOpen, setIsOpen] = useState(false)
|
const [isOpen, setIsOpen] = useState(false)
|
||||||
const [anchorEl, setAnchorEl] = useState(null)
|
const [anchorEl, setAnchorEl] = useState(null)
|
||||||
const [popoverId, setPopoverId] = useState(undefined)
|
const [popoverId, setPopoverId] = useState(undefined)
|
||||||
@@ -32,45 +39,63 @@ const EmojiReaction = ({ emotes, callback = () => {} }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleEmojiClick = (emoji) => {
|
const handleEmojiClick = (emoji) => {
|
||||||
callback(emoji)
|
onEmote(emoji)
|
||||||
closePopover()
|
closePopover()
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return (
|
||||||
<div className="flex justify-between">
|
<>
|
||||||
<Fab size="medium" variant="round" aria-describedby={popoverId} onClick={togglePopover}>
|
<div className={getActiveClasses("h-10 relative overflow-hidden py-4", className)}>
|
||||||
<div className="bg-gray-200 border-2 m-px border-gray-300 text-gray-500 absolute inset-0 rounded-full flex justify-center items-center">
|
<div className="absolute left-0 w-8 inset-y-0 z-10 flex items-center bg-gray-100">
|
||||||
<Svg name="dots-vertical" />
|
<div className="h-8 w-8 relative" aria-describedby={popoverId} onClick={togglePopover}>
|
||||||
|
<button
|
||||||
|
className="bg-gray-200 border-2 m-px w-full h-full border-gray-300 rounded-full flex justify-center items-center shadow-md hover:shadow-lg hover:border-indigo-200 transform hover:-translate-y-px transition-all duration-150"
|
||||||
|
>
|
||||||
|
<Svg className="h-8 w-8 pt-px mt-px text-gray-500" name="dots-vertical" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Fab>
|
|
||||||
|
|
||||||
<div>
|
<div className="whitespace-no-wrap absolute right-0 inset-y-0 flex items-center flex-row-reverse">
|
||||||
{emotes.map((emote, i) => (
|
{(emotes.length ? emotes : noEmotes).map((emote, i) => (
|
||||||
<IconButton key={`${emote.emoji}--${i}`} onClick={() => handleEmojiClick(emote.emoji)}>
|
<span
|
||||||
{emote.emoji} <span>{emote.count}</span>
|
className="rounded-full tracking-wide hover:bg-indigo-100 p-1 mx-px transform hover:-translate-y-px transition-all duration-150"
|
||||||
</IconButton>
|
style={textShadow}
|
||||||
))}
|
key={`${emote.emoji}--${i}`}
|
||||||
|
onClick={() => handleEmojiClick(emote.emoji)}
|
||||||
|
>
|
||||||
|
<span className="text-lg pr-1">{emote.emoji}</span><span className="text-sm font-ropa-sans">{emote.count}</span>
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>,
|
<Popover
|
||||||
<Popover
|
id={popoverId}
|
||||||
id={popoverId}
|
open={isOpen}
|
||||||
open={isOpen}
|
anchorEl={anchorEl}
|
||||||
anchorEl={anchorEl}
|
onClose={closePopover}
|
||||||
onClose={closePopover}
|
anchorOrigin={{
|
||||||
anchorOrigin={{
|
vertical: 'bottom',
|
||||||
vertical: 'bottom',
|
horizontal: 'left',
|
||||||
horizontal: 'right',
|
}}
|
||||||
}}
|
transformOrigin={{
|
||||||
transformOrigin={{
|
vertical: 'top',
|
||||||
vertical: 'top',
|
horizontal: 'left',
|
||||||
horizontal: 'center',
|
}}
|
||||||
}}
|
>
|
||||||
>
|
<div className="py-2 mt-2">
|
||||||
{emojiMenu.map((emoji, i) => (
|
{emojiMenu.map((emoji, i) => (
|
||||||
<IconButton key={`${emoji}-${i}}`} onClick={() => handleEmojiClick(emoji)}>{emoji}</IconButton>
|
<button
|
||||||
))}
|
className="p-2"
|
||||||
</Popover>,
|
style={textShadow}
|
||||||
]
|
key={`${emoji}-${i}}`}
|
||||||
|
onClick={() => handleEmojiClick(emoji)}
|
||||||
|
>{emoji}</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Popover>
|
||||||
|
</>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default EmojiReaction
|
export default EmojiReaction
|
||||||
|
|||||||
110
web/src/components/Part2Cell/Part2Cell.js
Normal file
110
web/src/components/Part2Cell/Part2Cell.js
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
import Editor from "rich-markdown-editor";
|
||||||
|
|
||||||
|
// import Part from 'src/components/Part'
|
||||||
|
import ImageUploader from 'src/components/ImageUploader'
|
||||||
|
import Breadcrumb from 'src/components/Breadcrumb'
|
||||||
|
import EmojiReaction from 'src/components/EmojiReaction'
|
||||||
|
import Button from 'src/components/Button'
|
||||||
|
|
||||||
|
export const QUERY = gql`
|
||||||
|
query FIND_PART_BY_USERNAME_TITLE($userName: String!, $partTitle: String!) {
|
||||||
|
userPart: userName(userName: $userName) {
|
||||||
|
name
|
||||||
|
userName
|
||||||
|
bio
|
||||||
|
image
|
||||||
|
id
|
||||||
|
Part(partTitle: $partTitle) {
|
||||||
|
id
|
||||||
|
title
|
||||||
|
description
|
||||||
|
code
|
||||||
|
mainImage
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
userId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
export const Loading = () => <div>Loading...</div>
|
||||||
|
|
||||||
|
export const Empty = () => <div>Empty</div>
|
||||||
|
|
||||||
|
export const Failure = ({ error }) => <div>Error: {error.message}</div>
|
||||||
|
|
||||||
|
export const Success = ({ userPart, variables }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="grid mt-20 gap-8" style={{gridTemplateColumns: 'auto 12rem minmax(12rem, 42rem) 10rem auto'}}>
|
||||||
|
|
||||||
|
{/* Side column */}
|
||||||
|
<aside className="col-start-2">
|
||||||
|
<ImageUploader
|
||||||
|
className="rounded-half rounded-br-lg shadow-md border-2 border-gray-200 border-solid"
|
||||||
|
onImageUpload={() => {}}
|
||||||
|
aspectRatio={1}
|
||||||
|
imageUrl={userPart.image === 'abc' ? '': userPart.image}
|
||||||
|
/>
|
||||||
|
<h4 className="text-indigo-800 text-xl underline text-right py-4">{userPart?.name}</h4>
|
||||||
|
<div className="h-px bg-indigo-200 mb-4" />
|
||||||
|
{/* TODO hook up to emoji data properly */}
|
||||||
|
<EmojiReaction
|
||||||
|
// emotes={[{emoji: '❤️',count: 3},{emoji: '😁',count: 2},{emoji: '😜',count: 2},{emoji: '🤩',count: 2},{emoji: '🤣',count: 2},{emoji: '🙌',count: 2},{emoji: '🚀',count: 2}]}
|
||||||
|
emotes={[{emoji: '❤️',count: 3},{emoji: '😁',count: 2}]}
|
||||||
|
// emotes={[]}
|
||||||
|
onEmote={() => {}}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
className="mt-6 ml-auto hover:shadow-lg bg-gradient-to-r from-transparent to-indigo-100"
|
||||||
|
shouldAnimateHover
|
||||||
|
iconName="plus"
|
||||||
|
onClick={() => {}}
|
||||||
|
>
|
||||||
|
Comments 11
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
className="mt-4 ml-auto shadow-md hover:shadow-lg bg-indigo-200"
|
||||||
|
shouldAnimateHover
|
||||||
|
iconName="plus"
|
||||||
|
onClick={() => {}}
|
||||||
|
>
|
||||||
|
Open IDE
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
className="mt-4 ml-auto shadow-md hover:shadow-lg bg-indigo-200"
|
||||||
|
shouldAnimateHover
|
||||||
|
iconName="pencil"
|
||||||
|
onClick={() => {}}
|
||||||
|
>
|
||||||
|
Edit Part
|
||||||
|
</Button>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
{/* main project center column */}
|
||||||
|
<section className="col-start-3">
|
||||||
|
<Breadcrumb className="mb-8" userName={userPart.userName} partTitle={userPart?.Part?.title}/>
|
||||||
|
{ userPart?.Part?.mainImage && <ImageUploader
|
||||||
|
className="rounded-lg shadow-md border-2 border-gray-200 border-solid"
|
||||||
|
onImageUpload={() => {}}
|
||||||
|
aspectRatio={16/9}
|
||||||
|
imageUrl={userPart?.Part?.mainImage}
|
||||||
|
/>}
|
||||||
|
<div name="description" className="markdown-overrides rounded-lg shadow-md bg-white p-12 my-8 min-h-md">
|
||||||
|
<Editor
|
||||||
|
defaultValue={userPart?.Part?.description || ''}
|
||||||
|
readOnly
|
||||||
|
// readOnly={!isEditable}
|
||||||
|
// onChange={(bioFn) => setInput({
|
||||||
|
// ...input,
|
||||||
|
// bio: bioFn(),
|
||||||
|
// })}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
6
web/src/components/Part2Cell/Part2Cell.mock.js
Normal file
6
web/src/components/Part2Cell/Part2Cell.mock.js
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
// Define your own mock data here:
|
||||||
|
export const standard = (/* vars, { ctx, req } */) => ({
|
||||||
|
part2: {
|
||||||
|
id: 42,
|
||||||
|
},
|
||||||
|
})
|
||||||
20
web/src/components/Part2Cell/Part2Cell.stories.js
Normal file
20
web/src/components/Part2Cell/Part2Cell.stories.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { Loading, Empty, Failure, Success } from './Part2Cell'
|
||||||
|
import { standard } from './Part2Cell.mock'
|
||||||
|
|
||||||
|
export const loading = () => {
|
||||||
|
return Loading ? <Loading /> : null
|
||||||
|
}
|
||||||
|
|
||||||
|
export const empty = () => {
|
||||||
|
return Empty ? <Empty /> : null
|
||||||
|
}
|
||||||
|
|
||||||
|
export const failure = () => {
|
||||||
|
return Failure ? <Failure error={new Error('Oh no')} /> : null
|
||||||
|
}
|
||||||
|
|
||||||
|
export const success = () => {
|
||||||
|
return Success ? <Success {...standard()} /> : null
|
||||||
|
}
|
||||||
|
|
||||||
|
export default { title: 'Cells/Part2Cell' }
|
||||||
26
web/src/components/Part2Cell/Part2Cell.test.js
Normal file
26
web/src/components/Part2Cell/Part2Cell.test.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { render, screen } from '@redwoodjs/testing'
|
||||||
|
import { Loading, Empty, Failure, Success } from './Part2Cell'
|
||||||
|
import { standard } from './Part2Cell.mock'
|
||||||
|
|
||||||
|
describe('Part2Cell', () => {
|
||||||
|
test('Loading renders successfully', () => {
|
||||||
|
render(<Loading />)
|
||||||
|
// Use screen.debug() to see output
|
||||||
|
expect(screen.getByText('Loading...')).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Empty renders successfully', async () => {
|
||||||
|
render(<Empty />)
|
||||||
|
expect(screen.getByText('Empty')).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Failure renders successfully', async () => {
|
||||||
|
render(<Failure error={new Error('Oh no')} />)
|
||||||
|
expect(screen.getByText(/Oh no/i)).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Success renders successfully', async () => {
|
||||||
|
render(<Success part2={standard().part2} />)
|
||||||
|
expect(screen.getByText(/42/i)).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -45,9 +45,9 @@ const UserProfile = ({user, isEditable, loading, onSave, error}) => {
|
|||||||
...textFields,
|
...textFields,
|
||||||
})} isEditable={isEditable}/>
|
})} isEditable={isEditable}/>
|
||||||
{isEditable ?
|
{isEditable ?
|
||||||
<Button iconName="plus" onClick={() => onSave(user.userName, input)}>Save Profile</Button> : // TODO replace pencil with a save icon
|
<Button className="bg-indigo-200" iconName="plus" onClick={() => onSave(user.userName, input)}>Save Profile</Button> : // TODO replace pencil with a save icon
|
||||||
canEdit ?
|
canEdit ?
|
||||||
<Button iconName="pencil" onClick={() => navigate(routes.editUser2({userName: user.userName}))}>Edit Profile</Button>:
|
<Button className="bg-indigo-200" iconName="pencil" onClick={() => navigate(routes.editUser2({userName: user.userName}))}>Edit Profile</Button>:
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
12
web/src/pages/Part2Page/Part2Page.js
Normal file
12
web/src/pages/Part2Page/Part2Page.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import MainLayout from 'src/layouts/MainLayout'
|
||||||
|
import Part2Cell from 'src/components/Part2Cell'
|
||||||
|
|
||||||
|
const Part2Page = ({userName, partTitle}) => {
|
||||||
|
return (
|
||||||
|
<MainLayout>
|
||||||
|
<Part2Cell userName={userName} partTitle={partTitle}/>
|
||||||
|
</MainLayout>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Part2Page
|
||||||
7
web/src/pages/Part2Page/Part2Page.stories.js
Normal file
7
web/src/pages/Part2Page/Part2Page.stories.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import Part2Page from './Part2Page'
|
||||||
|
|
||||||
|
export const generated = () => {
|
||||||
|
return <Part2Page />
|
||||||
|
}
|
||||||
|
|
||||||
|
export default { title: 'Pages/Part2Page' }
|
||||||
11
web/src/pages/Part2Page/Part2Page.test.js
Normal file
11
web/src/pages/Part2Page/Part2Page.test.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { render } from '@redwoodjs/testing'
|
||||||
|
|
||||||
|
import Part2Page from './Part2Page'
|
||||||
|
|
||||||
|
describe('Part2Page', () => {
|
||||||
|
it('renders successfully', () => {
|
||||||
|
expect(() => {
|
||||||
|
render(<Part2Page />)
|
||||||
|
}).not.toThrow()
|
||||||
|
})
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user