Skip to content
Open
104 changes: 95 additions & 9 deletions src/components/lists/PropertyList/PropertyList.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,106 @@
@layer baklava.components {
.bk-property-list {
@include bk.component-base(bk-property-list);

margin: 0;
display: flex;
flex-flow: row wrap;
flex-wrap: wrap;
align-items: flex-start;
gap: bk.$spacing-4 bk.$spacing-8;

.bk-property-list__property {
&.bk-property-list__property--full-width {
inline-size: -webkit-fill-available;
}

dt {
font-weight: bk.$font-weight-semibold;
display: flex;
flex-direction: column;
gap: bk.$spacing-2;
justify-content: flex-start;
}

// Small
.bk-property-list__property--small {
flex: 0 0 11ch;
max-inline-size: 11ch;
}

// Medium
.bk-property-list__property--medium {
flex: 0 0 22ch;
max-inline-size: 22ch;
}

// Large
.bk-property-list__property--large {
flex: 0 0 44ch;
max-inline-size: 44ch;
}

// Full size
.bk-property-list__property--full-size {
flex: 0 0 100%;
max-inline-size: 100%;
}

dt {
font-weight: bk.$font-weight-regular;
font-size: bk.$font-size-s;
color: bk.$theme-text-small-text-subtle;
}

dd {
display: flex;
flex-direction: column;
font-weight: bk.$font-weight-regular;
font-size: bk.$font-size-m;
color: bk.$theme-text-body-default;
}

.bk-property-list__property__value {
// Preserve multi-line string formatting
white-space: pre-line;
@include bk.text-layout;
}

/**
* This wrapper is the ONLY element that gets clamped.
* React will dynamically set:
* style={{ WebkitLineClamp: clampLines }}
*/
.bk-property-list__property__clamped {
display: -webkit-box;
-webkit-box-orient: vertical;
overflow: hidden;
}

.bk-property-list__property__toggle-expand {
align-self: flex-start;
font-weight: bk.$font-weight-regular;
font-size: bk.$font-size-m;
color: bk.$theme-button-tertiary-text-default;
cursor: pointer;
text-decoration: none; // remove default underline

&:hover {
color: bk.$theme-button-tertiary-text-hover;
}
}
}

// Horizontal layout (default)
.bk-property-list--horizontal {
flex-direction: row;
}

// Horizontal layout
.bk-property-list--horizontal {
flex-direction: row;
}

// Vertical layout
.bk-property-list--vertical {
flex-direction: column;

.bk-property-list__property {
flex: 0 0 100%;
max-inline-size: 100%;
}
}
}
235 changes: 223 additions & 12 deletions src/components/lists/PropertyList/PropertyList.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,21 @@ import type { Meta, StoryObj } from '@storybook/react-vite';

import { PropertyList } from './PropertyList.tsx';

import { loremIpsumParagraph } from '../../../util/storybook/LoremIpsum.tsx';

type PropertyListArgs = React.ComponentProps<typeof PropertyList>;
type Story = StoryObj<typeof PropertyList>;
/**
* Merge PropertyList + Property props
* but omit children from auto-control exposure
*/
type PropertyListArgs =
Omit<React.ComponentProps<typeof PropertyList>, 'children'> &
React.ComponentProps<typeof PropertyList.Property> & {
children?: React.ReactNode;
};

const sizes = ['small', 'medium', 'large', 'full-size'] as const;

type Story = StoryObj<PropertyListArgs>;

export default {
component: PropertyList,
Expand All @@ -19,28 +31,227 @@ export default {
//design: { type: 'figma', url: '' },
},
tags: ['autodocs'],
argTypes: {},
argTypes: {
orientation: {
control: 'select',
options: ['horizontal', 'vertical'],
},
label: { control: 'text' },
value: { control: 'text' },

size: {
control: 'select',
options: sizes,
},

expandable: { control: 'boolean' },

clampLines: {
control: { type: 'number', min: 1, max: 10 },
if: { arg: 'expandable', truthy: true },
},

// Hide children from controls panel
children: {
table: { disable: true },
},

},

args: {
label: 'Editable Property',
value: `${loremIpsumParagraph} ${loremIpsumParagraph}`,
size: 'large',
expandable: true,
clampLines: 3,
},

render: (args) => {
const {
children,
orientation,
label,
value,
size,
expandable,
clampLines,
} = args;

return (
<PropertyList orientation={orientation}>
{children ? (
children
) : (
<>
<PropertyList.Property
label={label}
value={value}
size={size}
expandable={expandable}
clampLines={clampLines}
/>

<PropertyList.Property
label="Large"
value="This is the large value property"
size="large"
/>
<PropertyList.Property
label="Medium"
value="This is the medium value property"
size="medium"
/>
<PropertyList.Property
label="Small"
value="This is the small value property"
size="small"
/>
<PropertyList.Property
label="Full Size"
value="This is the full size value property"
size="full-size"
/>
</>
)}
</PropertyList>
);
},
} satisfies Meta<PropertyListArgs>;

export const Playground: Story = {};

export const HorizontalSizes: Story = {
args: {
children: (
<>
<PropertyList.Property
label="Large"
value="This is the large value property"
size="large"
/>
<PropertyList.Property
label="Medium"
value="This is the medium value property"
size="medium"
/>
<PropertyList.Property
label="Small"
value="This is the small value property"
size="small"
/>
<PropertyList.Property
label="Full Size"
value="This is the full size value property"
size="full-size"
/>
</>
),
},
};

export const VerticalOrientation: Story = {
args: {
orientation: 'vertical',
children: (
<>
<PropertyList.Property label="Key 1" value="Value 1" />
<PropertyList.Property label="Key 2" value="Value 2" />
<PropertyList.Property label="Key 3" value="Value 3" />
<PropertyList.Property label="Key 4" value="Value 4" />
</>
),
},
};

export const MixedLayout: Story = {
args: {
children: (
<>
<PropertyList.Property label="ID (Small)" value="12345" size="small" />
<PropertyList.Property label="Owner (Medium)" value="This is the new owner John" size="medium" />
<PropertyList.Property label="Status (Small)" value="Active" size="small" />
<PropertyList.Property
label="Key 1"
value="Value 1"
label="Notes (Full-Size)"
value={`${loremIpsumParagraph} ${loremIpsumParagraph}`}
size="full-size"
expandable
/>
<PropertyList.Property
label="Key 2"
value="Value 2"
label="Description (Large)"
value={`${loremIpsumParagraph}`}
size="large"
/>
</>
),
},
};

export const LongWord: Story = {
args: {
children: (
<>
<PropertyList.Property
fullWidth
label="Key 3"
value="Value 3"
label="Large"
value="ThisIsAVeryVeryVeryVeryVeryLongStringWithoutSpaces"
size="large"
/>
</>
),
},
render: (args) => <PropertyList {...args}/>,
} satisfies Meta<PropertyListArgs>;
};

export const ManyProperties: Story = {
args: {
children: (
<>
{Array.from({ length: 12 }).map((_, i) => {
return (
<PropertyList.Property
key={i}
label={`Key ${i + 1}`}
value={`Value ${i + 1}`}
/>
);
})}
</>
),
},
};

export const ManyPropertiesWithDifferentRandomSizes: Story = {
args: {
children: (
<>
{Array.from({ length: 12 }).map((_, i) => {
const size = sizes[Math.floor(Math.random() * sizes.length)];

return (
<PropertyList.Property
key={i}
label={`Key ${i + 1} (${size})`}
value={`Value ${i + 1}`}
size={size}
/>
);
})}
</>
),
},
};

export const WithViewMore: Story = {
args: {
children: (
<>
<PropertyList.Property
label="View More"
value={`${loremIpsumParagraph} ${loremIpsumParagraph}`}
size="full-size"
expandable
clampLines={3}
/>
</>
),
},
};
export const PropertyListStandard: Story = {};
Loading