Style userProfile display and editable

This commit is contained in:
Kurt Hutten
2020-11-05 06:47:55 +11:00
parent 217cb31ed2
commit 249dbaba1a
17 changed files with 210 additions and 47 deletions

View File

@@ -16,7 +16,8 @@ const Routes = () => {
{/* <Route path="/blah/*" page={PartsPage} name="home" /> */}
<Route notfound page={NotFoundPage} />
<Route path="/u/{userName}" page={UserPage2} name="user" />
<Route path="/u/{userName}" page={User2Page} name="user2" />
<Route path="/u/{userName}/edit" page={EditUser2Page} name="editUser2" />
{/* 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" />

View File

@@ -1,6 +1,6 @@
import Svg from 'src/components/Svg'
const Button = ({onClick, children}) => {
const Button = ({onClick, iconName, children}) => {
return (
<div>
<button
@@ -8,7 +8,7 @@ const Button = ({onClick, children}) => {
onClick={onClick}
>
{children}
<Svg className="w-6 ml-4" name="pencil" />
<Svg className="w-6 ml-4" name={iconName} />
</button>
</div>
)

View File

@@ -0,0 +1,25 @@
import UserProfile from 'src/components/UserProfile'
export const QUERY = gql`
query FIND_USER_BY_ID($userName: String!) {
user: userName(userName: $userName) {
id
userName
email
createdAt
updatedAt
image
bio
}
}
`
export const Loading = () => <div>Loading...</div>
export const Empty = () => <div>Empty</div>
export const Failure = ({ error }) => <div>Error: {error.message}</div>
export const Success = ({ user }) => {
return <UserProfile user={user} isEditable />
}

View File

@@ -0,0 +1,6 @@
// Define your own mock data here:
export const standard = (/* vars, { ctx, req } */) => ({
editUser2: {
id: 42,
},
})

View File

@@ -0,0 +1,20 @@
import { Loading, Empty, Failure, Success } from './EditUser2Cell'
import { standard } from './EditUser2Cell.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/EditUser2Cell' }

View File

@@ -0,0 +1,26 @@
import { render, screen } from '@redwoodjs/testing'
import { Loading, Empty, Failure, Success } from './EditUser2Cell'
import { standard } from './EditUser2Cell.mock'
describe('EditUser2Cell', () => {
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 editUser2={standard().editUser2} />)
expect(screen.getByText(/42/i)).toBeInTheDocument()
})
})

View File

