UNPKG

@kiwicom/orbit-components

Version:

Orbit-components is a React component library which provides developers with the easiest possible way of building Kiwi.com's products.

169 lines (168 loc) 6.38 kB
"use client"; import * as React from "react"; import cx from "clsx"; import Stack from "../Stack"; import useMediaQuery from "../hooks/useMediaQuery"; import { TimelineStatusProvider, TimelineStepProvider } from "./TimelineContext"; import { spaceAfterClasses } from "../common/tailwind"; /** * @orbit-doc-start * README * ---------- * # Timeline * * To implement the `Timeline` component into your project you'll need to add the import: * * ```jsx * import Timeline, { TimelineStep } from "@kiwicom/orbit-components/lib/Timeline"; * ``` * * After adding import to your project you can use it simply like: * * ```jsx * <Timeline> * <TimelineStep label="In Progress" type="success"> * We'll wait for the carrier(s) to send us the refund and contact them again if necessary. * </TimelineStep> * </Timeline> * ``` * * ## Props * * The table below contains all types of props available in the **Timeline** component. * * | Name | Type | Default | Description | * | :----------- | :------------------ | :------ | :---------------------------------------------------------------------------------------- | * | **children** | `React.Node` | | List of [`TimelineStep`](#TimelineStep) components. | * | dataTest | `string` | | Optional prop for testing purposes. | * | id | `string` | | Set `id` for `Timeline`. | * | spaceAfter | [`enum`](#enum) | | Additional `margin-bottom` after component. | * | direction | `"column" \| "row"` | | Allows to set direction, by default on desktop is `row` and on mobile is set to `column`. | * * ### enum * * | spaceAfter | * | :----------- | * | `"none"` | * | `"smallest"` | * | `"small"` | * | `"normal"` | * | `"medium"` | * | `"large"` | * | `"largest"` | * * ## Subcomponents * * ### TimelineStep * * ```jsx * import TimelineStep from "@kiwicom/orbit-components/lib/Timeline/TimelineStep"; * ``` * * #### Props * * The table below contains all types of the props in **TimelineStep** component. * * | Name | Type | Default | Description | * | :----------- | :-------------- | :------ | :------------------------------------------ | * | **children** | `React.Node` | | Optional. The content of the `TimelineStep` | * | label | `React.Node` | | Text for `label` component inside. | * | subLabel | `React.Node` | | Text for `subLabel` component inside. | * | type | [`enum`](#enum) | | Type of current process step. | * | active | `boolean` | | Controlled state of the step. | * | spaceAfter | [`enum`](#enum) | | Additional `margin-bottom` after component. | * * #### enum * * | type | * | :--------- | * | "success" | * | "warning" | * | "critical" | * | "info" | * * * Accessibility * ------------- * ## Accessibility * * The Timeline component has been designed with accessibility in mind, providing visual progress indication that communicates the current state to screen readers. * * ### Accessibility Props * * **TimelineStep props:** * * | Name | Type | Description | * | :----- | :-------- | :---------------------------------------------------------------------------------- | * | active | `boolean` | Controls whether the step is marked as the current step with `aria-current="step"`. | * * ### Automatic Accessibility Features * * - **aria-current="step"**: Automatically applied to active timeline steps based on the `active` prop * - **aria-hidden**: Applied to decorative visual elements (icons, progress lines) that don't convey meaning to screen readers * - Content is announced in document order, providing logical progression through the timeline * * ### Best Practices * * - Use descriptive labels that clearly indicate the purpose of each timeline step * - Only mark one step as active at a time to avoid confusion for screen reader users * - Ensure timeline content follows a logical sequence that makes sense when read linearly * * ### Examples * * #### Basic Timeline with Active Step * * ```jsx * <Timeline> * <TimelineStep label="Processing check-in" type="success" /> * <TimelineStep label="Boarding pass ready" type="info" active> * You can now print your boarding pass. * </TimelineStep> * </Timeline> * ``` * * Screen reader announces: "Processing check-in. Boarding pass ready, current step. You can now print your boarding pass." * * * @orbit-doc-end */ const Timeline = ({ children, spaceAfter, direction, dataTest, id }) => { const childrenArr = React.Children.toArray(children); const { isDesktop } = useMediaQuery(); const getDirection = () => { if (direction) return direction; return isDesktop ? "row" : "column"; }; const hasSubLabelMargin = React.useMemo(() => childrenArr.some(child => /*#__PURE__*/React.isValidElement(child) && child.props.subLabel), [childrenArr]); return childrenArr && childrenArr.length > 0 ? /*#__PURE__*/React.createElement("div", { className: cx("orbit-timeline relative overflow-hidden", spaceAfter && spaceAfterClasses[spaceAfter]), "data-test": dataTest, id: id }, /*#__PURE__*/React.createElement(Stack, { flex: true, shrink: true, direction: getDirection(), as: "ol" }, /*#__PURE__*/React.createElement(TimelineStatusProvider, { direction: direction }, React.Children.map(childrenArr, (child, i) => { if ( /*#__PURE__*/React.isValidElement(child)) { return /*#__PURE__*/React.createElement(TimelineStepProvider, { index: i, last: i + 1 === childrenArr.length, hasSubLabelMargin: hasSubLabelMargin }, child); } return null; })))) : null; }; export default Timeline; export { default as TimelineStep } from "./TimelineStep";