mirror of
https://github.com/Lifeforge-app/lifeforge.git
synced 2026-06-28 14:55:45 +00:00
feat(ui): enhance ModuleWrapper layout and add stories for testing
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react-vite'
|
||||
|
||||
import { ModuleHeader } from '@components/layout'
|
||||
import { Box, Flex, Grid, Text } from '@components/primitives'
|
||||
|
||||
import ModuleWrapper from './index'
|
||||
|
||||
const meta = {
|
||||
component: ModuleWrapper,
|
||||
argTypes: {
|
||||
children: { control: false }
|
||||
}
|
||||
} satisfies Meta<typeof ModuleWrapper>
|
||||
|
||||
export default meta
|
||||
|
||||
type Story = StoryObj<typeof meta>
|
||||
|
||||
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
||||
|
||||
function StatCard({ label, value }: { label: string; value: string }) {
|
||||
return (
|
||||
<Box shadow bg={{ base: 'bg-50', dark: 'bg-900' }} p="lg" rounded="lg">
|
||||
<Text color={{ base: 'bg-500', dark: 'bg-400' }} size="sm">
|
||||
{label}
|
||||
</Text>
|
||||
<Text as="p" mt="xs" size="2xl" weight="bold">
|
||||
{value}
|
||||
</Text>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
function ContentCard({ title, body }: { title: string; body: string }) {
|
||||
return (
|
||||
<Box shadow bg={{ base: 'bg-50', dark: 'bg-900' }} p="lg" rounded="lg">
|
||||
<Text as="h3" mb="xs" size="lg" weight="semibold">
|
||||
{title}
|
||||
</Text>
|
||||
<Text color={{ base: 'bg-500', dark: 'bg-400' }} size="sm">
|
||||
{body}
|
||||
</Text>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
// ─── Stories ──────────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* The default `ModuleWrapper` sets up the module header context, a scrollable
|
||||
* content area, and proper padding. Pair it with `ModuleHeader` as the first
|
||||
* child to render the icon, title, and description bar.
|
||||
*/
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
children: <></>,
|
||||
config: {
|
||||
title: 'Demo Module',
|
||||
icon: 'tabler:cube',
|
||||
clearQueryOnUnmount: false
|
||||
}
|
||||
},
|
||||
render: args => (
|
||||
<Box height="100vh" minHeight="100vh" width="100%">
|
||||
<ModuleWrapper {...args}>
|
||||
<ModuleHeader />
|
||||
|
||||
<Grid columns="repeat(3, minmax(0, 1fr))" gap="md" mb="xl" width="100%">
|
||||
<StatCard label="Total Items" value="1,284" />
|
||||
<StatCard label="Active" value="847" />
|
||||
<StatCard label="Completed" value="437" />
|
||||
</Grid>
|
||||
|
||||
<Flex direction="column" gap="md" width="100%">
|
||||
<ContentCard
|
||||
body="Here is a brief summary of the first item in your module. It contains useful information."
|
||||
title="First Entry"
|
||||
/>
|
||||
<ContentCard
|
||||
body="This is the second entry in the list. Use this area to display relevant module data."
|
||||
title="Second Entry"
|
||||
/>
|
||||
<ContentCard
|
||||
body="A third entry demonstrating how the module content area scrolls naturally."
|
||||
title="Third Entry"
|
||||
/>
|
||||
<ContentCard
|
||||
body="Another entry to show padding and spacing consistency across the module body."
|
||||
title="Fourth Entry"
|
||||
/>
|
||||
</Flex>
|
||||
</ModuleWrapper>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
@@ -7,8 +7,12 @@ import {
|
||||
normalizeSubnamespace
|
||||
} from 'shared'
|
||||
|
||||
import { Flex } from '@components/primitives'
|
||||
import { Scrollbar } from '@components/utilities'
|
||||
|
||||
/**
|
||||
* The wrapper component for all modules in the app. It provides the layout and context for the module header and sidebar, as well as handling query cleanup on unmount if specified. If being used within LifeForge instance, it will be automatically wrapped around the module content. Therefore, no explicit usage is needed in most cases.
|
||||
*/
|
||||
function ModuleWrapper({
|
||||
children,
|
||||
config: { title, icon, clearQueryOnUnmount = true }
|
||||
@@ -38,14 +42,20 @@ function ModuleWrapper({
|
||||
value={{ title: normalizeSubnamespace(title).replace('__', '$'), icon }}
|
||||
>
|
||||
<ModuleSidebarStateProvider>
|
||||
<Scrollbar
|
||||
className="no-overflow-x flex min-h-0 flex-col transition-all"
|
||||
usePaddingRight={false}
|
||||
>
|
||||
<div className="flex w-full flex-1 flex-col px-4 pt-8 sm:px-12">
|
||||
{children}
|
||||
</div>
|
||||
</Scrollbar>
|
||||
<Flex asChild direction="column" minHeight="0">
|
||||
<Scrollbar className="no-overflow-x" usePaddingRight={false}>
|
||||
<Flex
|
||||
direction="column"
|
||||
flex="1"
|
||||
overflowX="hidden"
|
||||
pt="xl"
|
||||
px={{ base: 'md', sm: '2xl' }}
|
||||
width="100%"
|
||||
>
|
||||
{children}
|
||||
</Flex>
|
||||
</Scrollbar>
|
||||
</Flex>
|
||||
</ModuleSidebarStateProvider>
|
||||
</ModuleHeaderStateProvider>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user