Skip to content
Merged
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
84cdde4
feat(pie-css): DSW-4116 mixin for slotted element allowlists
jamieomaguire Jul 1, 2026
fcccb12
fix lint
jamieomaguire Jul 1, 2026
3888ebb
initial list-item markup and props
jamieomaguire Jul 1, 2026
42ec0f1
basic list item structure styling
jamieomaguire Jul 1, 2026
1d4999c
typograpghy
jamieomaguire Jul 1, 2026
8d46787
add named slots to the scss mixin
jamieomaguire Jul 1, 2026
c4ebbd4
borders
jamieomaguire Jul 1, 2026
6e7a7f8
fix lint
jamieomaguire Jul 1, 2026
122d831
comment
jamieomaguire Jul 1, 2026
842e10a
compact padding
jamieomaguire Jul 1, 2026
1ababb4
fix lint
jamieomaguire Jul 1, 2026
06126b2
icons, fix trailing slot display, flex alignment and lint fixes
jamieomaguire Jul 1, 2026
28bd389
todo readme comments
jamieomaguire Jul 1, 2026
a2a95a1
handle multiline text and more story demos
jamieomaguire Jul 1, 2026
4df3e20
fix lint
jamieomaguire Jul 1, 2026
168df5c
organise stories
jamieomaguire Jul 1, 2026
f8da6db
handle avatar padding quirk and disallow large comps in compact mode
jamieomaguire Jul 1, 2026
21c1fe7
readme and slot policing
jamieomaguire Jul 1, 2026
e6dde0f
add tests + readme and fix metatext single line alignment
jamieomaguire Jul 2, 2026
6ee328c
fix lint
jamieomaguire Jul 2, 2026
ad6a8e0
test scss mixin
jamieomaguire Jul 2, 2026
dc88e5b
tidy ts
jamieomaguire Jul 2, 2026
81930b6
safari firefox style fix
jamieomaguire Jul 2, 2026
d0a6b48
display block list-items
jamieomaguire Jul 3, 2026
6c43400
Merge branch 'main' into dsw-4116-plaintext-list
jamieomaguire Jul 3, 2026
8016ae4
remove slot policing
jamieomaguire Jul 3, 2026
fb1a059
apply gap padding to slotted trailing content and metatext instead of…
jamieomaguire Jul 3, 2026
6a5aaa1
Merge branch 'main' into dsw-4116-plaintext-list
jamieomaguire Jul 3, 2026
b56148f
Merge branch 'main' into dsw-4116-plaintext-list
jamieomaguire Jul 3, 2026
d1b3cb3
rename default story
jamieomaguire Jul 3, 2026
72f5f1e
no longer reflect props
jamieomaguire Jul 7, 2026
16cf329
classmap
jamieomaguire Jul 7, 2026
928cf3e
Merge branch 'main' into dsw-4116-plaintext-list
jamieomaguire Jul 7, 2026
490c8e3
tidy up css overrides and variable names
jamieomaguire Jul 7, 2026
d26d751
Merge branch 'main' into dsw-4116-plaintext-list
jamieomaguire Jul 7, 2026
8770bcb
update css
jamieomaguire Jul 7, 2026
73d4dbe
update alignment override var
jamieomaguire Jul 7, 2026
5fbfca9
remove requiredpropertyvalue decorator use
jamieomaguire Jul 7, 2026
13cb968
camelCase and boolean bindings
jamieomaguire Jul 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fiery-terms-obey.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@justeattakeaway/pie-storybook": minor
---

[Added] - New pie list examples for the plain text list/list item
5 changes: 5 additions & 0 deletions .changeset/new-rules-repair.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@justeattakeaway/pie-list": minor
---

[Added] - Plain text list/list item component variation.
6 changes: 6 additions & 0 deletions apps/pie-storybook/stories/pie-list.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { Meta } from '@storybook/addon-docs/blocks';
import { Markdown } from '@storybook/addon-docs/blocks';
import readme from '@justeattakeaway/pie-list/README.md?raw';

<Meta title="Components/List" name="Overview" />
<Markdown>{readme}</Markdown>
228 changes: 217 additions & 11 deletions apps/pie-storybook/stories/pie-list.stories.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,80 @@
import { html } from 'lit';
import { html, nothing } from 'lit';
import { type Meta } from '@storybook/web-components';

