Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<script setup lang="ts">
import type { DescriptionListItem } from '@bitrix24/b24ui-nuxt'

const items: DescriptionListItem[] = [
{
label: 'Documentation',
description: 'Getting started guide',
to: '/docs/getting-started'
},
{
label: 'External profile',
description: 'View on partner portal',
to: 'https://bitrix24.com',
target: '_blank'
},
{
label: 'Created',
description: 'Oct 6, 2024 08:37'
}
]
</script>

<template>
<B24DescriptionList :items="items" size="sm" />
</template>
13 changes: 13 additions & 0 deletions docs/content/docs/2.components/description-list.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ Use the `items` prop as an array of objects to control the value of the Descript
- [`actions?: ButtonProps[]`{lang="ts-type"}](#with-actions-in-items)
- [`orientation?: "horizontal" | "vertical"`{lang="ts-type"}](#with-actions-in-items)
- [`slot?: string`{lang="ts-type"}](#with-custom-slot)
- [`to?: RouteLocationRaw | string`{lang="ts-type"}](#with-link-in-items)
- [`target?: "_blank" | "_self" | "_parent" | "_top"`{lang="ts-type"}](#with-link-in-items)
- `class?: any`{lang="ts-type"}
- `b24ui?: { labelWrapper?: ClassNameValue, icon?: ClassNameValue, avatar?: ClassNameValue, label?: ClassNameValue, descriptionWrapper?: ClassNameValue, description?: ClassNameValue, actions?: ClassNameValue }`{lang="ts-type"}

Expand Down Expand Up @@ -231,6 +233,17 @@ name: 'description-list-custom-slot-example'
---
::

### With link in items

Use the `to` property on an item to render the description as a [Link](/docs/components/link/). Pair it with `target: '_blank'` for external destinations. Items that set their own `slot` are not affected.

::component-example
---
collapse: true
name: 'description-list-link-example'
---
::

## API

### Props
Expand Down
31 changes: 30 additions & 1 deletion playgrounds/demo/app/pages/components/description-list.vue
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ const itemsIcons: DescriptionListItem[] = [
label: 'Line 1.1',
description: 'Description 1.1',
avatar: {
src: '/b24ui/demo/avatar/employee.png'
src: '/avatar/employee.png'
}
},
{
Expand All @@ -141,6 +141,24 @@ const itemsIcons: DescriptionListItem[] = [
}
]

const itemsLink: DescriptionListItem[] = [
{
label: 'Account manager',
description: 'Sample owner',
to: '/users/owner'
},
{
label: 'External profile',
description: 'View on partner portal',
to: 'https://example.com',
target: '_blank'
},
{
label: 'Created',
description: 'Oct 6, 2024 08:37'
}
]

const itemsCustom: (DescriptionListItem & { value?: Date | string })[] = [
{
label: 'Amount',
Expand Down Expand Up @@ -198,6 +216,17 @@ const itemsCustom: (DescriptionListItem & { value?: Date | string })[] = [
</div>
</ExampleCard>

<ExampleCard title="link" :use-bg="isUseBg" class="sm:col-span-2">
<B24Separator class="my-3" type="dotted" />
<div class="mb-4 flex flex-wrap flex-col items-start justify-start gap-4">
<B24DescriptionList
legend="With link in items"
text="Use item.to to render the description as a B24Link."
:items="itemsLink"
/>
</div>
</ExampleCard>

<ExampleCard title="actions" :use-bg="isUseBg" class="sm:col-span-2">
<B24Separator class="my-3" type="dotted" />
<div class="mb-4 flex flex-wrap flex-col items-start justify-start gap-4">
Expand Down
29 changes: 29 additions & 0 deletions playgrounds/nuxt/app/pages/components/description-list.vue
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,24 @@ const itemsIcons: DescriptionListItem[] = [
}
]

const itemsLink: DescriptionListItem[] = [
{
label: 'Account manager',
description: 'Sample owner',
to: '/users/owner'
},
{
label: 'External profile',
description: 'View on partner portal',
to: 'https://example.com',
target: '_blank'
},
{
label: 'Created',
description: 'Oct 6, 2024 08:37'
}
]

const itemsCustom: (DescriptionListItem & { value?: Date | string })[] = [
{
label: 'Amount',
Expand Down Expand Up @@ -198,6 +216,17 @@ const itemsCustom: (DescriptionListItem & { value?: Date | string })[] = [
</div>
</ExampleCard>

<ExampleCard title="link" :use-bg="isUseBg" class="sm:col-span-2">
<B24Separator class="my-3" type="dotted" />
<div class="mb-4 flex flex-wrap flex-col items-start justify-start gap-4">
<B24DescriptionList
legend="With link in items"
text="Use item.to to render the description as a B24Link."
:items="itemsLink"
/>
</div>
</ExampleCard>

<ExampleCard title="actions" :use-bg="isUseBg" class="sm:col-span-2">
<B24Separator class="my-3" type="dotted" />
<div class="mb-4 flex flex-wrap flex-col items-start justify-start gap-4">
Expand Down
18 changes: 17 additions & 1 deletion src/runtime/components/DescriptionList.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<script lang="ts">
import type { VNode } from 'vue'
import type { AppConfig } from '@nuxt/schema'
import type { RouteLocationRaw } from 'vue-router'
import theme from '#build/b24ui/description-list'
import type { IconComponent } from '../types/icons'
import type { AvatarProps } from './Avatar.vue'
Expand All @@ -20,6 +21,15 @@ export interface DescriptionListItem {
avatar?: AvatarProps
slot?: string
description?: string
/**
* Render the description as a link. Accepts any value supported by `B24Link` `to`.
* Ignored when `slot` is set, since the consumer is rendering the description manually.
*/
to?: RouteLocationRaw | string
/**
* Forwarded to `B24Link` when `to` is set.
*/
target?: '_blank' | '_parent' | '_self' | '_top' | (string & {}) | null
/**
* The orientation between the content and the actions.
* @defaultValue 'vertical'
Expand Down Expand Up @@ -83,6 +93,7 @@ import { get } from '../utils'
import { tv } from '../utils/tv'
import B24Avatar from './Avatar.vue'
import B24Button from './Button.vue'
import B24Link from './Link.vue'

const _props = withDefaults(defineProps<DescriptionListProps<T>>(), {
labelKey: 'label',
Expand Down Expand Up @@ -233,7 +244,12 @@ const normalizedItems = computed(() => {
})"
>
<slot name="description" :item="item" :index="index" :b24ui="b24ui">
{{ item.description }}
<B24Link v-if="item.to" :to="item.to" :target="item.target">
{{ item.description }}
</B24Link>
<template v-else>
{{ item.description }}
</template>
</slot>
</span>
<span
Expand Down
25 changes: 25 additions & 0 deletions test/components/DescriptionList.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ describe('DescriptionList', () => {
['with class', { props: { items: baseItems, class: 'custom-class' } }],
['with b24ui', { props: { items: baseItems, b24ui: { text: 'font-(--ui-font-weight-bold)' } } }],
['with empty items', { props: { items: [] } }],
['with link description', { props: { items: [createItem({ to: '/docs', description: 'Documentation' })] } }],
['with link description and target', { props: { items: [createItem({ to: 'https://bitrix24.com', target: '_blank', description: 'Bitrix24' })] } }],
// Custom keys
['with labelKey and descriptionKey', {
props: {
Expand Down Expand Up @@ -85,6 +87,29 @@ describe('DescriptionList', () => {
])

describe('accessibility', () => {
it('renders the description as a link only when `to` is set', async () => {
const plain = await mountSuspended(DescriptionList, {
props: { items: [createItem({ description: 'Documentation' })] }
})
expect(plain.find('[data-slot="text"] a').exists()).toBe(false)
expect(plain.text()).toContain('Documentation')

const linked = await mountSuspended(DescriptionList, {
props: { items: [createItem({ to: '/docs', description: 'Documentation' })] }
})
const link = linked.find('a')
expect(link.exists()).toBe(true)
expect(link.attributes('href')).toBe('/docs')
expect(link.text()).toBe('Documentation')
})

it('forwards `target` to the link', async () => {
const wrapper = await mountSuspended(DescriptionList, {
props: { items: [createItem({ to: 'https://bitrix24.com', target: '_blank', description: 'Bitrix24' })] }
})
expect(wrapper.find('a').attributes('target')).toBe('_blank')
})

it('passes accessibility tests with basic items', async () => {
const wrapper = await mountSuspended(DescriptionList, {
props: {
Expand Down
32 changes: 32 additions & 0 deletions test/components/__snapshots__/DescriptionList-vue.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,38 @@ exports[`DescriptionList > renders with legend correctly 1`] = `
</div>"
`;

exports[`DescriptionList > renders with link description and target correctly 1`] = `
"<div data-slot="root" class="w-full shrink-0">
<!--v-if-->
<!--v-if-->
<dl data-slot="container" class="grid grid-cols-1 sm:grid-cols-[min(50%,theme(spacing.80))_auto] mt-3 text-(length:--ui-font-size-lg)">
<dt data-slot="labelWrapper" class="col-start-1 border-t first:border-none sm:border-t flex flex-nowrap flex-row items-center justify-start gap-1.5 border-(--ui-color-divider-vibrant-default) sm:border-(--ui-color-divider-vibrant-default) text-description pt-3 sm:py-3">
<!--v-if--><span data-slot="label" class="">Test Item</span>
</dt>
<dd data-orientation="vertical" data-slot="descriptionWrapper" class="sm:border-t sm:[&amp;:nth-child(2)]:border-none sm:border-(--ui-color-divider-vibrant-default) text-label pb-3 pt-1 sm:py-3"><span data-slot="description" class=""><a href="https://bitrix24.com" rel="noopener noreferrer" target="_blank" class="cursor-pointer focus-visible:outline-(--ui-color-accent-main-primary) focus-visible:outline-1 focus-visible:rounded-[4px] text-start text-(--ui-color-design-selection-content) underline-offset-2 hover:text-(--ui-color-accent-main-primary-alt-2) hover:underline">Bitrix24</a></span>
<!--v-if-->
</dd>
</dl>
<!--v-if-->
</div>"
`;

exports[`DescriptionList > renders with link description correctly 1`] = `
"<div data-slot="root" class="w-full shrink-0">
<!--v-if-->
<!--v-if-->
<dl data-slot="container" class="grid grid-cols-1 sm:grid-cols-[min(50%,theme(spacing.80))_auto] mt-3 text-(length:--ui-font-size-lg)">
<dt data-slot="labelWrapper" class="col-start-1 border-t first:border-none sm:border-t flex flex-nowrap flex-row items-center justify-start gap-1.5 border-(--ui-color-divider-vibrant-default) sm:border-(--ui-color-divider-vibrant-default) text-description pt-3 sm:py-3">
<!--v-if--><span data-slot="label" class="">Test Item</span>
</dt>
<dd data-orientation="vertical" data-slot="descriptionWrapper" class="sm:border-t sm:[&amp;:nth-child(2)]:border-none sm:border-(--ui-color-divider-vibrant-default) text-label pb-3 pt-1 sm:py-3"><span data-slot="description" class=""><a href="/docs" class="cursor-pointer focus-visible:outline-(--ui-color-accent-main-primary) focus-visible:outline-1 focus-visible:rounded-[4px] text-start text-(--ui-color-design-selection-content) underline-offset-2 hover:text-(--ui-color-accent-main-primary-alt-2) hover:underline" isaction="false">Documentation</a></span>
<!--v-if-->
</dd>
</dl>
<!--v-if-->
</div>"
`;

exports[`DescriptionList > renders with multiple actions correctly 1`] = `
"<div data-slot="root" class="w-full shrink-0">
<!--v-if-->
Expand Down
32 changes: 32 additions & 0 deletions test/components/__snapshots__/DescriptionList.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,38 @@ exports[`DescriptionList > renders with legend correctly 1`] = `
</div>"
`;

exports[`DescriptionList > renders with link description and target correctly 1`] = `
"<div data-slot="root" class="w-full shrink-0">
<!--v-if-->
<!--v-if-->
<dl data-slot="container" class="grid grid-cols-1 sm:grid-cols-[min(50%,theme(spacing.80))_auto] mt-3 text-(length:--ui-font-size-lg)">
<dt data-slot="labelWrapper" class="col-start-1 border-t first:border-none sm:border-t flex flex-nowrap flex-row items-center justify-start gap-1.5 border-(--ui-color-divider-vibrant-default) sm:border-(--ui-color-divider-vibrant-default) text-description pt-3 sm:py-3">
<!--v-if--><span data-slot="label" class="">Test Item</span>
</dt>
<dd data-orientation="vertical" data-slot="descriptionWrapper" class="sm:border-t sm:[&amp;:nth-child(2)]:border-none sm:border-(--ui-color-divider-vibrant-default) text-label pb-3 pt-1 sm:py-3"><span data-slot="description" class=""><a href="https://bitrix24.com" rel="noopener noreferrer" target="_blank" class="cursor-pointer focus-visible:outline-(--ui-color-accent-main-primary) focus-visible:outline-1 focus-visible:rounded-[4px] text-start text-(--ui-color-design-selection-content) underline-offset-2 hover:text-(--ui-color-accent-main-primary-alt-2) hover:underline">Bitrix24</a></span>
<!--v-if-->
</dd>
</dl>
<!--v-if-->
</div>"
`;

exports[`DescriptionList > renders with link description correctly 1`] = `
"<div data-slot="root" class="w-full shrink-0">
<!--v-if-->
<!--v-if-->
<dl data-slot="container" class="grid grid-cols-1 sm:grid-cols-[min(50%,theme(spacing.80))_auto] mt-3 text-(length:--ui-font-size-lg)">
<dt data-slot="labelWrapper" class="col-start-1 border-t first:border-none sm:border-t flex flex-nowrap flex-row items-center justify-start gap-1.5 border-(--ui-color-divider-vibrant-default) sm:border-(--ui-color-divider-vibrant-default) text-description pt-3 sm:py-3">
<!--v-if--><span data-slot="label" class="">Test Item</span>
</dt>
<dd data-orientation="vertical" data-slot="descriptionWrapper" class="sm:border-t sm:[&amp;:nth-child(2)]:border-none sm:border-(--ui-color-divider-vibrant-default) text-label pb-3 pt-1 sm:py-3"><span data-slot="description" class=""><a href="/docs" class="cursor-pointer focus-visible:outline-(--ui-color-accent-main-primary) focus-visible:outline-1 focus-visible:rounded-[4px] text-start text-(--ui-color-design-selection-content) underline-offset-2 hover:text-(--ui-color-accent-main-primary-alt-2) hover:underline">Documentation</a></span>
<!--v-if-->
</dd>
</dl>
<!--v-if-->
</div>"
`;

exports[`DescriptionList > renders with multiple actions correctly 1`] = `
"<div data-slot="root" class="w-full shrink-0">
<!--v-if-->
Expand Down
Loading