fix(site): Minor UI fixes related to avatar components (#6019)

This commit is contained in:
Bruno Quaresma
2023-02-03 13:09:16 -03:00
committed by GitHub
parent 6c90701a73
commit dffd7953bc
12 changed files with 108 additions and 110 deletions

View File

@ -9,7 +9,7 @@ import { combineClasses } from "util/combineClasses"
import { firstLetter } from "./firstLetter"
export type AvatarProps = MuiAvatarProps & {
size?: "md" | "xl"
size?: "sm" | "md" | "xl"
colorScheme?: "light" | "darken"
fitImage?: boolean
}
@ -50,6 +50,11 @@ export const AvatarIcon: FC<{ src: string }> = ({ src }) => {
const useStyles = makeStyles((theme) => ({
// Size styles
sm: {
width: theme.spacing(3),
height: theme.spacing(3),
fontSize: theme.spacing(1.5),
},
// Just use the default value from theme
md: {},
xl: {

View File

@ -156,6 +156,11 @@ interface StyleProps {
const useStyles = makeStyles<Theme, StyleProps>((theme) => ({
root: {
marginBottom: theme.spacing(2),
"&:has(button) .MuiInputBase-root": {
borderTopLeftRadius: 0,
borderBottomLeftRadius: 0,
},
},
// necessary to expand the textField
// the length of the page (within the bordered filterContainer)
@ -174,7 +179,6 @@ const useStyles = makeStyles<Theme, StyleProps>((theme) => ({
inputStyles: {
height: "100%",
width: "100%",
borderRadius: `0px ${theme.shape.borderRadius}px ${theme.shape.borderRadius}px 0px`,
color: theme.palette.primary.contrastText,
backgroundColor: theme.palette.background.paper,
@ -189,7 +193,7 @@ const useStyles = makeStyles<Theme, StyleProps>((theme) => ({
paddingTop: "inherit",
paddingBottom: "inherit",
// The same as the button
minHeight: 42,
minHeight: 40,
},
},
searchIcon: {

View File

@ -0,0 +1,23 @@
import { Story } from "@storybook/react"
import { MockUser } from "testHelpers/entities"
import { UserAutocomplete, UserAutocompleteProps } from "./UserAutocomplete"
export default {
title: "components/UserAutocomplete",
component: UserAutocomplete,
}
const Template: Story<UserAutocompleteProps> = (
args: UserAutocompleteProps,
) => <UserAutocomplete {...args} />
export const Example = Template.bind({})
Example.args = {
value: MockUser,
label: "User",
}
export const NoLabel = Template.bind({})
NoLabel.args = {
value: MockUser,
}

View File

@ -1,5 +1,5 @@
import CircularProgress from "@material-ui/core/CircularProgress"
import { makeStyles, Theme } from "@material-ui/core/styles"
import { makeStyles } from "@material-ui/core/styles"
import TextField from "@material-ui/core/TextField"
import Autocomplete from "@material-ui/lab/Autocomplete"
import { useMachine } from "@xstate/react"
@ -8,29 +8,22 @@ import { Avatar } from "components/Avatar/Avatar"
import { AvatarData } from "components/AvatarData/AvatarData"
import debounce from "just-debounce-it"
import { ChangeEvent, FC, useEffect, useState } from "react"
import { combineClasses } from "util/combineClasses"
import { searchUserMachine } from "xServices/users/searchUserXService"
export type UserAutocompleteProps = {
value: User | null
onChange: (user: User | null) => void
label?: string
inputMargin?: "none" | "dense" | "normal"
inputStyles?: string
className?: string
showAvatar?: boolean
}
export const UserAutocomplete: FC<UserAutocompleteProps> = ({
value,
onChange,
className,
label,
inputMargin,
inputStyles,
showAvatar = false,
className,
}) => {
const styles = useStyles({ showAvatar })
const styles = useStyles()
const [isAutocompleteOpen, setIsAutocompleteOpen] = useState(false)
const [searchState, sendSearch] = useMachine(searchUserMachine)
const { searchResults } = searchState.context
@ -53,6 +46,9 @@ export const UserAutocomplete: FC<UserAutocompleteProps> = ({
return (
<Autocomplete
className={className}
options={searchResults}
loading={searchState.matches("searching")}
value={value}
id="user-autocomplete"
open={isAutocompleteOpen}
@ -80,22 +76,21 @@ export const UserAutocomplete: FC<UserAutocompleteProps> = ({
src={option.avatar_url}
/>
)}
options={searchResults}
loading={searchState.matches("searching")}
className={combineClasses([styles.autocomplete, className])}
renderInput={(params) => (
<TextField
{...params}
fullWidth
variant="outlined"
margin={inputMargin ?? "normal"}
label={label ?? undefined}
placeholder="User email or username"
className={inputStyles}
className={styles.textField}
InputProps={{
...params.InputProps,
onChange: handleFilterChange,
startAdornment: showAvatar && value && (
<Avatar src={value.avatar_url}>{value.username}</Avatar>
startAdornment: value && (
<Avatar size="sm" src={value.avatar_url}>
{value.username}
</Avatar>
),
endAdornment: (
<>
@ -105,6 +100,9 @@ export const UserAutocomplete: FC<UserAutocompleteProps> = ({
{params.InputProps.endAdornment}
</>
),
classes: {
root: styles.inputRoot,
},
}}
/>
)}
@ -112,54 +110,14 @@ export const UserAutocomplete: FC<UserAutocompleteProps> = ({
)
}
interface styleProps {
showAvatar: boolean
}
export const useStyles = makeStyles<Theme, styleProps>((theme) => {
return {
autocomplete: (props) => ({
width: "100%",
"& .MuiFormControl-root": {
width: "100%",
},
"& .MuiInputBase-root": {
width: "100%",
// Match button small height
height: props.showAvatar ? 60 : 40,
},
"& input": {
fontSize: 16,
padding: `${theme.spacing(0, 0.5, 0, 0.5)} !important`,
},
}),
}
})
export const UserAutocompleteInline: React.FC<UserAutocompleteProps> = (
props,
) => {
const style = useInlineStyle()
return <UserAutocomplete {...props} className={style.inline} />
}
export const useInlineStyle = makeStyles(() => {
return {
inline: {
width: "300px",
"& .MuiFormControl-root": {
margin: 0,
},
"& .MuiInputBase-root": {
// Match button small height
height: 36,
},
export const useStyles = makeStyles((theme) => ({
textField: {
"&:not(:has(label))": {
margin: 0,
},
}
})
},
inputRoot: {
paddingLeft: `${theme.spacing(1.75)}px !important`, // Same padding left as input
gap: theme.spacing(0.5),
},
}))

View File

@ -92,8 +92,6 @@ export const Workspace: FC<React.PropsWithChildren<WorkspaceProps>> = ({
const styles = useStyles()
const navigate = useNavigate()
const serverVersion = buildInfo?.version || ""
const hasTemplateIcon =
workspace.template_icon && workspace.template_icon !== ""
const buildError = Boolean(workspaceErrors[WorkspaceErrors.BUILD_ERROR]) && (
<AlertBanner
@ -159,14 +157,14 @@ export const Workspace: FC<React.PropsWithChildren<WorkspaceProps>> = ({
}
>
<Stack direction="row" spacing={3} alignItems="center">
{hasTemplateIcon && (
<Avatar
size="xl"
src={workspace.template_icon}
variant="square"
fitImage
/>
)}
<Avatar
size="xl"
src={workspace.template_icon}
variant={workspace.template_icon ? "square" : undefined}
fitImage={Boolean(workspace.template_icon)}
>
{workspace.name}
</Avatar>
<div>
<PageHeaderTitle>
{workspace.name}

View File

@ -4,7 +4,6 @@ import TableRow from "@material-ui/core/TableRow"
import KeyboardArrowRight from "@material-ui/icons/KeyboardArrowRight"
import { AvatarData } from "components/AvatarData/AvatarData"
import { WorkspaceStatusBadge } from "components/WorkspaceStatusBadge/WorkspaceStatusBadge"
import { useClickable } from "hooks/useClickable"
import { FC } from "react"
import { useNavigate, Link as RouterLink } from "react-router-dom"
import { getDisplayWorkspaceTemplateName } from "util/workspace"
@ -15,6 +14,7 @@ import { Avatar } from "components/Avatar/Avatar"
import { Stack } from "components/Stack/Stack"
import TemplateLinkIcon from "@material-ui/icons/OpenInNewOutlined"
import Link from "@material-ui/core/Link"
import { useClickableTableRow } from "hooks/useClickableTableRow"
export const WorkspacesRow: FC<{
workspace: Workspace
@ -23,20 +23,13 @@ export const WorkspacesRow: FC<{
const styles = useStyles()
const navigate = useNavigate()
const workspacePageLink = `/@${workspace.owner_name}/${workspace.name}`
const hasTemplateIcon =
workspace.template_icon && workspace.template_icon !== ""
const displayTemplateName = getDisplayWorkspaceTemplateName(workspace)
const clickable = useClickable(() => {
const clickable = useClickableTableRow(() => {
navigate(workspacePageLink)
})
return (
<TableRow
className={styles.row}
hover
data-testid={`workspace-${workspace.id}`}
{...clickable}
>
<TableRow data-testid={`workspace-${workspace.id}`} {...clickable}>
<TableCell>
<AvatarData
title={
@ -53,9 +46,13 @@ export const WorkspacesRow: FC<{
}
subtitle={workspace.owner_name}
avatar={
hasTemplateIcon && (
<Avatar src={workspace.template_icon} variant="square" fitImage />
)
<Avatar
src={workspace.template_icon}
variant={workspace.template_icon ? "square" : undefined}
fitImage={Boolean(workspace.template_icon)}
>
{workspace.name}
</Avatar>
}
/>
</TableCell>
@ -95,15 +92,6 @@ export const WorkspacesRow: FC<{
}
const useStyles = makeStyles((theme) => ({
row: {
cursor: "pointer",
"&:focus": {
outline: `1px solid ${theme.palette.secondary.dark}`,
outlineOffset: -1,
},
},
arrowRight: {
color: theme.palette.text.secondary,
width: 20,

View File

@ -27,5 +27,10 @@ const useStyles = makeStyles((theme) => ({
outline: `1px solid ${theme.palette.secondary.dark}`,
outlineOffset: -1,
},
"&:last-of-type": {
borderBottomLeftRadius: theme.shape.borderRadius,
borderBottomRightRadius: theme.shape.borderRadius,
},
},
}))

View File

@ -215,8 +215,6 @@ export const CreateWorkspacePageView: FC<
value={props.owner}
onChange={props.setOwner}
label={t("ownerLabel")}
inputMargin="dense"
showAvatar
/>
</Stack>
</div>

View File

@ -13,7 +13,10 @@ const Template: Story<SelectedTemplateProps> = (args) => (
export const WithIcon = Template.bind({})
WithIcon.args = {
template: MockTemplate,
template: {
...MockTemplate,
icon: "/icon/docker.png",
},
}
export const WithoutIcon = Template.bind({})

View File

@ -18,7 +18,13 @@ export const SelectedTemplate: FC<SelectedTemplateProps> = ({ template }) => {
className={styles.template}
alignItems="center"
>
<Avatar src={template.icon}>{template.name}</Avatar>
<Avatar
variant={template.icon ? "square" : undefined}
fitImage={Boolean(template.icon)}
src={template.icon}
>
{template.name}
</Avatar>
<Stack direction="column" spacing={0.5}>
<span className={styles.templateName}>

View File

@ -25,19 +25,21 @@ import {
} from "components/PageHeader/PageHeader"
import { Stack } from "components/Stack/Stack"
import { TableRowMenu } from "components/TableRowMenu/TableRowMenu"
import { UserAutocompleteInline } from "components/UserAutocomplete/UserAutocomplete"
import { UserAutocomplete } from "components/UserAutocomplete/UserAutocomplete"
import { useState } from "react"
import { Helmet } from "react-helmet-async"
import { Link as RouterLink, useNavigate, useParams } from "react-router-dom"
import { pageTitle } from "util/page"
import { groupMachine } from "xServices/groups/groupXService"
import { Maybe } from "components/Conditionals/Maybe"
import { makeStyles } from "@material-ui/core/styles"
const AddGroupMember: React.FC<{
isLoading: boolean
onSubmit: (user: User, reset: () => void) => void
}> = ({ isLoading, onSubmit }) => {
const [selectedUser, setSelectedUser] = useState<User | null>(null)
const styles = useStyles()
const resetValues = () => {
setSelectedUser(null)
@ -54,7 +56,8 @@ const AddGroupMember: React.FC<{
}}
>
<Stack direction="row" alignItems="center" spacing={1}>
<UserAutocompleteInline
<UserAutocomplete
className={styles.autoComplete}
value={selectedUser}
onChange={(newValue) => {
setSelectedUser(newValue)
@ -64,7 +67,6 @@ const AddGroupMember: React.FC<{
<LoadingButton
disabled={!selectedUser}
type="submit"
size="small"
startIcon={<PersonAdd />}
loading={isLoading}
>
@ -223,4 +225,10 @@ export const GroupPage: React.FC = () => {
)
}
const useStyles = makeStyles(() => ({
autoComplete: {
width: 300,
},
}))
export default GroupPage

View File

@ -42,7 +42,8 @@ export const getOverrides = ({
MuiButton: {
root: {
// Prevents a loading button from collapsing!
minHeight: 42,
minHeight: 40,
height: 40, // Same size of input height
fontWeight: "normal",
fontSize: 16,
textTransform: "none",
@ -73,6 +74,7 @@ export const getOverrides = ({
padding: `0 16px`,
fontSize: 14,
minHeight: 36,
height: 36,
borderRadius: borderRadiusSm,
},
iconSizeSmall: {