@roadlittledawn/docs-design-system-react 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,14 +5,22 @@ interface CollapserProps {
5
5
  /** Optional ID for the title element */
6
6
  id?: string;
7
7
  /**
8
- * Whether the collapser should be open by default
8
+ * Whether the collapser should be open by default (uncontrolled)
9
9
  * @default false
10
10
  */
11
11
  defaultOpen?: boolean;
12
+ /**
13
+ * Controlled open state (used by CollapserGroup)
14
+ */
15
+ open?: boolean;
16
+ /**
17
+ * Callback when toggle is clicked (used by CollapserGroup)
18
+ */
19
+ onToggle?: () => void;
12
20
  /** Content to show/hide when toggling */
13
21
  children: ReactNode;
14
22
  /** Additional CSS classes */
15
23
  className?: string;
16
24
  }
17
- export declare function Collapser({ title, id, defaultOpen, children, className, }: CollapserProps): import("react/jsx-runtime").JSX.Element;
25
+ export declare function Collapser({ title, id, defaultOpen, open: controlledOpen, onToggle, children, className, }: CollapserProps): import("react/jsx-runtime").JSX.Element;
18
26
  export {};
@@ -2,19 +2,30 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useState, useRef, useEffect } from "react";
3
3
  import { useKeyPress } from "../hooks/useKeyPress";
4
4
  export function Collapser(_a) {
5
- var title = _a.title, id = _a.id, _b = _a.defaultOpen, defaultOpen = _b === void 0 ? false : _b, children = _a.children, _c = _a.className, className = _c === void 0 ? "" : _c;
6
- var _d = useState(defaultOpen), isOpen = _d[0], setIsOpen = _d[1];
5
+ var title = _a.title, id = _a.id, _b = _a.defaultOpen, defaultOpen = _b === void 0 ? false : _b, controlledOpen = _a.open, onToggle = _a.onToggle, children = _a.children, _c = _a.className, className = _c === void 0 ? "" : _c;
6
+ var _d = useState(defaultOpen), uncontrolledOpen = _d[0], setUncontrolledOpen = _d[1];
7
+ var isControlled = controlledOpen !== undefined;
8
+ var isOpen = isControlled ? controlledOpen : uncontrolledOpen;
7
9
  var _e = useState(undefined), height = _e[0], setHeight = _e[1];
8
10
  var contentRef = useRef(null);
9
11
  // Keyboard shortcuts: 's' or 'f' to show, 'h' to hide
10
- useKeyPress(['s', 'f', 'h'], function (e) { return setIsOpen(e.key !== 'h'); });
12
+ useKeyPress(['s', 'f', 'h'], function (e) {
13
+ if (!isControlled) {
14
+ setUncontrolledOpen(e.key !== 'h');
15
+ }
16
+ });
11
17
  useEffect(function () {
12
18
  if (contentRef.current) {
13
19
  setHeight(contentRef.current.scrollHeight);
14
20
  }
15
21
  }, [children]);
16
22
  var toggleOpen = function () {
17
- setIsOpen(!isOpen);
23
+ if (onToggle) {
24
+ onToggle();
25
+ }
26
+ else {
27
+ setUncontrolledOpen(!uncontrolledOpen);
28
+ }
18
29
  };
19
30
  var collapserClasses = ["dds-collapser", className]
20
31
  .filter(Boolean)
@@ -23,6 +23,6 @@ export declare const WithID: Story;
23
23
  */
24
24
  export declare const ComplexContent: Story;
25
25
  /**
26
- * Multiple collapsers in a FAQ-style layout.
26
+ * Multiple collapsers in a FAQ-style layout using CollapserGroup.
27
27
  */
28
28
  export declare const FAQExample: Story;
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Collapser } from './Collapser';
3
+ import { CollapserGroup } from './CollapserGroup';
3
4
  /**
4
5
  * The Collapser component creates expandable/collapsible content sections with smooth animations.
5
6
  */
@@ -55,8 +56,8 @@ export var ComplexContent = {
55
56
  },
56
57
  };
57
58
  /**
58
- * Multiple collapsers in a FAQ-style layout.
59
+ * Multiple collapsers in a FAQ-style layout using CollapserGroup.
59
60
  */