import '@justeattakeaway/pie-webc/components/list';
import { type ListProps } from '@justeattakeaway/pie-webc/components/list';
import '@justeattakeaway/pie-webc/components/list-item';
import '@justeattakeaway/pie-webc/components/thumbnail';
import '@justeattakeaway/pie-webc/components/tag';
import '@justeattakeaway/pie-icons-webc/dist/IconPlaceholder';

import { createStory } from '../utilities';
import { createStory, type TemplateFunction } from '../utilities';

type ListStoryMeta = Meta<ListProps>;
// The list itself has no props; these are the `pie-list-item` props plus two
// helper controls (`leadingContent` / `trailingContent`) for experimenting with
// the slots. The latter two are not component props.
type ListPlaygroundProps = {
primaryText: string;
secondaryText: string;
metaText: string;
isCompact: boolean;
isBold: boolean;
hasMedia: boolean;
leadingContent: 'none' | 'icon' | 'thumbnail';
trailingContent: 'none' | 'icon' | 'tag';
};

type ListStoryMeta = Meta<ListPlaygroundProps>;

const defaultArgs: ListProps = {};
const defaultArgs: ListPlaygroundProps = {
primaryText: 'Primary text',
secondaryText: 'Secondary text',
metaText: '',
isCompact: false,
isBold: false,
hasMedia: false,
leadingContent: 'none',
trailingContent: 'icon',
};

