diff --git a/packages/dnb-design-system-portal/src/docs/uilib/components/timeline/Examples.js b/packages/dnb-design-system-portal/src/docs/uilib/components/timeline/Examples.js deleted file mode 100644 index 2b7b7275aea..00000000000 --- a/packages/dnb-design-system-portal/src/docs/uilib/components/timeline/Examples.js +++ /dev/null @@ -1,392 +0,0 @@ -/** - * UI lib Component Example - * - */ - -import ComponentBox from 'dnb-design-system-portal/src/shared/tags/ComponentBox' -import { - card as Card, - account_card as AccountCard, - confetti as Confetti, -} from '@dnb/eufemia/src/icons' - -export const TimelineSingleCompleted = () => ( - - { - /* jsx */ ` - -` - } - -) - -export const TimelineSingleCurrent = () => ( - - { - /* jsx */ ` - -` - } - -) - -export const TimelineSingleUpcoming = () => ( - - { - /* jsx */ ` - -` - } - -) - -export const TimelineMultipleData = () => ( - - { - /* jsx */ ` -() => { - const events = [ - { - title: "Completed event", - subtitle: "10. september 2021", - state: "completed" - }, - { - title: "Current event", - infoMessage: "Additional information about this step if needed.", - state: "current", - }, - { - title: "Upcoming event", - state: "upcoming", - }, - ]; - - return ( - - ) -} -` - } - -) - -export const TimelineMultipleCompletedData = () => ( - - { - /* jsx */ ` -() => { - const events = [ - { - title: "Completed event#1", - infoMessage: "Additional information about this step if needed.", - subtitle: "10. september 2021", - state: "completed" - }, - { - title: "Completed event#2", - infoMessage: "Additional information about this step if needed.", - state: "completed" - }, - { - title: "Completed event#3", - subtitle: "10. september 2021", - state: "completed" - }, - ]; - - return ( - - ) -} -` - } - -) - -export const TimelineMultipleUpcomingData = () => ( - - { - /* jsx */ ` -() => { - const events = [ - { - title: "Upcoming event#1", - infoMessage: "Additional information about this step if needed.", - subtitle: "10. september 2021", - state: "upcoming" - }, - { - title: "Upcoming event#2", - infoMessage: "Additional information about this step if needed.", - state: "upcoming" - }, - { - title: "Upcoming event#3", - subtitle: "10. september 2021", - state: "upcoming" - }, - ]; - - return ( - - ) -} -` - } - -) - -export const TimelineMultipleCurrentData = () => ( - - { - /* jsx */ ` -() => { - const events = [ - { - title: "Current event#1", - infoMessage: "Additional information about this step if needed.", - subtitle: "10. september 2021", - state: "current" - }, - { - title: "Current event#2", - infoMessage: "Additional information about this step if needed.", - state: "current" - }, - { - title: "Current event#3", - subtitle: "10. september 2021", - state: "current" - }, - ]; - - return ( - - ) -} -` - } - -) - -export const TimelineMultiple = () => ( - - { - /* jsx */ ` - - - - - -` - } - -) - -export const TimelineStates = () => ( - - { - /* jsx */ ` -() => { - const events = [ - { - title: "Completed event", - subtitle: "10. september 2021", - infoMessage: "Additional information about this step if needed.", - state: "completed" - }, - { - title: "Current event", - subtitle: "10. september 2021", - infoMessage: "Additional information about this step if needed.", - state: "current" - }, - { - title: "Upcoming event", - subtitle: "10. september 2021", - infoMessage: "Additional information about this step if needed.", - state: "upcoming" - }, - ]; - - return ( - - ) -} -` - } - -) - -export const TimelineIcons = () => ( - - { - /* jsx */ ` -() => { - const events = [ - { - title: "Completed event", - state: "completed", - icon: Confetti, - iconAlt: "Celebration" - }, - { - title: "Current event", - state: "current", - icon: Card, - iconAlt: "Bank card" - }, - { - title: "Upcoming event", - state: "upcoming", - icon: AccountCard, - iconAlt: "Money bag & card" - }, - ]; - - return ( - - ) -} -` - } - -) - -export const TimelineSkeleton = () => ( - - { - /* jsx */ ` - -` - } - -) - -export const TimelineAsChildrenSkeleton = () => ( - - { - /* jsx */ ` - - - - - -` - } - -) - -export const TimelineItemSkeleton = () => ( - - { - /* jsx */ ` - -` - } - -) diff --git a/packages/dnb-design-system-portal/src/docs/uilib/components/timeline/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/components/timeline/Examples.tsx new file mode 100644 index 00000000000..042d7c02866 --- /dev/null +++ b/packages/dnb-design-system-portal/src/docs/uilib/components/timeline/Examples.tsx @@ -0,0 +1,327 @@ +/** + * UI lib Component Example + * + */ + +import ComponentBox from 'dnb-design-system-portal/src/shared/tags/ComponentBox' +import { + card as Card, + account_card as AccountCard, + confetti as Confetti, +} from '@dnb/eufemia/src/icons' +import Timeline from '@dnb/eufemia/src/components/timeline/Timeline' + +export const TimelineSingleCompleted = () => ( + + + +) + +export const TimelineSingleCurrent = () => ( + + + +) + +export const TimelineSingleUpcoming = () => ( + + + +) + +export const TimelineMultipleData = () => ( + + {() => { + return ( + + ) + }} + +) + +export const TimelineMultipleCompletedData = () => ( + + {() => { + return ( + + ) + }} + +) + +export const TimelineMultipleUpcomingData = () => ( + + {() => { + return ( + + ) + }} + +) + +export const TimelineMultipleCurrentData = () => ( + + {() => { + return ( + + ) + }} + +) + +export const TimelineMultiple = () => ( + + + + + + + +) + +export const TimelineStates = () => ( + + {() => { + return ( + + ) + }} + +) + +export const TimelineIcons = () => ( + + {() => { + return ( + + ) + }} + +) + +export const TimelineSkeleton = () => ( + + + +) + +export const TimelineAsChildrenSkeleton = () => ( + + + + + + + +) + +export const TimelineItemSkeleton = () => ( + + + +) diff --git a/packages/dnb-eufemia/src/components/timeline/Timeline.tsx b/packages/dnb-eufemia/src/components/timeline/Timeline.tsx index 282486e0e79..6e41088ccda 100644 --- a/packages/dnb-eufemia/src/components/timeline/Timeline.tsx +++ b/packages/dnb-eufemia/src/components/timeline/Timeline.tsx @@ -16,13 +16,7 @@ import { extendPropsWithContext, } from '../../shared/component-helper' -export interface TimelineProps { - /** - * Custom className on the component root - * Default: null - */ - className?: string - +export type TimelineProps = { /** * Skeleton should be applied when loading content * Default: null @@ -44,6 +38,10 @@ export interface TimelineProps { | React.ReactElement } +export type TimelineAllProps = TimelineProps & + Omit, 'type' | 'data'> & + SpacingProps + export const defaultProps = { className: null, skeleton: false, @@ -51,7 +49,7 @@ export const defaultProps = { children: null, } -const Timeline = (localProps: TimelineProps & SpacingProps) => { +const Timeline = (localProps: TimelineAllProps) => { // Every component should have a context const context = React.useContext(Context) @@ -88,8 +86,13 @@ const Timeline = (localProps: TimelineProps & SpacingProps) => { validateDOMAttributes(allProps, props) return ( -
@@ -98,7 +101,7 @@ const Timeline = (localProps: TimelineProps & SpacingProps) => { ))} {children} -
+ ) } diff --git a/packages/dnb-eufemia/src/components/timeline/TimelineItem.tsx b/packages/dnb-eufemia/src/components/timeline/TimelineItem.tsx index 1efb1c28347..905acf9ccea 100644 --- a/packages/dnb-eufemia/src/components/timeline/TimelineItem.tsx +++ b/packages/dnb-eufemia/src/components/timeline/TimelineItem.tsx @@ -16,7 +16,7 @@ import Context from '../../shared/Context' import { SkeletonShow } from '../skeleton/Skeleton' import { extendPropsWithContext } from '../../shared/component-helper' -export interface TimelineItemProps { +export type TimelineItemProps = { /** * Icon displaying on the left side. * Default: `check` for state `completed`, `pin` for state `current`, and `calendar` for state `upcoming` . @@ -57,6 +57,9 @@ export interface TimelineItemProps { skeleton?: SkeletonShow } +export type TimelineItemAllProps = TimelineItemProps & + Omit, 'title' | 'name'> + const defaultProps = { icon: null, iconAlt: null, @@ -67,7 +70,7 @@ const defaultProps = { skeleton: false, } -const TimelineItem = (localProps: TimelineItemProps) => { +const TimelineItem = (localProps: TimelineItemAllProps) => { // Every component should have a context const context = React.useContext(Context) const { @@ -205,7 +208,7 @@ const TimelineItem = (localProps: TimelineItemProps) => { } return ( -
{ > -
+ ) } diff --git a/packages/dnb-eufemia/src/components/timeline/__tests__/Timeline.test.tsx b/packages/dnb-eufemia/src/components/timeline/__tests__/Timeline.test.tsx index 2ed40d2c165..36996884afc 100644 --- a/packages/dnb-eufemia/src/components/timeline/__tests__/Timeline.test.tsx +++ b/packages/dnb-eufemia/src/components/timeline/__tests__/Timeline.test.tsx @@ -43,7 +43,7 @@ describe('Timeline', () => { expect(screen.queryAllByTestId('timeline-item')).toHaveLength(3) }) - it('current will have aria-current="step', () => { + it('current will have aria-current="step"', () => { render( { expect(lastElem.getAttribute('aria-current')).toBe('step') }) + it('uses ordered list semantic elements', () => { + render( + + ) + + const element = document.querySelector('.dnb-timeline') + const firstChild = element.firstChild as HTMLLIElement + const lastChild = element.lastChild as HTMLLIElement + + expect(element.tagName).toBe('OL') + expect(firstChild.tagName).toBe('LI') + expect(lastChild.tagName).toBe('LI') + }) + it('inherits skeleton prop from provider', () => { const skeletonClassName = 'dnb-skeleton' @@ -85,13 +108,13 @@ describe('Timeline', () => { it('should support spacing props', () => { render( ) @@ -103,10 +126,31 @@ describe('Timeline', () => { expect(attributes).toEqual(['class', 'data-testid']) expect(Array.from(element.classList)).toEqual([ 'dnb-timeline', + 'dnb-space__reset', 'dnb-space__top--large', ]) }) + it('should support extra attributes', () => { + render( + + ) + + const element = document.querySelector('.dnb-timeline') + + expect(element.getAttribute('aria-label')).toBe('extra-label') + }) + describe('TimelineItem', () => { it('renders title', () => { const title = 'Completed' diff --git a/packages/dnb-eufemia/src/components/timeline/__tests__/__snapshots__/Timeline.test.tsx.snap b/packages/dnb-eufemia/src/components/timeline/__tests__/__snapshots__/Timeline.test.tsx.snap index d6bc55bd571..df588849fbb 100644 --- a/packages/dnb-eufemia/src/components/timeline/__tests__/__snapshots__/Timeline.test.tsx.snap +++ b/packages/dnb-eufemia/src/components/timeline/__tests__/__snapshots__/Timeline.test.tsx.snap @@ -12,7 +12,7 @@ exports[`Timeline scss have to match default theme snapshot 1`] = ` * Button mixins * */ -:root { +.dnb-timeline { --timeline-icon-height--small: var(--button-height--small); --timeline-icon-width--small: var(--button-width--small); --timeline-icon-border-radius--small: calc( @@ -34,7 +34,6 @@ exports[`Timeline scss have to match default theme snapshot 1`] = ` var(--timeline-icon-width-diff) + var(--timeline-border-spacing) ); } - .dnb-timeline__item { margin-left: var(--timeline-icon-width-diff); } @@ -143,19 +142,21 @@ exports[`Timeline scss have to match snapshot 1`] = ` .dnb-timeline { display: flex; flex-direction: column; + padding: 0; + list-style: none; } .dnb-timeline__item__label { display: flex; align-items: center; - text-align: left; padding: 0; + text-align: left; } .dnb-timeline__item__label__icon { display: flex; flex-shrink: 0; align-items: center; - height: auto; justify-content: center; + height: auto; padding: 0; } .dnb-timeline__item__label__title { diff --git a/packages/dnb-eufemia/src/components/timeline/style/dnb-timeline.scss b/packages/dnb-eufemia/src/components/timeline/style/dnb-timeline.scss index 8289712b2dc..0906c918654 100644 --- a/packages/dnb-eufemia/src/components/timeline/style/dnb-timeline.scss +++ b/packages/dnb-eufemia/src/components/timeline/style/dnb-timeline.scss @@ -8,28 +8,37 @@ .dnb-timeline { display: flex; flex-direction: column; + padding: 0; + list-style: none; + &__item { &__label { display: flex; align-items: center; - text-align: left; padding: 0; + + text-align: left; + &__icon { display: flex; flex-shrink: 0; align-items: center; - height: auto; justify-content: center; + + height: auto; padding: 0; } &__title { cursor: text; } } + &__content { padding-bottom: var(--spacing-small); + &__subtitle { cursor: text; + font-size: var(--font-size-x-small); font-weight: var(--font-weight-basis); color: var(--color-black-55); diff --git a/packages/dnb-eufemia/src/components/timeline/style/themes/dnb-timeline-theme-ui.scss b/packages/dnb-eufemia/src/components/timeline/style/themes/dnb-timeline-theme-ui.scss index ac64f9675eb..afb52a13840 100644 --- a/packages/dnb-eufemia/src/components/timeline/style/themes/dnb-timeline-theme-ui.scss +++ b/packages/dnb-eufemia/src/components/timeline/style/themes/dnb-timeline-theme-ui.scss @@ -6,7 +6,15 @@ @import '../../../../style/core/utilities.scss'; @import '../../../button/style/themes/button-mixins.scss'; -:root { +@mixin centerBorder( + $width: var(--timeline-icon-width--medium), + $spacing: var(--timeline-border-spacing--icon-adjusted) +) { + margin-left: calc(#{$width} / 2); + padding-left: calc(#{$width} / 2 + #{$spacing}); +} + +.dnb-timeline { --timeline-icon-height--small: var(--button-height--small); --timeline-icon-width--small: var(--button-width--small); --timeline-icon-border-radius--small: calc( @@ -27,17 +35,7 @@ --timeline-border-spacing--icon-adjusted: calc( var(--timeline-icon-width-diff) + var(--timeline-border-spacing) ); -} -@mixin centerBorder( - $width: var(--timeline-icon-width--medium), - $spacing: var(--timeline-border-spacing--icon-adjusted) -) { - margin-left: calc(#{$width} / 2); - padding-left: calc(#{$width} / 2 + #{$spacing}); -} - -.dnb-timeline { &__item { margin-left: var(--timeline-icon-width-diff); &__label {