60
61
  export var FAQExample = {
61
- render: function () { return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: '0.5rem' }, children: [_jsx(Collapser, { title: "What is this documentation system?", children: _jsx("p", { children: "This is a comprehensive documentation design system that provides reusable components and guidelines for creating effective technical documentation." }) }), _jsx(Collapser, { title: "How do I get started?", children: _jsx("p", { children: "Start by installing the component package, then explore the components in Storybook to understand their usage and configuration options." }) }), _jsx(Collapser, { title: "Can I customize the components?", children: _jsx("p", { children: "Yes! All components accept a className prop for custom styling, and you can override the default styles using CSS." }) }), _jsx(Collapser, { title: "Is this accessible?", children: _jsx("p", { children: "Accessibility is a core principle. All components follow WAI-ARIA best practices and are keyboard navigable." }) })] })); },
62
+ render: function () { return (_jsxs(CollapserGroup, { children: [_jsx(Collapser, { title: "What is this documentation system?", children: _jsx("p", { children: "This is a comprehensive documentation design system that provides reusable components and guidelines for creating effective technical documentation." }) }), _jsx(Collapser, { title: "How do I get started?", children: _jsx("p", { children: "Start by installing the component package, then explore the components in Storybook to understand their usage and configuration options." }) }), _jsx(Collapser, { title: "Can I customize the components?", children: _jsx("p", { children: "Yes! All components accept a className prop for custom styling, and you can override the default styles using CSS." }) }), _jsx(Collapser, { title: "Is this accessible?", children: _jsx("p", { children: "Accessibility is a core principle. All components follow WAI-ARIA best practices and are keyboard navigable." }) })] })); },
62
63
  };
