Skip to content

Commit e2f7453

Browse files
feat(fuselage): Contextualbar component (#749)
Co-authored-by: dougfabris <devfabris@gmail.com>
1 parent f7c5854 commit e2f7453

15 files changed

+349
-2
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import type { ComponentStory, ComponentMeta } from '@storybook/react';
2+
import React from 'react';
3+
4+
import {
5+
Contextualbar,
6+
ContextualbarAction,
7+
ContextualbarActions,
8+
ContextualbarButton,
9+
ContextualbarContent,
10+
ContextualbarEmptyContent,
11+
ContextualbarFooter,
12+
ContextualbarHeader,
13+
ContextualbarSkeleton,
14+
ContextualbarTitle,
15+
} from '.';
16+
import { Button, ButtonGroup, IconButton, Box } from '..';
17+
18+
export default {
19+
title: 'Containers/Contextualbar',
20+
component: Contextualbar,
21+
parameters: {
22+
docs: {
23+
description: {
24+
component: `The \`Contextualbar\` has the purpose to persist and input information about the scope of the related page.
25+
`,
26+
},
27+
},
28+
},
29+
decorators: [
30+
(storyFn) => (
31+
<Box width='x400' elevation='2'>
32+
{storyFn()}
33+
</Box>
34+
),
35+
],
36+
} as ComponentMeta<typeof Contextualbar>;
37+
38+
export const Default: ComponentStory<typeof Contextualbar> = () => (
39+
<Contextualbar position='static' height='x540'>
40+
<ContextualbarHeader>
41+
<ContextualbarAction name='chevron-right' />
42+
<ContextualbarAction title='Back' name='arrow-back' />
43+
<ContextualbarTitle>Contextualbar Title</ContextualbarTitle>
44+
<ContextualbarActions>
45+
<ContextualbarAction
46+
title='Title'
47+
name='new-window'
48+
onClick={() => {}}
49+
/>
50+
<ContextualbarAction
51+
name='add-user'
52+
onClick={() => console.log('close')}
53+
/>
54+
</ContextualbarActions>
55+
</ContextualbarHeader>
56+
<ContextualbarContent>Contextualbar Content</ContextualbarContent>
57+
<ContextualbarFooter>
58+
<ButtonGroup>
59+
<ContextualbarButton width='full' secondary>
60+
Cancel
61+
</ContextualbarButton>
62+
<Button width='full' primary>
63+
Save
64+
</Button>
65+
<IconButton icon='menu' />
66+
</ButtonGroup>
67+
</ContextualbarFooter>
68+
</Contextualbar>
69+
);
70+
71+
export const Skeleton: ComponentStory<typeof Contextualbar> = () => (
72+
<ContextualbarSkeleton position='static' height='x540' />
73+
);
74+
75+
export const Empty: ComponentStory<typeof Contextualbar> = () => (
76+
<Contextualbar position='static' height='x540'>
77+
<ContextualbarHeader>
78+
<ContextualbarAction name='chevron-right' />
79+
<ContextualbarTitle>Contextualbar Empty</ContextualbarTitle>
80+
<ContextualbarActions>
81+
<ContextualbarAction
82+
title='Title'
83+
name='new-window'
84+
onClick={() => {}}
85+
/>
86+
</ContextualbarActions>
87+
</ContextualbarHeader>
88+
<ContextualbarEmptyContent />
89+
<ContextualbarFooter>Footer</ContextualbarFooter>
90+
</Contextualbar>
91+
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import type { ComponentProps } from 'react';
2+
import React, { memo } from 'react';
3+
4+
import { Box } from '..';
5+
6+
type ContextualbarProps = ComponentProps<typeof Box>;
7+
8+
const Contextualbar = ({
9+
children,
10+
width,
11+
position,
12+
bg = 'room',
13+
...props
14+
}: ContextualbarProps) => (
15+
<Box
16+
rcx-vertical-bar
17+
bg={bg}
18+
display='flex'
19+
flexDirection='column'
20+
flexShrink={0}
21+
width={width}
22+
borderInlineStartWidth='default'
23+
borderInlineStartColor='extra-light'
24+
borderInlineStartStyle='solid'
25+
height='full'
26+
position={position}
27+
zIndex={5}
28+
{...props}
29+
>
30+
{children}
31+
</Box>
32+
);
33+
34+
export default memo(Contextualbar);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import type { ReactElement, ComponentProps } from 'react';
2+
import React, { memo } from 'react';
3+
4+
import type { Icon } from '..';
5+
import { IconButton } from '..';
6+
7+
type ContextualbarActionProps = {
8+
name: ComponentProps<typeof Icon>['name'];
9+
} & Omit<ComponentProps<typeof IconButton>, 'icon'>;
10+
11+
const ContextualbarAction = ({
12+
name,
13+
...props
14+
}: ContextualbarActionProps): ReactElement => (
15+
<IconButton flexShrink={0} icon={name} {...props} tiny />
16+
);
17+
18+
export default memo(ContextualbarAction);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import type { ReactElement, ComponentProps } from 'react';
2+
import React, { memo } from 'react';
3+
4+
import { ButtonGroup } from '..';
5+
6+
const ContextualbarActions = (
7+
props: ComponentProps<typeof ButtonGroup>
8+
): ReactElement => <ButtonGroup {...props} />;
9+
10+
export default memo(ContextualbarActions);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import type { ComponentProps, ReactElement } from 'react';
2+
import React, { memo } from 'react';
3+
4+
import { Button } from '..';
5+
6+
const ContextualbarButton = (
7+
props: ComponentProps<typeof Button>
8+
): ReactElement => <Button {...props} />;
9+
10+
export default memo(ContextualbarButton);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import type { ComponentProps } from 'react';
2+
import React, { forwardRef, memo } from 'react';
3+
4+
import { Box } from '..';
5+
6+
const ContextualbarContent = forwardRef<
7+
HTMLElement,
8+
ComponentProps<typeof Box>
9+
>(function ContextualbarContent(props, ref) {
10+
return (
11+
<Box
12+
ref={ref}
13+
rcx-vertical-bar__content
14+
paddingInline='x24'
15+
display='flex'
16+
flexDirection='column'
17+
overflowY='hidden'
18+
height='full'
19+
{...props}
20+
/>
21+
);
22+
});
23+
24+
export default memo(ContextualbarContent);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import type { ComponentProps } from 'react';
2+
import React, { forwardRef, memo } from 'react';
3+
4+
import type { Box } from '..';
5+
import { StatesIcon, States, StatesTitle, StatesSubtitle } from '..';
6+
import ContextualbarContent from './ContextualbarContent';
7+
8+
type ContextualbarEmptyContentProps = ComponentProps<typeof Box> & {
9+
icon?: ComponentProps<typeof StatesIcon>['name'];
10+
title?: string;
11+
subtitle?: string;
12+
};
13+
14+
const ContextualbarEmptyContent = forwardRef<
15+
HTMLElement,
16+
ContextualbarEmptyContentProps
17+
>(function ContextualbarEmptyContent(
18+
{ icon = 'magnifier', title = 'Nothing Found', subtitle, ...props },
19+
ref
20+
) {
21+
return (
22+
<ContextualbarContent justifyContent='center' {...props} ref={ref}>
23+
<States>
24+
<StatesIcon name={icon} />
25+
<StatesTitle>{title}</StatesTitle>
26+
{subtitle && <StatesSubtitle>{subtitle}</StatesSubtitle>}
27+
</States>
28+
</ContextualbarContent>
29+
);
30+
});
31+
32+
export default memo(ContextualbarEmptyContent);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import type { ComponentProps } from 'react';
2+
import React, { forwardRef, memo } from 'react';
3+
4+
import { Box } from '..';
5+
6+
const ContextualbarFooter = forwardRef<HTMLElement, ComponentProps<typeof Box>>(
7+
function ContextualbarFooter({ children, ...props }, ref) {
8+
return (
9+
<Box is='footer' p='x24' {...props} ref={ref}>
10+
{children}
11+
</Box>
12+
);
13+
}
14+
);
15+
16+
export default memo(ContextualbarFooter);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import type { ComponentProps } from 'react';
2+
import React, { memo } from 'react';
3+
4+
import Box from '../Box';
5+
import Margins from '../Margins';
6+
7+
type ContextualbarHeaderProps = ComponentProps<typeof Box>;
8+
9+
const ContextualbarHeader = ({
10+
children,
11+
...props
12+
}: ContextualbarHeaderProps) => (
13+
<Box
14+
display='flex'
15+
alignItems='center'
16+
height='56px'
17+
is='h3'
18+
pi='x24'
19+
borderBlockEndWidth='default'
20+
borderBlockColor='extra-light'
21+
flexShrink={0}
22+
{...props}
23+
>
24+
<Box
25+
marginInline='neg-x4'
26+
display='flex'
27+
alignItems='center'
28+
justifyContent='space-between'
29+
fontScale='h4'
30+
flexGrow={1}
31+
overflow='hidden'
32+
color='default'
33+
>
34+
<Margins inline='x4'>{children}</Margins>
35+
</Box>
36+
</Box>
37+
);
38+
39+
export default memo(ContextualbarHeader);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import type { ReactElement, ComponentProps } from 'react';
2+
import React, { memo } from 'react';
3+
4+
import { Icon } from '..';
5+
6+
const ContextualbarIcon = (
7+
props: ComponentProps<typeof Icon>
8+
): ReactElement => <Icon {...props} pi='x2' size='x24' />;
9+
10+
export default memo(ContextualbarIcon);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import type { ReactElement, ComponentProps } from 'react';
2+
import React, { memo } from 'react';
3+
4+
import { Contextualbar, ContextualbarHeader } from '.';
5+
import { Box, Skeleton } from '..';
6+
7+
const ContextualbarSkeleton = (
8+
props: ComponentProps<typeof Box>
9+
): ReactElement => (
10+
<Contextualbar {...props} width='100%'>
11+
<ContextualbarHeader>
12+
<Skeleton width='100%' />
13+
</ContextualbarHeader>
14+
<Box p='x24'>
15+
<Skeleton mbe='x4' width='32px' height='32px' variant='rect' />
16+
{Array(5)
17+
.fill(5)
18+
.map((_, index) => (
19+
<Skeleton key={index} />
20+
))}
21+
</Box>
22+
</Contextualbar>
23+
);
24+
25+
export default memo(ContextualbarSkeleton);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import type { ReactElement, ComponentProps } from 'react';
2+
import React, { memo } from 'react';
3+
4+
import { Box } from '..';
5+
6+
const ContextualbarTitle = (
7+
props: ComponentProps<typeof Box>
8+
): ReactElement => (
9+
<Box flexShrink={1} flexGrow={1} withTruncatedText {...props} />
10+
);
11+
12+
export default memo(ContextualbarTitle);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import Contextualbar from './Contextualbar';
2+
import ContextualbarAction from './ContextualbarAction';
3+
import ContextualbarActions from './ContextualbarActions';
4+
import ContextualbarButton from './ContextualbarButton';
5+
import ContextualbarContent from './ContextualbarContent';
6+
import ContextualbarEmptyContent from './ContextualbarEmptyContent';
7+
import ContextualbarFooter from './ContextualbarFooter';
8+
import ContextualbarHeader from './ContextualbarHeader';
9+
import ContextualbarIcon from './ContextualbarIcon';
10+
import ContextualbarSkeleton from './ContextualbarSkeleton';
11+
import ContextualbarTitle from './ContextualbarTitle';
12+
13+
export {
14+
Contextualbar,
15+
ContextualbarAction,
16+
ContextualbarActions,
17+
ContextualbarButton,
18+
ContextualbarContent,
19+
ContextualbarEmptyContent,
20+
ContextualbarFooter,
21+
ContextualbarHeader,
22+
ContextualbarIcon,
23+
ContextualbarSkeleton,
24+
ContextualbarTitle,
25+
};

packages/fuselage/src/components/Skeleton/Skeleton.styles.scss

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
animation: rcx-skeleton__animation 1s linear 0s infinite running;
1010

11+
border-radius: lengths.border-radius(medium);
12+
1113
background-color: colors.stroke(extra-dark);
1214

1315
&--text {
@@ -17,8 +19,6 @@
1719
transform: scale(1, 0.6);
1820
transform-origin: 0 60%;
1921

20-
border-radius: lengths.border-radius(medium);
21-
2222
&:empty::before {
2323
content: '\00a0';
2424
}

packages/fuselage/src/components/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export * from './Callout';
1313
export * from './CheckBox';
1414
export * from './Chevron';
1515
export { default as CodeSnippet } from './CodeSnippet';
16+
export * from './Contextualbar';
1617
export { default as Chip } from './Chip';
1718
export * from './Divider';
1819
export * from './Dropdown';

0 commit comments

Comments
 (0)