-
-
Notifications
You must be signed in to change notification settings - Fork 112
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: improve ActivityTimeline
and TimelineMarker
components
#2100
Changes from 3 commits
9f7a30d
0087339
fe0e19f
b7ec076
9c00423
81a2004
2986bfd
d7372d2
0b99d2b
3823009
937bd42
e86e4ff
4476552
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import React from 'react'; | ||
|
||
export const ActivityTimelineContext = React.createContext(); | ||
export const { Provider, Consumer } = ActivityTimelineContext; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
export default function getChildTimelineMarkersNodes(ref) { | ||
if (ref) { | ||
return ref.querySelectorAll('li[data-id="timeline-marker-li"]'); | ||
} | ||
return []; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
/* eslint-disable import/prefer-default-export */ | ||
export { default as getChildTimelineMarkersNodes } from './getChildTimelineMarkersNodes'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,13 @@ | ||
import { ReactNode } from 'react'; | ||
import { ReactNode, MouseEvent } from 'react'; | ||
import { BaseProps } from '../types'; | ||
|
||
type Names = string[] | string; | ||
|
||
export interface ActivityTimelineProps extends BaseProps { | ||
children?: ReactNode; | ||
multiple?: boolean; | ||
onToggleSection?: (event: MouseEvent<HTMLElement>, name: Names) => void; | ||
activeSectionNames?: Names; | ||
} | ||
|
||
export default function(props: ActivityTimelineProps): JSX.Element | null; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,100 @@ | ||
import React from 'react'; | ||
import React, { useRef, useState, useCallback, useMemo, useEffect } from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import isChildRegistered from '../InternalDropdown/helpers/isChildRegistered'; | ||
import insertChildOrderly from '../InternalDropdown/helpers/insertChildOrderly'; | ||
import { getChildTimelineMarkersNodes } from './helpers'; | ||
import { Provider } from './context'; | ||
import StyledUl from './styled/ul'; | ||
|
||
/** | ||
* The ActivityTimeline displays each of any item upcoming, current, and past activities. | ||
* @category Layout | ||
*/ | ||
export default function ActivityTimeline(props) { | ||
const { children, className, style } = props; | ||
const { | ||
wildergd marked this conversation as resolved.
Show resolved
Hide resolved
|
||
id, | ||
children, | ||
className, | ||
style, | ||
variant, | ||
multiple, | ||
activeSectionNames, | ||
onToggleSection, | ||
} = props; | ||
const registeredTimelineMarkers = useRef([]); | ||
const [activeNames, setActiveNames] = useState(activeSectionNames); | ||
const containerRef = useRef(); | ||
|
||
useEffect(() => { | ||
if ( | ||
activeSectionNames && | ||
activeSectionNames !== activeNames && | ||
typeof onToggleSection === 'function' | ||
) { | ||
setActiveNames(activeSectionNames); | ||
} | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
}, [activeSectionNames, onToggleSection]); | ||
|
||
const privateRegisterMarker = useCallback((stepRef, stepProps) => { | ||
if (isChildRegistered(stepProps.name, registeredTimelineMarkers.current)) return; | ||
const [...nodes] = getChildTimelineMarkersNodes(containerRef.current); | ||
const newStepsList = insertChildOrderly( | ||
registeredTimelineMarkers.current, | ||
{ | ||
ref: stepRef, | ||
...stepProps, | ||
}, | ||
nodes, | ||
); | ||
registeredTimelineMarkers.current = newStepsList; | ||
}, []); | ||
|
||
const privateUnregisterMarker = useCallback((stepRef, stepName) => { | ||
if (!isChildRegistered(stepName, registeredTimelineMarkers.current)) return; | ||
registeredTimelineMarkers.current = registeredTimelineMarkers.current.filter( | ||
step => step.name !== stepName, | ||
); | ||
}, []); | ||
|
||
const privateOnToggleMarker = useCallback( | ||
(event, name) => { | ||
if (typeof onToggleSection === 'function') { | ||
return onToggleSection(event, name); | ||
} | ||
return setActiveNames(name); | ||
}, | ||
[onToggleSection], | ||
); | ||
|
||
const context = useMemo(() => { | ||
return { | ||
activeNames, | ||
multiple, | ||
isVariantAccordion: variant === 'accordion', | ||
privateRegisterMarker, | ||
privateUnregisterMarker, | ||
privateOnToggleMarker, | ||
}; | ||
}, [ | ||
variant, | ||
activeNames, | ||
multiple, | ||
privateRegisterMarker, | ||
privateUnregisterMarker, | ||
privateOnToggleMarker, | ||
]); | ||
|
||
return ( | ||
<StyledUl className={className} style={style}> | ||
{children} | ||
<StyledUl id={id} className={className} style={style} ref={containerRef} variant={variant}> | ||
<Provider value={context}>{children}</Provider> | ||
</StyledUl> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think here, we should execute all this new code only if the variant is accordion:
|
||
); | ||
} | ||
|
||
ActivityTimeline.propTypes = { | ||
/** The id of the outer element. */ | ||
id: PropTypes.string, | ||
/** | ||
* This prop that should not be visible in the documentation. | ||
* @ignore | ||
|
@@ -26,10 +104,32 @@ ActivityTimeline.propTypes = { | |
className: PropTypes.string, | ||
/** An object with custom style applied to the outer element. */ | ||
style: PropTypes.object, | ||
/** If true, expands multiples TimelineMarkers. | ||
* This value defaults to false. */ | ||
multiple: PropTypes.bool, | ||
/** The variant changes the appearance of the timeline. Accepted variants include | ||
* default and accordion. */ | ||
variant: PropTypes.oneOf(['default', 'accordion']), | ||
/** It contain the name of the TimelineMarker that is expanded. | ||
* It is an array of string when multiple is true, | ||
* or a string when when multiple is false. | ||
* It must match the name of the TimelineMarker. */ | ||
activeSectionNames: PropTypes.oneOfType([ | ||
PropTypes.arrayOf(PropTypes.string), | ||
PropTypes.string, | ||
]), | ||
/** Action fired when a TimelineMarker is selected. | ||
* The event params include the `name` of the selected TimelineMarker. */ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we need to explain that this props (multiple, activeSectionNames and onToggleSection) are only used for accordion variant |
||
onToggleSection: PropTypes.func, | ||
}; | ||
|
||
ActivityTimeline.defaultProps = { | ||
id: undefined, | ||
children: null, | ||
className: undefined, | ||
style: undefined, | ||
variant: 'default', | ||
multiple: false, | ||
onToggleSection: undefined, | ||
activeSectionNames: undefined, | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here we will pass one single argument, a object with the name:
{ name }