feat: New Collapse component for Apps logs (#36142)
Co-authored-by: Tasso Evangelista <2263066+tassoevan@users.noreply.github.com>pull/36256/head^2
parent
93acfbe014
commit
0ba4d8bc18
@ -0,0 +1,6 @@ |
||||
--- |
||||
"@rocket.chat/meteor": minor |
||||
"@rocket.chat/i18n": minor |
||||
--- |
||||
|
||||
Implements new component for Apps Logs View |
||||
@ -0,0 +1,21 @@ |
||||
/* eslint-disable @typescript-eslint/naming-convention */ |
||||
import { mockAppRoot } from '@rocket.chat/mock-providers'; |
||||
import { composeStories } from '@storybook/react'; |
||||
import { render } from '@testing-library/react'; |
||||
import { axe } from 'jest-axe'; |
||||
|
||||
import * as stories from './AppLogsItem.stories'; |
||||
|
||||
const testCases = Object.values(composeStories(stories)).map((Story) => [Story.storyName || 'Story', Story]); |
||||
|
||||
test.each(testCases)(`renders AppLogsItem without crashing`, async (_storyname, Story) => { |
||||
const view = render(<Story />, { wrapper: mockAppRoot().build() }); |
||||
expect(view.baseElement).toMatchSnapshot(); |
||||
}); |
||||
|
||||
test.each(testCases)('AppLogsItem should have no a11y violations', async (_storyname, Story) => { |
||||
const { container } = render(<Story />, { wrapper: mockAppRoot().build() }); |
||||
|
||||
const results = await axe(container); |
||||
expect(results).toHaveNoViolations(); |
||||
}); |
||||
@ -0,0 +1,43 @@ |
||||
import type { Meta, StoryFn } from '@storybook/react'; |
||||
import type { ComponentProps } from 'react'; |
||||
|
||||
import AppLogsItem from './AppLogsItem'; |
||||
import { CollapsiblePanel } from './Components/CollapsiblePanel'; |
||||
|
||||
export default { |
||||
title: 'Components/AppLogsItem', |
||||
component: AppLogsItem, |
||||
decorators: [(fn) => <CollapsiblePanel style={{ padding: 24 }}>{fn()}</CollapsiblePanel>], |
||||
args: { |
||||
_id: '683da1e32025cfca7b3d8238', |
||||
appId: 'ce0e318b-ffc0-4ce4-832b-f1b464beb22a', |
||||
method: 'app:checkPostMessageSent', |
||||
entries: [ |
||||
{ |
||||
caller: 'anonymous OR constructor -> handleApp', |
||||
severity: 'debug', |
||||
method: 'app:checkPostMessageSent', |
||||
timestamp: '2025-06-02T13:06:43.772Z', |
||||
args: ["'checkPostMessageSent' is being called..."], |
||||
}, |
||||
{ |
||||
caller: 'anonymous OR constructor', |
||||
severity: 'debug', |
||||
method: 'app:checkPostMessageSent', |
||||
timestamp: '2025-06-02T13:06:43.777Z', |
||||
args: ["'checkPostMessageSent' was successfully called! The result is:", 'false'], |
||||
}, |
||||
], |
||||
startTime: '2025-06-02T13:06:43.771Z', |
||||
endTime: '2025-06-02T13:06:43.777Z', |
||||
totalTime: 6, |
||||
_createdAt: '2025-06-02T13:06:43.777Z', |
||||
instanceId: 'b97ce445-b9ff-4513-8206-966afd799cd6', |
||||
_updatedAt: '2025-06-02T13:06:43.778Z', |
||||
}, |
||||
parameters: { |
||||
layout: 'fullscreen', |
||||
}, |
||||
} satisfies Meta<ComponentProps<typeof AppLogsItem>>; |
||||
|
||||
export const Simple: StoryFn<ComponentProps<typeof AppLogsItem>> = (args) => <AppLogsItem {...args} />; |
||||
@ -0,0 +1,16 @@ |
||||
import { Box } from '@rocket.chat/fuselage'; |
||||
import type { ComponentProps, ReactNode } from 'react'; |
||||
|
||||
type AppsLogItemFieldProps = { |
||||
field: ReactNode | string; |
||||
label: string; |
||||
} & ComponentProps<typeof Box>; |
||||
|
||||
export const AppsLogItemField = ({ field, label, ...props }: AppsLogItemFieldProps) => { |
||||
return ( |
||||
<Box mb={16} display='flex' color='default' flexDirection='column' {...props}> |
||||
<Box fontWeight={700}>{label}</Box> |
||||
{field} |
||||
</Box> |
||||
); |
||||
}; |
||||
@ -0,0 +1,43 @@ |
||||
import { css } from '@rocket.chat/css-in-js'; |
||||
import { Box, Chevron, Palette } from '@rocket.chat/fuselage'; |
||||
import type { CSSProperties, ReactNode } from 'react'; |
||||
|
||||
type CollapseButtonProps = { |
||||
children: ReactNode; |
||||
regionId: string; |
||||
expanded?: boolean; |
||||
onClick: () => void; |
||||
}; |
||||
|
||||
export const CollapseButton = ({ regionId, children, expanded, onClick }: CollapseButtonProps) => { |
||||
const clickable = css` |
||||
background: ${Palette.surface['surface-light']}; |
||||
|
||||
&:hover { |
||||
background: ${Palette.surface['surface-tint']}; |
||||
} |
||||
`;
|
||||
const style: CSSProperties = { whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }; |
||||
return ( |
||||
<Box is='dt' style={style}> |
||||
<Box |
||||
is='button' |
||||
role='button' |
||||
onClick={onClick} |
||||
className={clickable} |
||||
aria-expanded={expanded} |
||||
aria-controls={regionId} |
||||
display='flex' |
||||
flexDirection='row' |
||||
width='full' |
||||
focusable |
||||
color={Palette.text['font-default']} |
||||
> |
||||
<Chevron size={32} down={!expanded} up={expanded} style={{ alignSelf: 'flex-start' }} /> |
||||
<Box pb='x4' pi='x4' fontWeight='700'> |
||||
{children} |
||||
</Box> |
||||
</Box> |
||||
</Box> |
||||
); |
||||
}; |
||||
@ -0,0 +1,41 @@ |
||||
import type { StoryFn } from '@storybook/react'; |
||||
|
||||
import { CollapseButton } from './CollapseButton'; |
||||
import { CollapsiblePanel } from './CollapsiblePanel'; |
||||
import { CollapsibleRegion } from './CollapsibleRegion'; |
||||
|
||||
export default { |
||||
title: 'Components/CollapsiblePanel', |
||||
component: CollapsiblePanel, |
||||
|
||||
args: { |
||||
expanded: true, |
||||
}, |
||||
|
||||
parameters: { |
||||
layout: 'centered', |
||||
}, |
||||
}; |
||||
|
||||
const Template: StoryFn = (args) => { |
||||
return ( |
||||
<CollapsiblePanel> |
||||
<CollapseButton |
||||
onClick={() => { |
||||
args.expanded = !args.expanded; |
||||
}} |
||||
expanded={args.expanded} |
||||
regionId='collapse-item' |
||||
> |
||||
Click Me |
||||
</CollapseButton> |
||||
<CollapsibleRegion expanded={args.expanded} id='collapse-item'> |
||||
<p>This is the content of the panel that can be activated.</p> |
||||
<button>Click Me</button> |
||||
<p>More content can go here.</p> |
||||
</CollapsibleRegion> |
||||
</CollapsiblePanel> |
||||
); |
||||
}; |
||||
|
||||
export const Default = Template.bind({}); |
||||
@ -0,0 +1,12 @@ |
||||
import { Box } from '@rocket.chat/fuselage'; |
||||
import type { ComponentProps } from 'react'; |
||||
|
||||
type CollapsiblePanelProps = ComponentProps<typeof Box>; |
||||
|
||||
export const CollapsiblePanel = (props: CollapsiblePanelProps) => { |
||||
return ( |
||||
<Box {...props} is='dl'> |
||||
{props.children} |
||||
</Box> |
||||
); |
||||
}; |
||||
@ -0,0 +1,26 @@ |
||||
import { css } from '@rocket.chat/css-in-js'; |
||||
import { Box } from '@rocket.chat/fuselage'; |
||||
import type { ComponentProps, ReactNode } from 'react'; |
||||
|
||||
type CollapsibleRegionProps = { |
||||
children: ReactNode; |
||||
expanded?: boolean; |
||||
} & ComponentProps<typeof Box>; |
||||
|
||||
export const CollapsibleRegion = ({ children, expanded, ...props }: CollapsibleRegionProps) => { |
||||
return ( |
||||
<Box |
||||
{...props} |
||||
maxHeight={expanded ? 'fit-content' : 0} |
||||
className={[ |
||||
css` |
||||
transition: all 0.18s ease; |
||||
`,
|
||||
]} |
||||
overflowY='hidden' |
||||
is='dd' |
||||
> |
||||
<Box role='region'>{children}</Box> |
||||
</Box> |
||||
); |
||||
}; |
||||
Loading…
Reference in new issue