Skip to content

Commit d5df1cd

Browse files
authored
feat: childrenTag prop for SfListItem (#2888)
* feat:childrenTag prop for SfListItem * fix: cr fixes * fix: lint fixes * fix: cr fixes
1 parent aab5262 commit d5df1cd

File tree

9 files changed

+70
-16
lines changed

9 files changed

+70
-16
lines changed

.changeset/dry-humans-wave.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@storefront-ui/react': minor
3+
'@storefront-ui/vue': minor
4+
---
5+
6+
New prop for defining tags for children in SfListItem component

apps/docs/components/components/listitem.md

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -87,14 +87,16 @@ ListItem component can be rendered as an `<li>` or `<a>` or any other tag by pro
8787
| `disabled ` | `boolean` | `false` | |
8888
| `selected` | `boolean` | `false` | |
8989
<!-- vue -->
90-
| `tag` | `string` | `'li'` | any tag name |
90+
| `tag` | `string` | `'li'` | any tag name |
91+
| `childrenTag` | `string` | `'span'` | any tag name |
9192
<!-- end vue -->
9293
<!-- react -->
93-
| `as` | `ReactElement` | `'li'` | any tag name |
94-
| `children` | `ReactNode` | | label content |
95-
| `slotPrefix` | `ReactNode` | | right side content |
96-
| `slotSuffix` | `ReactNode` | | left side content |
97-
| `className` | `string` | | |
94+
| `as` | `ReactElement` | `'li'` | any tag name |
95+
| `children` | `ReactNode` | | label content |
96+
| `childrenTag` | `ReactElement` | `'span'` | any tag name |
97+
| `slotPrefix` | `ReactNode` | | right side content |
98+
| `slotSuffix` | `ReactNode` | | left side content |
99+
| `className` | `string` | | |
98100
<!-- end react -->
99101

100102
<!-- vue -->

apps/preview/next/pages/examples/SfListItem.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ function Example() {
4343
modelName: 'as',
4444
description: 'Change a tag to any other tag',
4545
},
46+
{
47+
type: 'text',
48+
modelName: 'childrenTag',
49+
description: 'Change a tag for children to any other tag',
50+
},
4651
{
4752
type: 'text',
4853
modelName: 'label',
@@ -99,6 +104,7 @@ function Example() {
99104
],
100105
{
101106
as: 'li',
107+
childrenTag: 'span',
102108
label: 'Label',
103109
size: SfListItemSize.base,
104110
counter: 123,
@@ -124,6 +130,7 @@ function Example() {
124130
slotSuffix={suffixSlotOptions.getValue(state.get.slotSuffix)?.({
125131
size: state.get.size === 'sm' ? 'sm' : 'base',
126132
})}
133+
childrenTag={state.get.childrenTag}
127134
onClick={() => state.set((currentState) => ({ ...currentState, selected: !currentState.selected }))}
128135
>
129136
<span className="break-words">

apps/preview/nuxt/pages/examples/SfListItem.vue

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ const { controlsAttrs, state } = prepareControls(
5151
modelName: 'tag',
5252
description: 'Change a tag to any other tag',
5353
},
54+
{
55+
type: 'text',
56+
modelName: 'childrenTag',
57+
description: 'Change a tag for children to any other tag',
58+
},
5459
{
5560
type: 'text',
5661
modelName: 'label',
@@ -105,6 +110,7 @@ const { controlsAttrs, state } = prepareControls(
105110
],
106111
{
107112
tag: ref('li'),
113+
childrenTag: ref('span'),
108114
label: ref<string>('Label'),
109115
size: ref<SfListItemSize>(SfListItemSize.base),
110116
counter: ref(123),

packages/sfui/frameworks/react/components/SfListItem/SfListItem.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const sizeClasses = {
99
};
1010

1111
const defaultListItemTag = 'li';
12+
const defaultChildrenTag = 'span';
1213

1314
const SfListItem = polymorphicForwardRef<typeof defaultListItemTag, SfListItemProps>((props, ref) => {
1415
const {
@@ -19,11 +20,13 @@ const SfListItem = polymorphicForwardRef<typeof defaultListItemTag, SfListItemPr
1920
slotPrefix,
2021
slotSuffix,
2122
as,
23+
childrenTag,
2224
children,
2325
...attributes
2426
} = props;
2527

2628
const Tag = as || defaultListItemTag;
29+
const ChildrenTag = childrenTag || defaultChildrenTag;
2730

2831
return (
2932
<Tag
@@ -41,9 +44,13 @@ const SfListItem = polymorphicForwardRef<typeof defaultListItemTag, SfListItemPr
4144
data-testid="list-item"
4245
{...attributes}
4346
>
44-
{slotPrefix && <span className={disabled ? 'text-disabled-500' : 'text-neutral-500'}>{slotPrefix}</span>}
45-
<span className="flex flex-col w-full min-w-0">{children}</span>
46-
{slotSuffix && <span className={disabled ? 'text-disabled-500' : 'text-neutral-500'}>{slotSuffix}</span>}
47+
{slotPrefix && (
48+
<ChildrenTag className={disabled ? 'text-disabled-500' : 'text-neutral-500'}>{slotPrefix}</ChildrenTag>
49+
)}
50+
<ChildrenTag className="flex flex-col w-full min-w-0">{children}</ChildrenTag>
51+
{slotSuffix && (
52+
<ChildrenTag className={disabled ? 'text-disabled-500' : 'text-neutral-500'}>{slotSuffix}</ChildrenTag>
53+
)}
4754
</Tag>
4855
);
4956
});

packages/sfui/frameworks/react/components/SfListItem/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { ReactNode, PropsWithChildren } from 'react';
1+
import type { ReactNode, PropsWithChildren, ElementType } from 'react';
22
import type { PropsWithStyle } from '@storefront-ui/react';
33
import { SfListItemSize } from '@storefront-ui/shared';
44

@@ -10,4 +10,5 @@ export interface SfListItemProps extends PropsWithChildren, PropsWithStyle {
1010
slotSuffix?: ReactNode;
1111
slotPrefix?: ReactNode;
1212
role?: string;
13+
childrenTag?: ElementType;
1314
}

packages/sfui/frameworks/vue/components/SfListItem/SfListItem.vue

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ defineProps({
2727
type: [String, Object] as PropType<string | ConcreteComponent>,
2828
default: undefined,
2929
},
30+
childrenTag: {
31+
type: String,
32+
default: 'span',
33+
},
3034
});
3135
</script>
3236

@@ -41,14 +45,14 @@ defineProps({
4145
:disabled="disabled"
4246
data-testid="list-item"
4347
>
44-
<span v-if="$slots.prefix" :class="disabled ? 'text-disabled-500' : 'text-neutral-500'">
48+
<component :is="childrenTag" v-if="$slots.prefix" :class="disabled ? 'text-disabled-500' : 'text-neutral-500'">
4549
<slot name="prefix" />
46-
</span>
47-
<span class="flex flex-col w-full min-w-0">
50+
</component>
51+
<component :is="childrenTag" class="flex flex-col w-full min-w-0">
4852
<slot />
49-
</span>
50-
<span v-if="$slots.suffix" :class="disabled ? 'text-disabled-500' : 'text-neutral-500'">
53+
</component>
54+
<component :is="childrenTag" v-if="$slots.suffix" :class="disabled ? 'text-disabled-500' : 'text-neutral-500'">
5155
<slot name="suffix" />
52-
</span>
56+
</component>
5357
</component>
5458
</template>

packages/tests/components/SfListItem/SfListItem.PageObject.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@ export default class SfListItemObject extends BasePage {
88
return this;
99
}
1010

11+
childrenHaveTag(tagName: string) {
12+
this.container.children().each((child) => {
13+
const childTagName = child[0].tagName;
14+
expect(childTagName).to.equal(tagName);
15+
});
16+
return this;
17+
}
18+
1119
hasContent(content: string) {
1220
this.container.contains(content);
1321
return this;

packages/tests/components/SfListItem/SfListItem.cy.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ describe('SfListItem', () => {
1515
let selected: boolean;
1616
let slotPrefix: boolean;
1717
let slotSuffix: boolean;
18+
let childrenTag: string | React.ElementType;
1819
let onChangeSpy: Cypress.Agent<sinon.SinonSpy>;
1920

2021
const page = () => new SfListItemBaseObject('list-item');
@@ -33,6 +34,7 @@ describe('SfListItem', () => {
3334
disabled,
3435
size,
3536
selected,
37+
childrenTag,
3638
onClick: onChangeSpy,
3739
},
3840
slots: {
@@ -46,6 +48,7 @@ describe('SfListItem', () => {
4648
disabled={disabled}
4749
selected={selected}
4850
size={size}
51+
childrenTag={childrenTag}
4952
slotPrefix={slotPrefix && <SfIconCheckCircleReact />}
5053
slotSuffix={slotSuffix && <SfIconCircleReact />}
5154
onClick={onChangeSpy}
@@ -122,6 +125,16 @@ describe('SfListItem', () => {
122125
});
123126
});
124127

128+
describe('when childrenTag is set to div', () => {
129+
before(() => (childrenTag = 'div'));
130+
after(() => (childrenTag = 'span'));
131+
it(`should render child as div tag`, () => {
132+
initializeComponent();
133+
134+
page().childrenHaveTag('DIV').makeSnapshot();
135+
});
136+
});
137+
125138
describe('when component is clicked', () => {
126139
before(() => (onChangeSpy = cy.spy()));
127140

0 commit comments

Comments
 (0)