@@ -0,0 +1,15 @@
1
+ import React from 'react';
2
+ export interface CollapserGroupProps {
3
+ children: React.ReactNode;
4
+ /** Spacing between collapsers (CSS gap value) */
5
+ spacing?: string;
6
+ /** Allow multiple collapsers to be open simultaneously */
7
+ allowMultiple?: boolean;
8
+ /** Index(es) of collapser(s) that should be open by default */
9
+ defaultOpen?: number | number[];
10
+ /** Callback when collapser open state changes */
11
+ onChange?: (openIndexes: number[]) => void;
12
+ /** Additional CSS classes */
13
+ className?: string;
14
+ }
15
+ export declare const CollapserGroup: React.FC<CollapserGroupProps>;
@@ -0,0 +1,56 @@
1
+ var __assign = (this && this.__assign) || function () {
2
+ __assign = Object.assign || function(t) {
3
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
4
+ s = arguments[i];
5
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
6
+ t[p] = s[p];
7
+ }
8
+ return t;
9
+ };
10
+ return __assign.apply(this, arguments);
11
+ };
12
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
13
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
14
+ if (ar || !(i in from)) {
15
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
16
+ ar[i] = from[i];
17
+ }
18
+ }
19
+ return to.concat(ar || Array.prototype.slice.call(from));
20
+ };
21
+ import { jsx as _jsx } from "react/jsx-runtime";
22
+ import { useState, Children, cloneElement, isValidElement } from 'react';
23
+ import { Collapser } from './Collapser';
24
+ export var CollapserGroup = function (_a) {
25
+ var children = _a.children, _b = _a.spacing, spacing = _b === void 0 ? '0.5rem' : _b, _c = _a.allowMultiple, allowMultiple = _c === void 0 ? true : _c, defaultOpen = _a.defaultOpen, onChange = _a.onChange, _d = _a.className, className = _d === void 0 ? '' : _d;
26
+ var _e = useState(function () {
27
+ if (defaultOpen === undefined)
28
+ return [];
29
+ return Array.isArray(defaultOpen) ? defaultOpen : [defaultOpen];
30
+ }), openIndexes = _e[0], setOpenIndexes = _e[1];
31
+ var handleToggle = function (index) {
32
+ setOpenIndexes(function (prev) {
33
+ var next;
34
+ if (allowMultiple) {
35
+ next = prev.includes(index)
36
+ ? prev.filter(function (i) { return i !== index; })
37
+ : __spreadArray(__spreadArray([], prev, true), [index], false);
38
+ }
39
+ else {
40
+ next = prev.includes(index) ? [] : [index];
41
+ }
42
+ onChange === null || onChange === void 0 ? void 0 : onChange(next);
43
+ return next;
44
+ });
45
+ };
46
+ return (_jsx("div", { className: "dds-collapser-group ".concat(className).trim(), style: { gap: spacing }, children: Children.map(children, function (child, index) {
47
+ if (!isValidElement(child))
48
+ return child;
49
+ // Only inject props if child is a Collapser component
50
+ if (child.type === Collapser) {
51
+ return cloneElement(child, __assign(__assign({}, child.props), { open: openIndexes.includes(index), onToggle: function () { return handleToggle(index); } }));
52
+ }
53
+ return child;
54
+ }) }));
55
+ };
56
+ CollapserGroup.displayName = 'CollapserGroup';
@@ -0,0 +1,29 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { CollapserGroup } from './CollapserGroup';
3
+ /**
4
+ * CollapserGroup manages multiple Collapser components with consistent spacing
5
+ * and optional accordion behavior.
6
+ */
7
+ declare const meta: Meta<typeof CollapserGroup>;
8
+ export default meta;
9
+ type Story = StoryObj<typeof CollapserGroup>;
10
+ /**
11
+ * Basic group with default spacing, allowing multiple collapsers to be open.
12
+ */
13
+ export declare const Basic: Story;
14
+ /**
15
+ * Accordion mode - only one collapser can be open at a time.
16
+ */
17
+ export declare const AccordionMode: Story;
18
+ /**
19
+ * Custom spacing between collapsers.
20
+ */
21
+ export declare const CustomSpacing: Story;
22
+ /**
23
+ * One collapser open by default.
24
+ */
25
+ export declare const DefaultOpen: Story;
26
+ /**
27
+ * Multiple collapsers open by default.
28
+ */
29
+ export declare const MultipleDefaultOpen: Story;
@@ -0,0 +1,77 @@
1
+ var __assign = (this && this.__assign) || function () {
2
+ __assign = Object.assign || function(t) {
3
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
4
+ s = arguments[i];
5
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
6
+ t[p] = s[p];
7
+ }
8
+ return t;
9
+ };
10
+ return __assign.apply(this, arguments);
11
+ };
12
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
13
+ import { CollapserGroup } from './CollapserGroup';
14
+ import { Collapser } from './Collapser';
15
+ /**
16
+ * CollapserGroup manages multiple Collapser components with consistent spacing
17
+ * and optional accordion behavior.
18
+ */
19
+ var meta = {
20
+ title: 'Components/CollapserGroup',
21
+ component: CollapserGroup,
22
+ tags: ['autodocs'],
23
+ parameters: {
24
+ docs: {
25
+ description: {
26
+ component: "\nCollapserGroup provides a container for multiple Collapser components with built-in spacing and accordion functionality.\n\n## When to Use\n\n- FAQ sections with multiple questions\n- Accordion-style navigation or content sections\n- Any grouped collapsible content that needs consistent spacing\n- When you want only one section open at a time (accordion mode)\n\n## When Not to Use\n\n- For a single Collapser (use Collapser directly)\n- When collapsers need independent styling or spacing\n\n## Accessibility\n\n- Maintains all Collapser accessibility features\n- Keyboard navigation works within the group\n- Accordion mode provides clear single-selection behavior\n ",
27
+ },
28
+ },
29
+ },
30
+ };
31
+ export default meta;
32
+ /**
33
+ * Basic group with default spacing, allowing multiple collapsers to be open.
34
+ */
35
+ export var Basic = {
36
+ args: {
37
+ spacing: '0.5rem',
38
+ allowMultiple: true,
39
+ },
40
+ render: function (args) { return (_jsxs(CollapserGroup, __assign({}, args, { children: [_jsx(Collapser, { title: "What is this documentation system?", children: _jsx("p", { children: "This is a comprehensive documentation design system that provides reusable components and guidelines for creating effective technical documentation." }) }), _jsx(Collapser, { title: "How do I get started?", children: _jsx("p", { children: "Start by installing the component package, then explore the components in Storybook to understand their usage and configuration options." }) }), _jsx(Collapser, { title: "Can I customize the components?", children: _jsx("p", { children: "Yes! All components accept a className prop for custom styling, and you can override the default styles using CSS." }) })] }))); },
41
+ };
42
+ /**
43
+ * Accordion mode - only one collapser can be open at a time.
44
+ */
45
+ export var AccordionMode = {
46
+ args: {
47
+ allowMultiple: false,
48
+ },
49
+ render: function (args) { return (_jsxs(CollapserGroup, __assign({}, args, { children: [_jsxs(Collapser, { title: "Installation", children: [_jsx("p", { children: "Install the package using npm or yarn:" }), _jsx("pre", { children: "npm install @roadlittledawn/docs-design-system" })] }), _jsxs(Collapser, { title: "Configuration", children: [_jsx("p", { children: "Import the CSS and components in your application:" }), _jsx("pre", { children: "import '@roadlittledawn/docs-design-system/dist/styles.css';" })] }), _jsxs(Collapser, { title: "Usage", children: [_jsx("p", { children: "Use the components in your React application:" }), _jsx("pre", { children: '<Button variant="primary">Click me</Button>' })] })] }))); },
50
+ };
51
+ /**
52
+ * Custom spacing between collapsers.
53
+ */
54
+ export var CustomSpacing = {
55
+ args: {
56
+ spacing: '1.5rem',
57
+ },
58
+ render: function (args) { return (_jsxs(CollapserGroup, __assign({}, args, { children: [_jsx(Collapser, { title: "Section 1", children: _jsx("p", { children: "Content with larger spacing between sections." }) }), _jsx(Collapser, { title: "Section 2", children: _jsx("p", { children: "This makes the layout more breathable." }) }), _jsx(Collapser, { title: "Section 3", children: _jsx("p", { children: "Useful for prominent content sections." }) })] }))); },
59
+ };
60
+ /**
61
+ * One collapser open by default.
62
+ */
63
+ export var DefaultOpen = {
64
+ args: {
65
+ defaultOpen: 0,
66
+ },
67
+ render: function (args) { return (_jsxs(CollapserGroup, __assign({}, args, { children: [_jsx(Collapser, { title: "Getting Started", children: _jsx("p", { children: "This section is open by default." }) }), _jsx(Collapser, { title: "Advanced Topics", children: _jsx("p", { children: "This section starts closed." }) }), _jsx(Collapser, { title: "API Reference", children: _jsx("p", { children: "This section also starts closed." }) })] }))); },
68
+ };
69
+ /**
70
+ * Multiple collapsers open by default.
71
+ */
72
+ export var MultipleDefaultOpen = {
73
+ args: {
74
+ defaultOpen: [0, 2],
75
+ },
76
+ render: function (args) { return (_jsxs(CollapserGroup, __assign({}, args, { children: [_jsx(Collapser, { title: "Introduction", children: _jsx("p", { children: "This section is open by default." }) }), _jsx(Collapser, { title: "Installation", children: _jsx("p", { children: "This section starts closed." }) }), _jsx(Collapser, { title: "Quick Start", children: _jsx("p", { children: "This section is also open by default." }) })] }))); },
77
+ };
package/dist/index.d.ts CHANGED
@@ -3,6 +3,7 @@ export * from './components/Callout';
3
3
  export * from './components/Card';