@@ -68,9 +68,9 @@ export default function ImageUploader({ onImageUpload, imageUrl, aspectRatio, cl
</div>}
{!cloudinaryId && <button className="absolute inset-0"></button>}
{!cloudinaryId && isEditable && <div className="text-indigo-500 flex items-center justify-center rounded-lg w-full h-full">
<div>
<div className="px-6 text-center">
Drop files here ...
or <span className="group flex w-full items-center justify-center py-4">
or <span className="group flex w-full items-center justify-center py-2">
<span className="bg-indigo-500 shadow rounded text-gray-200 cursor-pointer p-2 hover:shadow-lg transform hover:-translate-y-1 transition-all duration-150">upload</span>
</span>
</div>

View File

@@ -0,0 +1,29 @@
import {Fragment, useState} from 'react'
const ProfileTextInput = ({fields, isEditable, onChange= () => {}}) => {
const keyValueDisplay = Object.entries(fields)
return (
<div>
<div className="grid items-center" style={{gridTemplateColumns: 'auto 1fr'}}>
{keyValueDisplay.map(([property, value]) => (<Fragment key={property}>
<span className="capitalize text-gray-500 text-sm align-middle my-3">{property}:</span>
{
isEditable ?
<div className="relative ml-2">
<div className="absolute inset-0 mb-2 rounded bg-gray-200 shadow-inner bg-gray-100" />
<input
className=" pl-2 pt-1 text-indigo-800 font-medium text-xl mb-px pb-px bg-transparent relative"
onChange={({target}) => onChange({...fields, [property]: target.value})}
value={value}
type="text"
/>
</div>:
<span className="pl-2 text-indigo-800 font-medium text-xl mb-px pb-px">{value}</span>
}
</Fragment>))}
</div>
</div>
)
}
export default ProfileTextInput

View File

@@ -0,0 +1,7 @@
import ProfileTextInput from './ProfileTextInput'
export const generated = () => {
return <ProfileTextInput />
}
export default { title: 'Components/ProfileTextInput' }

View File

@@ -0,0 +1,11 @@
import { render } from '@redwoodjs/testing'
import ProfileTextInput from './ProfileTextInput'
describe('ProfileTextInput', () => {
it('renders successfully', () => {
expect(() => {
render(<ProfileTextInput />)
}).not.toThrow()
})
})

View File

@@ -1,8 +1,4 @@
import User from 'src/components/User'
import {Fragment} from 'react'
import ImageUploader from 'src/components/ImageUploader'
import Button from 'src/components/Button'
import Editor from "rich-markdown-editor";
import UserProfile from 'src/components/UserProfile'
export const QUERY = gql`
query FIND_USER_BY_ID($userName: String!) {
@@ -22,40 +18,6 @@ export const Loading = () => <div>Loading...</div>
export const Empty = () => <div>User not found</div>
export const Success = ({ user }) => {
const {userName, email} = user
const keyValueDisplay = Object.entries({userName, name: email}) // TODO add name field to user
return (
<>
<div className="max-w-2xl mx-auto mt-20 ">
<div className="flex" >
<div className="w-40 flex-shrink-0">
<ImageUploader
className="rounded-half rounded-br-lg shadow-md border-2 border-gray-200 border-solid"
aspectRatio={1}
imageUrl={user.image === 'abc' ? '': user.image}
/>
</div>
<div className="ml-6 flex flex-col justify-between">
<div className="grid items-center" style={{gridTemplateColumns: 'auto 1fr'}}>
{keyValueDisplay.map(([property, value]) => (<Fragment key={property}>
<span className="capitalize text-gray-500 text-sm align-middle">{property}:</span>
<span className="pl-2 text-indigo-800 font-medium text-xl mb-px pb-px align-middle">{value}</span>
</Fragment>))}
</div>
<Button>Edit Profile</Button>
</div>
</div>
<div className="mt-10">
<h3 className="text-3xl text-gray-500 font-ropa-sans">Bio:</h3>
<div name="description" className="markdown-overrides rounded-lg shadow-md bg-white p-12 my-6 min-h-md">
<Editor
defaultValue={user.bio}
readOnly
/>
</div>
</div>
</div>
</>
)
export const Success = ({user}) => {
return <UserProfile user={user} />
}

View File

@@ -0,0 +1,46 @@
import {Fragment} from 'react'
import ImageUploader from 'src/components/ImageUploader'
import Button from 'src/components/Button'
import Editor from "rich-markdown-editor";
import ProfileTextInput from 'src/components/ProfileTextInput'
const UserProfile = ({user, isEditable}) => {
console.log(isEditable)
const {userName, email} = user
const editableTextFields = {userName, name: email} // TODO add name field to user
return (
<>
<div className="max-w-2xl mx-auto mt-20 ">
<div className="flex" >
<div className="w-40 flex-shrink-0">
<ImageUploader
className="rounded-half rounded-br-lg shadow-md border-2 border-gray-200 border-solid"
aspectRatio={1}
isEditable={isEditable}
imageUrl={user.image === 'abc' ? '': user.image}
/>
</div>
<div className="ml-6 flex flex-col justify-between">
<ProfileTextInput fields={editableTextFields} isEditable={isEditable}/>
{isEditable ?
<Button iconName="plus">Save Profile</Button> : // TODO replace pencil with a save icon
<Button iconName="pencil">Edit Profile</Button>
}
</div>
</div>
<div className="mt-10">
<h3 className="text-3xl text-gray-500 font-ropa-sans">Bio:</h3>
<div name="description" className="markdown-overrides rounded-lg shadow-md bg-white p-12 my-6 min-h-md">
<Editor
defaultValue={user.bio}
readOnly={!isEditable}
/>
</div>
</div>
</div>
</>
)
}
export default UserProfile

View File

@@ -0,0 +1,7 @@
import UserProfile from './UserProfile'
export const generated = () => {
return <UserProfile />
}
export default { title: 'Components/UserProfile' }

View File

@@ -0,0 +1,11 @@
import { render } from '@redwoodjs/testing'
import UserProfile from './UserProfile'
describe('UserProfile', () => {
it('renders successfully', () => {
expect(() => {
render(<UserProfile />)
}).not.toThrow()
})
})

View File

@@ -36,7 +36,7 @@
body {
/* TODO can I use a tailwind class here? */
background-color: #E5E5E5;
background-color: #f7fafc;
}
button, input, label, textarea {

View File

@@ -0,0 +1,12 @@
import MainLayout from 'src/layouts/MainLayout'
import EditUser2Cell from 'src/components/EditUser2Cell'
const UserPage = ({ userName }) => {
return (
<MainLayout>
<EditUser2Cell userName={userName}/>
</MainLayout>
)
}
export default UserPage