const listStoryMeta: ListStoryMeta = {
title: 'Components/List',
component: 'pie-list',
argTypes: {},
argTypes: {
primaryText: {
description: 'The main text of the item.',
control: 'text',
},
secondaryText: {
description: 'Optional additional detail, rendered on a second line.',
control: 'text',
},
metaText: {
description: 'Optional trailing text. Mutually exclusive with the `trailing` slot (if set, the trailing slot is not rendered).',
control: 'text',
},
isCompact: {
description: 'Reduces the item height. Do not use with secondary text or slotted media.',
control: 'boolean',
},
isBold: {
description: 'Sets the primary text to a bold font-weight.',
control: 'boolean',
},
hasMedia: {
description: 'Required to display slotted media (e.g. `pie-thumbnail`). Also reduces block padding on single-line items.',
control: 'boolean',
},
leadingContent: {
description: '**Not a component prop.** Chooses what to slot into the `leading` slot.',
control: 'select',
options: ['none', 'icon', 'thumbnail'],
},
trailingContent: {
description: '**Not a component prop.** Chooses what to slot into the `trailing` slot.',
control: 'select',
options: ['none', 'icon', 'tag'],
},
},
args: defaultArgs,
parameters: {
design: {
Expand All @@ -25,10 +86,155 @@ const listStoryMeta: ListStoryMeta = {

export default listStoryMeta;

// TODO: remove the eslint-disable rule when props are added
// eslint-disable-next-line no-empty-pattern
const Template = ({}: ListProps) => html`
<pie-list></pie-list>
// Slot content helpers -------------------------------------------------------

const renderLeading = (leadingContent: ListPlaygroundProps['leadingContent']) => {
if (leadingContent === 'icon') return html`<icon-placeholder slot="leading"></icon-placeholder>`;
if (leadingContent === 'thumbnail') return html`<pie-thumbnail slot="leading" size="40" backgroundColor="strong" variant="outline"></pie-thumbnail>`;
return nothing;
};

const renderTrailing = (trailingContent: ListPlaygroundProps['trailingContent']) => {
if (trailingContent === 'icon') return html`<icon-placeholder slot="trailing"></icon-placeholder>`;
if (trailingContent === 'tag') return html`<pie-tag slot="trailing">Label</pie-tag>`;
return nothing;
};

const renderItem = (args: ListPlaygroundProps, itemStyle = '') => html`
<pie-list-item
style=${itemStyle}
.primaryText=${args.primaryText}
.secondaryText=${args.secondaryText || undefined}
.metaText=${args.metaText || undefined}
.isCompact=${args.isCompact}
.isBold=${args.isBold}
.hasMedia=${args.hasMedia}>
Comment thread
jamieomaguire marked this conversation as resolved.
Outdated
${renderLeading(args.leadingContent)}
${renderTrailing(args.trailingContent)}
</pie-list-item>
`;

export const Default = createStory<ListProps>(Template, defaultArgs)();
// Surfaces the component's usage rules when the controls are set to a combination
// that the component intentionally handles (so the behaviour is not surprising).
const buildNotes = (args: ListPlaygroundProps) => {
const notes: string[] = [];
if (args.leadingContent === 'thumbnail' && !args.hasMedia) notes.push('Enable "hasMedia" to display the slotted thumbnail.');
if (args.isCompact && args.leadingContent === 'thumbnail') notes.push('Media is not displayed in compact items.');
if (args.isCompact && args.secondaryText) notes.push('Avoid secondary text in compact items.');
if (args.metaText && args.trailingContent !== 'none') notes.push('"metaText" replaces the trailing slot, so the trailing content is not shown.');
return notes;
};

/**
* Every story renders the same arg-driven list (so the controls are live on all of
* them). `pie-list` must always have an accessible name; we use `aria-labelledby`
* pointing at a visible heading (preferred over `aria-label`, so the visible and
* accessible names stay in sync). `headingId` is unique per story to keep the
* association valid when several stories render on one docs page. `itemStyle` is applied to each
* `pie-list-item` (not the list) so item-level CSS variables such as `--list-item-inline-padding`
* take effect (a value on `pie-list` cannot override the item's own default).
*/
const makeListTemplate = (headingId: string, heading: string, itemStyle = ''): TemplateFunction<ListPlaygroundProps> => (args) => {
const notes = buildNotes(args);

return html`
<style>
pie-list {
min-width: 300px;
max-width: 500px;
}
</style>
${notes.length ? html`<p><strong>Note:</strong> ${notes.join(' ')}</p>` : nothing}
<h2 id=${headingId}>${heading}</h2>
<pie-list aria-labelledby=${headingId}>
${renderItem(args, itemStyle)}
${renderItem(args, itemStyle)}
${renderItem(args, itemStyle)}
</pie-list>
`;
};

// Stories --------------------------------------------------------------------

/**
* Interactive example. Use the controls to experiment with the `pie-list-item`
* properties and slotted content.
*/
export const Default = createStory<ListPlaygroundProps>(
makeListTemplate('list-playground-heading', 'Example list'),
defaultArgs,
)();

/**
* `isBold` sets the primary text to a bold font-weight.
*/
export const Bold = createStory<ListPlaygroundProps>(
makeListTemplate('list-bold-heading', 'Bold primary text'),
defaultArgs,
)({ isBold: true });

/**
* `isCompact` reduces the vertical space of each item. Do not use it with
* secondary text or slotted media.
*/
export const Compact = createStory<ListPlaygroundProps>(
makeListTemplate('list-compact-heading', 'Compact'),
defaultArgs,
)({ isCompact: true, secondaryText: '' });

/**
* A trailing `pie-tag` slotted into each item.
*/
export const WithTag = createStory<ListPlaygroundProps>(
makeListTemplate('list-tag-heading', 'Trailing tag'),
defaultArgs,
)({ trailingContent: 'tag' });

/**
* `metaText` renders a trailing text string. It is mutually exclusive with the
* `trailing` slot.
*/
export const MetaText = createStory<ListPlaygroundProps>(
makeListTemplate('list-meta-heading', 'Meta text'),
defaultArgs,
)({ metaText: 'Meta text', trailingContent: 'none' });

/**
* Slotted media (`pie-thumbnail`, always `size="40"`) requires `hasMedia`. Toggle
* `secondaryText` in the controls to see the block padding adjust.
*/
export const Media = createStory<ListPlaygroundProps>(
makeListTemplate('list-media-heading', 'Media'),
defaultArgs,
)({ hasMedia: true, leadingContent: 'thumbnail', trailingContent: 'none' });

/**
* `--list-item-alignment: center` vertically centres the item content.
*/
export const AlignmentOverride = createStory<ListPlaygroundProps>(
makeListTemplate('list-alignment-heading', 'Centre aligned', '--list-item-alignment: center;'),
defaultArgs,
)();

/**
* `--list-item-inline-padding` sets the inline padding of the items
* (any spacing token, or `0` to remove it).
*/
export const InlinePaddingOverride = createStory<ListPlaygroundProps>(
makeListTemplate('list-padding-heading', 'No inline padding', '--list-item-inline-padding: 0;'),
defaultArgs,
)({ leadingContent: 'none' });

/**
* Long text wraps within each item.
*/
export const LongText = createStory<ListPlaygroundProps>(
makeListTemplate('list-long-text-heading', 'Long content'),
defaultArgs,
)({
primaryText: 'Primary text that goes on far too long Primary text that goes on far too long',
secondaryText: 'Secondary text that goes on far too long Secondary text that goes on far too long',
metaText: 'Some very long awful meta text',
leadingContent: 'icon',
trailingContent: 'none',
});
Loading
Loading