-
Notifications
You must be signed in to change notification settings - Fork 24
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
Demo app - Record detail layout #4745
base: master
Are you sure you want to change the base?
Changes from 9 commits
1b2e699
c3513ad
af5f7d1
b31e2fe
6c6d4dc
89e528f
e9eb5a5
ca9890a
62b5eaa
6cb8486
adf0b47
1c43b62
df00866
07b417a
afdf44f
56bd791
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import React from 'react'; | ||
import { Box, NameValueList, NameValuePair, Text } from 'grommet'; | ||
import { StatusGoodSmall } from 'grommet-icons'; | ||
import { sentenceCase } from '../../../../utils/format'; | ||
|
||
const groupDetails = { | ||
status: { | ||
value: 'Okay', | ||
render: () => <Box direction="row" gap="xsmall" align="center"> | ||
<StatusGoodSmall color='status-ok' /> | ||
<Text>Okay</Text> | ||
</Box> | ||
, | ||
}, | ||
state: 'Job in progress', | ||
group: 'Server group name', | ||
servers: 2, | ||
baseline: 'SPP 2022.09.04(04 Sep 2022)', | ||
power: 'On', | ||
}; | ||
|
||
const nameProps = { | ||
width: ['xsmall', 'max-content'] | ||
}; | ||
|
||
export const DetailSummary: React.FC = () => { | ||
return ( | ||
<NameValueList nameProps={nameProps}> | ||
{Object.entries(groupDetails).map(([name, value]) => ( | ||
<NameValuePair | ||
key={name} | ||
name={sentenceCase(name)} | ||
> | ||
{typeof value === 'object' && 'render' in value ? | ||
value.render() : | ||
sentenceCase(value.toString())} | ||
</NameValuePair> | ||
))} | ||
</NameValueList> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import React from "react"; | ||
import { Box, NameValueList, NameValuePair } from "grommet"; | ||
import { sentenceCase } from "../../../../utils/format"; | ||
import { statusMap } from "../../../../utils/status"; | ||
|
||
const iLOSecurity = { | ||
'critical': { | ||
servers: [], | ||
label: 'At risk', | ||
}, | ||
'warning': { | ||
servers: [{ id: 'server1', name: 'Server 1', url: '#' }], | ||
label: 'Needs attention', | ||
}, | ||
'ok': { | ||
servers: [{ id: 'server2', name: 'Server 2', url: '#' }], | ||
label: 'Okay', | ||
}, | ||
'unknown': { | ||
servers: [], | ||
label: 'Unknown', | ||
}, | ||
'unsupported': { | ||
servers: [], | ||
label: 'Unsupported', | ||
}, | ||
} | ||
|
||
const nameProps = { | ||
width: ['xsmall', 'max-content'] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we include this in grommet-theme-hpe v6.0.0 as the default? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oooh I like your thinking. Perhaps we could do something similar for bottom pad on PageHeader. Maybe audit some other "we always seem to apply these props". |
||
}; | ||
|
||
export const ILOSecurity: React.FC = () => { | ||
return ( | ||
<NameValueList nameProps={nameProps}> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same TS error here There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
{Object.entries(iLOSecurity).map(([key, detail]) => ( | ||
<NameValuePair | ||
key={key} | ||
name={sentenceCase(detail.label)} | ||
> | ||
<Box direction="row" align="center" gap="small"> | ||
{React.cloneElement(statusMap.get(key)!.icon, { color: statusMap.get(key)!.color })} | ||
{detail.servers.length} servers | ||
</Box> | ||
</NameValuePair> | ||
))} | ||
</NameValueList> | ||
); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import React from 'react'; | ||
import { | ||
Box, | ||
Button, | ||
List, | ||
NameValueList, | ||
NameValuePair, | ||
Text | ||
} from 'grommet'; | ||
import { Edit, Trash } from 'grommet-icons'; | ||
import { sentenceCase } from "../../../../utils/format"; | ||
|
||
export const JobList = ( | ||
{ jobs }: | ||
{ | ||
jobs: { | ||
id: string, | ||
name: string, | ||
[key: string]: any, | ||
}[] | ||
} | ||
) => { | ||
return ( | ||
<List | ||
data={jobs} | ||
defaultItemProps={{ pad: { horizontal: 'none', vertical: 'xsmall' } }} | ||
> | ||
{(datum) => ( | ||
<Box | ||
key={datum.id} | ||
background="background-contrast" | ||
round="small" | ||
pad={{ left: 'small', right: 'xsmall', vertical: 'small' }} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would "small" all around work? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because we have an icon only button on the right, the padding in the button + pad on the container end up making the left-right whitespace feel unbalanced. Reducing the right pad to xsmall feels more balanced. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree locally I changed it to |
||
> | ||
<Box direction="row" justify="between"> | ||
<Text>{datum.name}</Text> | ||
<Box direction="row"> | ||
<Button icon={<Edit />} size="small" /> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agree. Fixed. |
||
<Button icon={<Trash />} size="small" /> | ||
</Box> | ||
</Box> | ||
<NameValueList | ||
nameProps={{ width: 'max-content' }} | ||
valueProps={{ width: 'medium' }} | ||
> | ||
{Object.entries(datum).map(([key, value]) => ( | ||
<NameValuePair key={key} name={sentenceCase(key)}> | ||
{Array.isArray(value) ? | ||
value.map((item) => (<Text key={item}>{item}</Text>)) : | ||
value} | ||
</NameValuePair> | ||
))} | ||
</NameValueList> | ||
</Box> | ||
) | ||
} | ||
</List > | ||
); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import React from 'react'; | ||
import { Anchor, Box, List, Paragraph, Text } from 'grommet'; | ||
|
||
const recentActivities = [ | ||
{ | ||
title: 'Firmware update', | ||
description: `Firmware update failed. Recommendation: Retry | ||
the firmware update. If the issue persists, open a support case | ||
by clicking on the help icon in the UI and select Compute Ops | ||
Management - Create case (error code: FWE-110).`, | ||
datetime: '2025-01-24T09:58:25', | ||
}, | ||
{ | ||
title: 'Collect hardware inventory', | ||
description: 'Inventory collection successful', | ||
datetime: '2025-01-21T08:37:33', | ||
}, | ||
{ | ||
title: 'Collect hardware inventory', | ||
description: 'Inventory collection successful', | ||
datetime: '2025-01-03T18:12:56', | ||
}, | ||
{ | ||
title: 'Server health', | ||
description: 'Network health changed to OK', | ||
datetime: '2025-01-21T14:20:21', | ||
}, { | ||
title: 'Server health', | ||
description: 'Network health changed to OK', | ||
datetime: '2025-01-21T14:20:21', | ||
} | ||
].sort((a, b) => { | ||
return new Date(b.datetime).getTime() - new Date(a.datetime).getTime(); | ||
}); | ||
|
||
interface ActivityItemProps { | ||
title: string; | ||
description: string; | ||
datetime: string; | ||
} | ||
|
||
const ActivityItem: React.FC<ActivityItemProps> = ({ title, description, datetime }) => { | ||
return ( | ||
<Box gap="xsmall"> | ||
<Text color="text-strong" weight={500}>{title}</Text> | ||
<Paragraph margin="none">{description}</Paragraph> | ||
<Text>{Intl.DateTimeFormat(undefined, { dateStyle: 'medium', timeStyle: 'short' }).format(new Date(datetime))}</Text> | ||
</Box> | ||
); | ||
} | ||
|
||
export const RecentActivity: React.FC = () => { | ||
return ( | ||
<Box gap="medium"> | ||
<List | ||
data={recentActivities} | ||
defaultItemProps={{ pad: { 'vertical': 'small' } }} | ||
> | ||
{datum => ( | ||
<ActivityItem | ||
title={datum.title} | ||
description={datum.description} | ||
datetime={datum.datetime} | ||
/> | ||
)} | ||
</List> | ||
<Anchor label="View all activity" href='#' /> | ||
</Box> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
import React from 'react'; | ||
import { Link } from 'react-router-dom'; | ||
import { | ||
Box, | ||
Button, | ||
Grid, | ||
Notification, | ||
Page, | ||
PageContent, | ||
PageHeader, | ||
} from 'grommet'; | ||
import { RoutedAnchor } from '../../../../components'; | ||
import { Close, Previous } from 'grommet-icons'; | ||
import ContentPane from '../../../../components/ContentPane'; | ||
import { DetailSummary } from './DetailSummary'; | ||
import { ScheduledActions } from './ScheduledActions'; | ||
import { ILOSecurity } from './ILOSecurity'; | ||
import { RecentActivity } from './RecentActivity'; | ||
import { ScheduledJobs } from './ScheduledJobs'; | ||
|
||
export const RecordDetail: React.FC = () => { | ||
const [scheduledJobs, setScheduledJobs] = React.useState<boolean>(false); | ||
const columns = scheduledJobs ? ['flex', 'auto'] : ['flex', 'medium']; | ||
const rows = ['auto']; | ||
|
||
const areas = [ | ||
['notification', 'context-pane'], | ||
['details', 'context-pane'], | ||
['scheduled-actions', 'context-pane'], | ||
['iLO-security', 'context-pane'], | ||
['unassigned', 'context-pane'], | ||
]; | ||
|
||
return ( | ||
<Page pad={{ bottom: 'xlarge' }} flex="grow"> | ||
<PageContent> | ||
<PageHeader | ||
title="Server group name" | ||
subtitle="2 servers" | ||
parent={<RoutedAnchor as={Link} to="/layouts" label="Layouts" icon={<Previous />} />} | ||
/> | ||
<Grid areas={areas} columns={columns} rows={rows} gap="large"> | ||
<Box gridArea="notification"> | ||
<Notification | ||
status="info" | ||
message="1 job is scheduled." | ||
actions={[{ | ||
label: 'View', | ||
href: '#', | ||
onClick: () => { setScheduledJobs(true) }, | ||
}]} | ||
/> | ||
</Box> | ||
<ContentPane | ||
gridArea="details" | ||
heading="Details" | ||
level={2} | ||
actions={undefined} | ||
skeleton={undefined} | ||
> | ||
<DetailSummary /> | ||
</ContentPane> | ||
<ContentPane | ||
gridArea="scheduled-actions" | ||
heading="Scheduled actions" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Made adjustments to improve responsive behavior. |
||
level={2} | ||
contain={true} | ||
actions={undefined} | ||
skeleton={undefined} | ||
> | ||
<ScheduledActions /> | ||
</ContentPane> | ||
<ContentPane | ||
gridArea="iLO-security" | ||
heading="iLO security" | ||
level={2} | ||
actions={undefined} | ||
skeleton={undefined} | ||
> | ||
<ILOSecurity /> | ||
</ContentPane> | ||
<Box gridArea='context-pane' gap="medium"> | ||
{scheduledJobs && | ||
<ContentPane | ||
heading="Scheduled and pending jobs" | ||
level={2} | ||
actions={ | ||
<Button | ||
autoFocus | ||
a11yTitle="You are in a new page region displaying scheduled and pending jobs. Remove this region by pressing Enter." | ||
icon={<Close />} | ||
onClick={() => setScheduledJobs(false)} | ||
/> | ||
} | ||
skeleton={undefined} | ||
animation={["slideLeft", "fadeIn"]} | ||
> | ||
<ScheduledJobs level={2} /> | ||
</ContentPane>} | ||
<ContentPane | ||
heading="Recent activity" | ||
level={2} | ||
actions={undefined} | ||
skeleton={undefined} | ||
> | ||
<RecentActivity /> | ||
</ContentPane> | ||
</Box> | ||
</Grid> | ||
</PageContent> | ||
</Page> | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
locally Im getting a TS error for the
nameProps
I wanted to check on your side if it was fine or getting same error.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I get the error too. I'm leaving it because I think Grommet should update the width type definition for NameValueList as it using Grid underneath and an array with min/max values should be valid. Leaving the error as a reminder that this is something that should be fixed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agree I was thinking grommet should be enhanced!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@halocline Can you file a ticket on this in Grommet?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
grommet/grommet#7509
one step ahead I put up a PR on this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sorry thought I had commented that I will put up a PR