4
4
  export * from './components/CardGrid';
5
5
  export * from './components/Collapser';
6
+ export * from './components/CollapserGroup';
6
7
  export * from './components/CodeBlock';
7
8
  export * from './components/Typography';
8
9
  export * from './components/Heading';
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@ export * from './components/Callout';
4
4
  export * from './components/Card';
5
5
  export * from './components/CardGrid';
6
6
  export * from './components/Collapser';
7
+ export * from './components/CollapserGroup';
7
8
  export * from './components/CodeBlock';
8
9
  export * from './components/Typography';
9
10
  export * from './components/Heading';
package/dist/styles.css CHANGED
@@ -1012,6 +1012,13 @@ a.no-text-decoration {
1012
1012
  padding: var(--dds-collapser-content-padding);
1013
1013
  color: var(--dds-collapser-text);
1014
1014
  }
1015
+ .dds-collapser-group {
1016
+ display: flex;
1017
+ flex-direction: column;
1018
+ }
1019
+ .dds-collapser-group > .dds-collapser {
1020
+ margin: 0;
1021
+ }
1015
1022
  .dds-code-block {
1016
1023
  border: 1px solid var(--dds-code-block-border);
1017
1024
  border-radius: var(--dds-code-block-radius);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@roadlittledawn/docs-design-system-react",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "license": "MIT",
5
5
  "description": "React components for documentation design system",
6
6
  "repository": {