Skip to content

Commit 956e64d

Browse files
authored
release of v10.101.2 (#7181)
2 parents 814cfff + b5b82a0 commit 956e64d

30 files changed

+759
-70
lines changed

packages/dnb-design-system-portal/src/docs/uilib/components/list/events.mdx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,15 @@ showTabs: true
33
---
44

55
import PropertiesTable from 'dnb-design-system-portal/src/shared/parts/PropertiesTable'
6-
import { ItemActionEvents } from '@dnb/eufemia/src/components/list/ListDocs'
6+
import {
7+
ItemActionEvents,
8+
ItemAccordionEvents,
9+
} from '@dnb/eufemia/src/components/list/ListDocs'
710

811
## List.Item.Action Events
912

1013
<PropertiesTable props={ItemActionEvents} />
14+
15+
## List.Item.Accordion Events
16+
17+
<PropertiesTable props={ItemAccordionEvents} />

packages/dnb-design-system-portal/src/docs/uilib/components/stat.mdx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ status: 'new'
55
showTabs: true
66
tabs:
77
- title: Info
8-
key: /uilib/components/stat/info
8+
key: /info
99
- title: Demos
10-
key: /uilib/components/stat/demos
10+
key: /demos
1111
- title: Properties
12-
key: /uilib/components/stat/properties
12+
key: /properties
1313
---
1414

1515
import StatInfo from 'Docs/uilib/components/stat/info'

packages/dnb-design-system-portal/src/docs/uilib/components/stat/info.mdx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,8 @@ import { Stat } from '@dnb/eufemia'
4949
- When e.g. `signDisplay="always"` is used, the sign is rendered as a separate visual element with CSS spacing, while the accessible text stays based on the formatted number string.
5050

5151
- All Stat variants keep dedicated accessibility handling. `Currency`, `Percent`, and `Trend` use a dedicated screen-reader value (`.dnb-sr-only`) based on the formatted content. `Rating` uses an accessible label (`role="img"` + `aria-label`) that includes value and max.
52+
53+
## Relevant links
54+
55+
- [Source code](https://github.com/dnbexperience/eufemia/tree/main/packages/dnb-eufemia/src/components/stat)
56+
- [Docs code](https://github.com/dnbexperience/eufemia/tree/main/packages/dnb-design-system-portal/src/docs/uilib/components/stat)

packages/dnb-eufemia/src/components/list/ItemAccordion.tsx

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { omitSpacingProps, pickSpacingProps } from '../flex/utils'
1919
import ItemIcon from './ItemIcon'
2020
import ItemTitle from './ItemTitle'
2121
import { createSkeletonClass } from '../skeleton/SkeletonHelper'
22+
import { warn } from '../../shared/component-helper'
2223
import Context from '../../shared/Context'
2324

2425
export type ItemAccordionIconPosition = 'left' | 'right'
@@ -41,7 +42,6 @@ export type ItemAccordionProps = {
4142
} & Omit<ItemContentProps, 'title'>
4243

4344
const ItemAccordionContext = createContext<{
44-
open?: boolean
4545
openState: boolean
4646
pending?: boolean
4747
disabled?: boolean
@@ -89,7 +89,6 @@ function ItemAccordion(props: ItemAccordionProps) {
8989
return (
9090
<ItemAccordionContext.Provider
9191
value={{
92-
open,
9392
openState,
9493
pending,
9594
disabled: appliedDisabled,
@@ -124,36 +123,26 @@ function ItemAccordion(props: ItemAccordionProps) {
124123
ItemAccordion._supportsSpacingProps = true
125124

126125
export type AccordionHeaderProps = {
127-
open?: boolean
128126
onClick?: (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => void
129127
} & ItemContentProps
130128

131129
function AccordionHeader(props: AccordionHeaderProps) {
132130
const { className, children, ...rest } = props
133-
const {
134-
setOpen,
135-
onClick,
136-
pending,
137-
disabled,
138-
chevronPosition,
139-
accordionId,
140-
openState,
141-
icon,
142-
title,
143-
} = useContext(ItemAccordionContext)
144-
131+
const accordionContext = useContext(ItemAccordionContext)
145132
const context = useContext(Context)
146133
const inheritedSkeleton = useContext(ListContext)?.skeleton
147-
const isInactive = pending || disabled
134+
135+
const isInactive =
136+
accordionContext?.pending || accordionContext?.disabled
148137

149138
const handleClick = useCallback(
150139
(event: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
151-
if (!isInactive) {
152-
setOpen((prev) => !prev)
153-
onClick && onClick(event)
140+
if (!isInactive && accordionContext) {
141+
accordionContext.setOpen((prev) => !prev)
142+
accordionContext.onClick && accordionContext.onClick(event)
154143
}
155144
},
156-
[onClick, isInactive, setOpen]
145+
[accordionContext, isInactive]
157146
)
158147

159148
const handleKeyDown = useCallback(
@@ -168,6 +157,16 @@ function AccordionHeader(props: AccordionHeaderProps) {
168157
[handleClick]
169158
)
170159

160+
if (!accordionContext) {
161+
warn(
162+
'List.Item.Accordion.Header should be used inside List.Item.Accordion.'
163+
)
164+
return null
165+
}
166+
167+
const { chevronPosition, accordionId, openState, icon, title } =
168+
accordionContext
169+
171170
const content = (
172171
<FlexItem
173172
className={classnames(
@@ -212,11 +211,18 @@ AccordionHeader._supportsSpacingProps = true
212211
function AccordionContent(props: ItemContentProps) {
213212
const { className, children, ...rest } = props
214213
const context = useContext(Context)
215-
const { openState, accordionId, keepInDOM } = useContext(
216-
ItemAccordionContext
217-
)
214+
const accordionContext = useContext(ItemAccordionContext)
218215
const inheritedSkeleton = useContext(ListContext)?.skeleton
219216

217+
if (!accordionContext) {
218+
warn(
219+
'List.Item.Accordion.Content should be used inside List.Item.Accordion.'
220+
)
221+
return null
222+
}
223+
224+
const { openState, accordionId, keepInDOM } = accordionContext
225+
220226
const spacingProps = pickSpacingProps(rest)
221227

222228
const content = (
@@ -229,7 +235,6 @@ function AccordionContent(props: ItemContentProps) {
229235
id={`${accordionId}-content`}
230236
aria-labelledby={`${accordionId}-header`}
231237
aria-hidden={!openState}
232-
aria-expanded={openState}
233238
{...omitSpacingProps(rest)}
234239
>
235240
<HeightAnimation open={openState} keepInDOM={keepInDOM}>

packages/dnb-eufemia/src/components/list/ItemAction.tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,15 @@ function ItemAction(props: ItemActionProps) {
7676

7777
const anchorRef = useRef<HTMLAnchorElement>(null)
7878

79+
const handleLinkClick = useCallback(
80+
(event: React.MouseEvent<HTMLDivElement>) => {
81+
if (!isInactive) {
82+
onClick?.(event)
83+
}
84+
},
85+
[onClick, isInactive]
86+
)
87+
7988
const handleLinkKeyDown = useCallback(
8089
(event: React.KeyboardEvent<HTMLDivElement>) => {
8190
if (event.key === 'Enter' || event.key === ' ') {
@@ -113,12 +122,13 @@ function ItemAction(props: ItemActionProps) {
113122
role="link"
114123
tabIndex={isInactive ? -1 : 0}
115124
aria-disabled={isInactive ? true : undefined}
125+
onClick={handleLinkClick}
116126
onKeyDown={handleLinkKeyDown}
117127
variant={variant}
118128
selected={selected}
119129
skeleton={skeleton}
120130
pending={pending}
121-
disabled={disabled}
131+
disabled={appliedDisabled}
122132
{...rest}
123133
>
124134
<Anchor
@@ -148,7 +158,7 @@ function ItemAction(props: ItemActionProps) {
148158
selected={selected}
149159
skeleton={skeleton}
150160
pending={pending}
151-
disabled={disabled}
161+
disabled={appliedDisabled}
152162
{...rest}
153163
>
154164
{content}

packages/dnb-eufemia/src/components/list/ItemTitle.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,16 @@ import Context from '../../shared/Context'
1515
export type ItemTitleProps = FlexItemProps & {
1616
/** Font size of the title content. Defaults to `basis`. */
1717
fontSize?: 'small' | 'basis'
18+
/** Font weight of the title content. Defaults to `regular`. */
19+
fontWeight?: 'regular' | 'medium'
1820
/** If `true`, applies skeleton loading state. Inherits from parent List context when not set. */
1921
skeleton?: SkeletonShow
2022
}
2123

2224
function ItemTitleBase({
2325
className,
2426
fontSize = 'basis',
27+
fontWeight,
2528
skeleton,
2629
children,
2730
...rest
@@ -40,7 +43,14 @@ function ItemTitleBase({
4043
)}
4144
{...rest}
4245
>
43-
<span className={`dnb-t__size--${fontSize}`}>{children}</span>
46+
<span
47+
className={classnames(
48+
`dnb-t__size--${fontSize}`,
49+
fontWeight === 'medium' && 'dnb-t__weight--medium'
50+
)}
51+
>
52+
{children}
53+
</span>
4454
</FlexItem>
4555
)
4656

packages/dnb-eufemia/src/components/list/ListDocs.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -285,8 +285,8 @@ export const ItemAccordionHeaderProperties: PropertiesTableProps = {
285285
type: 'React.ReactNode',
286286
status: 'optional',
287287
},
288-
'[List.Item.Basic](/uilib/components/list/properties/#listitembasic)': {
289-
doc: 'Inherits List.Item.Basic properties (variant, pending, spacing, etc.).',
288+
'[Flex.Item](/uilib/layout/flex/item/properties)': {
289+
doc: 'Renders as a `Flex.Item`. Supports all `Flex.Item` properties.',
290290
type: 'Various',
291291
status: 'optional',
292292
},
@@ -387,6 +387,14 @@ export const ItemActionEvents: PropertiesTableProps = {
387387
},
388388
}
389389

390+
export const ItemAccordionEvents: PropertiesTableProps = {
391+
onClick: {
392+
doc: 'Fired when the user clicks or activates the accordion header (click or Enter/Space key). In controlled mode (when `open` is provided), use this to update the `open` prop. Receives the native event.',
393+
type: '(event) => void',
394+
status: 'optional',
395+
},
396+
}
397+
390398
export const CardProperties: PropertiesTableProps = {
391399
children: {
392400
doc: 'Card content. Typically a `List.Container` (optionally wrapped in `List.ScrollView`). The card provides a visual container with border-radius resets and scrollbar integration for the list inside it.',

packages/dnb-eufemia/src/components/list/__tests__/ItemAccordion.test.tsx

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ describe('ItemAccordion', () => {
221221
expect(header.getAttribute('tabindex')).toBe('-1')
222222
})
223223

224-
it('content region has id, aria-labelledby, aria-hidden and aria-expanded', () => {
224+
it('content region has id, aria-labelledby and aria-hidden', () => {
225225
render(
226226
<ItemAccordion>
227227
<ItemAccordion.Header>Title</ItemAccordion.Header>
@@ -240,10 +240,10 @@ describe('ItemAccordion', () => {
240240
expect(contentRegion.getAttribute('id')).toBe(contentId)
241241
expect(contentRegion.getAttribute('aria-labelledby')).toBe(headerId)
242242
expect(contentRegion.getAttribute('aria-hidden')).toBe('true')
243-
expect(contentRegion.getAttribute('aria-expanded')).toBe('false')
243+
expect(contentRegion).not.toHaveAttribute('aria-expanded')
244244
})
245245

246-
it('content region has aria-hidden false and aria-expanded true when open', () => {
246+
it('content region has aria-hidden false when open and no aria-expanded', () => {
247247
render(
248248
<ItemAccordion open>
249249
<ItemAccordion.Header>Title</ItemAccordion.Header>
@@ -259,7 +259,7 @@ describe('ItemAccordion', () => {
259259

260260
expect(contentRegion).toBeInTheDocument()
261261
expect(contentRegion.getAttribute('aria-hidden')).toBe('false')
262-
expect(contentRegion.getAttribute('aria-expanded')).toBe('true')
262+
expect(contentRegion).not.toHaveAttribute('aria-expanded')
263263
})
264264

265265
describe('keepInDOM', () => {
@@ -812,4 +812,50 @@ describe('ItemAccordion', () => {
812812
const consumer = document.querySelector('[data-skeleton]')
813813
expect(consumer.getAttribute('data-skeleton')).toBe('true')
814814
})
815+
816+
it('warns and returns null when AccordionHeader is used outside ItemAccordion', () => {
817+
const spy = jest.spyOn(console, 'log').mockImplementation(() => {})
818+
819+
render(<ItemAccordion.Header>Orphan header</ItemAccordion.Header>)
820+
821+
const header = document.querySelector(
822+
'.dnb-list__item__accordion__header'
823+
)
824+
expect(header).not.toBeInTheDocument()
825+
826+
const didWarn = spy.mock.calls.some((call) =>
827+
call
828+
.map((entry) => String(entry))
829+
.join(' ')
830+
.includes(
831+
'List.Item.Accordion.Header should be used inside List.Item.Accordion.'
832+
)
833+
)
834+
expect(didWarn).toBe(true)
835+
836+
spy.mockRestore()
837+
})
838+
839+
it('warns and returns null when AccordionContent is used outside ItemAccordion', () => {
840+
const spy = jest.spyOn(console, 'log').mockImplementation(() => {})
841+
842+
render(<ItemAccordion.Content>Orphan content</ItemAccordion.Content>)
843+
844+
const content = document.querySelector(
845+
'.dnb-list__item__accordion__content'
846+
)
847+
expect(content).not.toBeInTheDocument()
848+
849+
const didWarn = spy.mock.calls.some((call) =>
850+
call
851+
.map((entry) => String(entry))
852+
.join(' ')
853+
.includes(
854+
'List.Item.Accordion.Content should be used inside List.Item.Accordion.'
855+
)
856+
)
857+
expect(didWarn).toBe(true)
858+
859+
spy.mockRestore()
860+
})
815861
})

0 commit comments

Comments
 (0)