@datalayer/primer-addons 1.0.4 → 1.0.5

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.
@@ -0,0 +1,31 @@
1
+ import React from 'react';
2
+ import { ArgTypes } from '@storybook/react';
3
+ import { Icon } from '@primer/octicons-react';
4
+ type StoryContext = Record<string, unknown> & {
5
+ globals: {
6
+ colorScheme: string;
7
+ };
8
+ parameters: Record<string, unknown>;
9
+ };
10
+ export declare const colormodeFromScheme: (colorScheme: string) => "dark" | "light";
11
+ export declare const withThemeProvider: (Story: React.FC<React.PropsWithChildren<StoryContext>>, context: StoryContext) => string | number | boolean | Iterable<React.ReactNode> | import("react/jsx-runtime").JSX.Element | null | undefined;
12
+ export declare const toolbarTypes: {
13
+ colorScheme: {
14
+ name: string;
15
+ description: string;
16
+ defaultValue: string;
17
+ toolbar: {
18
+ icon: string;
19
+ items: string[];
20
+ title: string;
21
+ };
22
+ };
23
+ };
24
+ export declare const inputWrapperArgTypes: ArgTypes;
25
+ export declare const getTextInputArgTypes: (category?: string) => Record<string, unknown>;
26
+ export declare const OcticonArgType: (iconList: Icon[]) => {
27
+ options: string[];
28
+ control: string;
29
+ mapping: Record<string, Icon>;
30
+ };
31
+ export {};
@@ -0,0 +1,105 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { createGlobalStyle } from 'styled-components';
3
+ import { ThemeProvider, themeGet, BaseStyles, theme } from '@primer/react';
4
+ // set global theme styles for each story
5
+ const GlobalStyle = createGlobalStyle `
6
+ body,
7
+ .docs-story {
8
+ background-color: ${themeGet('colors.canvas.default')};
9
+ color: ${themeGet('colors.fg.default')};
10
+ }
11
+ `;
12
+ export const colormodeFromScheme = (colorScheme) => {
13
+ return colorScheme.startsWith('light') ? 'light' : 'dark';
14
+ };
15
+ export const withThemeProvider = (Story, context) => {
16
+ // used for testing ThemeProvider.stories.tsx
17
+ if (context.parameters.disableThemeDecorator)
18
+ return Story(context);
19
+ const { colorScheme } = context.globals;
20
+ return (_jsxs(ThemeProvider, { theme: theme, colorMode: colormodeFromScheme(colorScheme), dayScheme: colorScheme, nightScheme: colorScheme, children: [colorScheme.startsWith('light') ? (_jsx(GlobalStyle, { "$lightTheme": true })) : (_jsx(GlobalStyle, {})), _jsx(BaseStyles, { children: _jsx("div", { id: "html-addon-root", children: Story(context) }) })] }));
21
+ };
22
+ export const toolbarTypes = {
23
+ colorScheme: {
24
+ name: 'Color Scheme',
25
+ description: 'Switch color scheme',
26
+ defaultValue: 'light',
27
+ toolbar: {
28
+ icon: 'photo',
29
+ items: [...Object.keys(theme.colorSchemes)],
30
+ title: 'Color Scheme',
31
+ },
32
+ },
33
+ };
34
+ export const inputWrapperArgTypes = {
35
+ block: {
36
+ defaultValue: false,
37
+ control: 'boolean',
38
+ },
39
+ contrast: {
40
+ defaultValue: false,
41
+ control: 'boolean',
42
+ },
43
+ disabled: {
44
+ defaultValue: false,
45
+ control: 'boolean',
46
+ },
47
+ placeholder: {
48
+ defaultValue: '',
49
+ control: 'text',
50
+ },
51
+ size: {
52
+ name: 'size (input)', // TODO: remove '(input)'
53
+ defaultValue: 'medium',
54
+ options: ['small', 'medium', 'large'],
55
+ control: 'radio',
56
+ },
57
+ validationStatus: {
58
+ defaultValue: undefined,
59
+ options: ['error', 'success', undefined],
60
+ control: 'radio',
61
+ },
62
+ };
63
+ const textInputArgTypesUnsorted = {
64
+ ...inputWrapperArgTypes,
65
+ loading: {
66
+ defaultValue: false,
67
+ control: 'boolean',
68
+ },
69
+ loaderPosition: {
70
+ defaultValue: 'auto',
71
+ options: ['auto', 'leading', 'trailing'],
72
+ control: 'radio',
73
+ },
74
+ monospace: {
75
+ defaultValue: false,
76
+ control: 'boolean',
77
+ },
78
+ };
79
+ // Alphabetize and optionally categorize the props
80
+ export const getTextInputArgTypes = (category) => Object.keys(textInputArgTypesUnsorted)
81
+ .sort()
82
+ .reduce((obj, key) => {
83
+ obj[key] = category
84
+ ? {
85
+ // have to do weird type casting so we can spread the object
86
+ ...textInputArgTypesUnsorted[key],
87
+ table: {
88
+ category,
89
+ },
90
+ }
91
+ : textInputArgTypesUnsorted[key];
92
+ return obj;
93
+ }, {});
94
+ // Use this function for icon options in the controls. Desired icons are passed in as an array of Octicons
95
+ export const OcticonArgType = (iconList) => {
96
+ const icons = iconList.reduce((obj, icon) => {
97
+ obj[icon.displayName || 'Icon'] = icon;
98
+ return obj;
99
+ }, {});
100
+ return {
101
+ options: Object.keys(icons),
102
+ control: 'select',
103
+ mapping: icons,
104
+ };
105
+ };
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { ThemeProvider, BaseStyles, Box, Button, IconButton, Text, ButtonGroup } from "@primer/react";
2
+ import { Box, Button, IconButton, Text, ButtonGroup } from "@primer/react";
3
3
  import { ProjectIcon, ThreeBarsIcon } from '@primer/octicons-react';
4
4
  import { Card } from '../..';
5
5
  const meta = {
@@ -14,7 +14,7 @@ const meta = {
14
14
  };
15
15
  export default meta;
16
16
  const ThemedCard = (props) => {
17
- return (_jsx(ThemeProvider, { colorMode: props.colorMode, children: _jsx(BaseStyles, { children: _jsx(Box, { p: 3, bg: "canvas.default", children: _jsx(Box, { children: _jsx(Card, { ...props }) }) }) }) }));
17
+ return (_jsx(Box, { p: 3, bg: "canvas.default", children: _jsx(Box, { children: _jsx(Card, { ...props }) }) }));
18
18
  };
19
19
  export const CardDay = {
20
20
  args: {
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { ThemeProvider, BaseStyles, Box } from "@primer/react";
2
+ import { Box } from "@primer/react";
3
3
  import { CheckIcon } from "@primer/octicons-react";
4
4
  import { CloseableFlash } from '../..';
5
5
  const meta = {
@@ -14,7 +14,7 @@ const meta = {
14
14
  };
15
15
  export default meta;
16
16
  const ThemedCloseableFlash = (props) => {
17
- return (_jsx(ThemeProvider, { colorMode: props.colorMode, children: _jsx(BaseStyles, { children: _jsx(Box, { p: 3, bg: "canvas.default", children: _jsx(Box, { children: _jsx(CloseableFlash, { ...props }) }) }) }) }));
17
+ return (_jsx(Box, { p: 3, bg: "canvas.default", children: _jsx(Box, { children: _jsx(CloseableFlash, { ...props }) }) }));
18
18
  };
19
19
  export const CloseableFlashDay = {
20
20
  args: {
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { ThemeProvider, BaseStyles, Box } from "@primer/react";
2
+ import { Box } from "@primer/react";
3
3
  import { ContentLoader } from '../..';
4
4
  const meta = {
5
5
  title: 'Components/ContentLoader',
@@ -12,7 +12,7 @@ const meta = {
12
12
  },
13
13
  };
14
14
  export default meta;
15
- const ThemedContentLoader = (props) => _jsx(ThemeProvider, { colorMode: props.colorMode, children: _jsx(BaseStyles, { children: _jsx(Box, { p: 3, bg: "canvas.default", children: _jsx(ContentLoader, { ...props }) }) }) });
15
+ const ThemedContentLoader = (props) => _jsx(Box, { p: 3, bg: "canvas.default", children: _jsx(ContentLoader, { ...props }) });
16
16
  export const ContentLoaderDay = {
17
17
  args: {
18
18
  count: 3
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { ThemeProvider, BaseStyles, Box } from "@primer/react";
2
+ import { Box } from "@primer/react";
3
3
  import { CircleIcon } from '../..';
4
4
  const meta = {
5
5
  title: 'Components/CircleIcon',
@@ -12,7 +12,7 @@ const meta = {
12
12
  },
13
13
  };
14
14
  export default meta;
15
- const ThemedCircleIcon = (props) => _jsx(ThemeProvider, { colorMode: props.colorMode, children: _jsx(BaseStyles, { children: _jsx(Box, { p: 3, bg: "canvas.default", children: _jsx(CircleIcon, { ...props }) }) }) });
15
+ const ThemedCircleIcon = (props) => _jsx(Box, { p: 3, bg: "canvas.default", children: _jsx(CircleIcon, { ...props }) });
16
16
  export const CircleIconDay = {
17
17
  args: {
18
18
  color: 'danger',
@@ -1,15 +1,13 @@
1
1
  export interface OverlayProps {
2
2
  closeButtonRef?: React.RefObject<HTMLButtonElement>;
3
3
  content: JSX.Element;
4
- direction: 'left' | 'right';
4
+ direction?: 'left' | 'right';
5
5
  headingRef?: React.RefObject<HTMLHeadingElement>;
6
6
  isOpen?: boolean;
7
7
  openButtonRef?: React.RefObject<HTMLButtonElement>;
8
8
  setIsOpen?: React.Dispatch<React.SetStateAction<boolean>>;
9
- width: number | string;
9
+ width?: number | string;
10
+ zIndex?: number;
10
11
  }
11
- export declare const Overlay: {
12
- (props: OverlayProps): import("react/jsx-runtime").JSX.Element;
13
- defaultProps: Partial<OverlayProps>;
14
- };
12
+ export declare const Overlay: ({ width, direction, zIndex, ...rest }: OverlayProps) => import("react/jsx-runtime").JSX.Element;
15
13
  export default Overlay;
@@ -1,53 +1,41 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { useEffect, useState } from 'react';
3
- import { Box, IconButton, Overlay as PrimerOverlay } from "@primer/react";
3
+ import { IconButton, Overlay as PrimerOverlay, BaseStyles, ThemeProvider, useTheme } from "@primer/react";
4
4
  import { XIcon } from "@primer/octicons-react";
5
+ import { Box } from '../box/Box';
5
6
  const PrimerAddonOverlay = (props) => {
6
- const { closeButtonRef, content, direction: propsDirection, headingRef, isOpen, openButtonRef, setIsOpen, width, } = props;
7
+ const { closeButtonRef, content, direction: propsDirection, headingRef, isOpen, openButtonRef, setIsOpen, width, zIndex, } = props;
8
+ const { theme, colorMode } = useTheme();
7
9
  const [direction, _] = useState(propsDirection);
8
10
  const [top, setTop] = useState(0);
9
11
  useEffect(() => {
10
12
  setTop(headingRef?.current?.getBoundingClientRect().bottom ?? 0);
11
13
  }, [headingRef]);
12
14
  const closeOverlay = () => setIsOpen(false);
15
+ const overlayContent = (_jsx(ThemeProvider, { theme: theme, colorMode: colorMode, children: _jsx(BaseStyles, { children: _jsxs(Box, { sx: {
16
+ /* We need to remove the padding */
17
+ height: `calc(100vh - ${top}px - 8px)`,
18
+ width,
19
+ display: 'flex',
20
+ flexDirection: 'column',
21
+ alignItems: 'center',
22
+ padding: '4px'
23
+ }, children: [_jsx(Box, { sx: {
24
+ display: 'flex',
25
+ justifyContent: 'right',
26
+ width: '100%'
27
+ }, children: _jsx(IconButton, { variant: 'invisible', ref: closeButtonRef, onClick: closeOverlay, icon: XIcon, "aria-labelledby": "close" }) }), content] }) }) }));
13
28
  return (closeButtonRef && openButtonRef ?
14
29
  _jsx(_Fragment, { children: isOpen ?
15
30
  direction === 'left' ?
16
- _jsx(PrimerOverlay, { initialFocusRef: closeButtonRef, returnFocusRef: openButtonRef, ignoreClickRefs: [openButtonRef], onEscape: closeOverlay, onClickOutside: closeOverlay, width: "auto", anchorSide: "inside-right", top: top, children: _jsxs(Box, { sx: {
17
- /* We need to remove the padding */
18
- height: `calc(100vh - ${top}px - 8px)`,
19
- width,
20
- display: 'flex',
21
- flexDirection: 'column',
22
- alignItems: 'center',
23
- padding: '4px'
24
- }, children: [_jsx(Box, { sx: {
25
- display: 'flex',
26
- justifyContent: 'right',
27
- width: '100%'
28
- }, children: _jsx(IconButton, { variant: 'invisible', ref: closeButtonRef, onClick: closeOverlay, icon: XIcon, "aria-labelledby": "close" }) }), content] }) })
31
+ _jsx(Box, { sx: { zIndex }, children: _jsx(PrimerOverlay, { initialFocusRef: closeButtonRef, returnFocusRef: openButtonRef, ignoreClickRefs: [openButtonRef], onEscape: closeOverlay, onClickOutside: closeOverlay, width: "auto", anchorSide: "inside-right", left: 0, position: "fixed", top: top, children: overlayContent }) })
29
32
  :
30
- _jsx(PrimerOverlay, { initialFocusRef: closeButtonRef, returnFocusRef: openButtonRef, ignoreClickRefs: [openButtonRef], onEscape: closeOverlay, onClickOutside: closeOverlay, width: "auto", anchorSide: 'inside-left', right: 0, position: "fixed", top: top, children: _jsxs(Box, { sx: {
31
- /* We need to remove the padding */
32
- height: `calc(100vh - ${top}px - 8px)`,
33
- width,
34
- display: 'flex',
35
- flexDirection: 'column',
36
- alignItems: 'center',
37
- padding: '4px'
38
- }, children: [_jsx(Box, { sx: {
39
- display: 'flex',
40
- justifyContent: 'right',
41
- width: '100%'
42
- }, children: _jsx(IconButton, { variant: 'invisible', ref: closeButtonRef, onClick: closeOverlay, icon: XIcon, "aria-labelledby": "close" }) }), content] }) })
33
+ _jsx(Box, { sx: { zIndex }, children: _jsx(PrimerOverlay, { initialFocusRef: closeButtonRef, returnFocusRef: openButtonRef, ignoreClickRefs: [openButtonRef], onEscape: closeOverlay, onClickOutside: closeOverlay, width: "auto", anchorSide: 'inside-left', right: 0, position: "fixed", top: top, children: overlayContent }) })
43
34
  : _jsx(_Fragment, {}) })
44
35
  :
45
36
  _jsx(_Fragment, {}));
46
37
  };
47
- export const Overlay = (props) => {
48
- return (_jsx(PrimerAddonOverlay, { ...props }));
49
- };
50
- Overlay.defaultProps = {
51
- width: '500px',
38
+ export const Overlay = ({ width = '500px', direction = 'left', zIndex = 100, ...rest }) => {
39
+ return (_jsx(PrimerAddonOverlay, { width: width, direction: direction, zIndex: zIndex, ...rest }));
52
40
  };
53
41
  export default Overlay;
@@ -2,10 +2,7 @@ import type { StoryObj } from '@storybook/react';
2
2
  import { OverlayProps } from '../..';
3
3
  declare const meta: {
4
4
  title: string;
5
- component: {
6
- (props: OverlayProps): import("react/jsx-runtime").JSX.Element;
7
- defaultProps: Partial<OverlayProps>;
8
- };
5
+ component: ({ width, direction, zIndex, ...rest }: OverlayProps) => import("react/jsx-runtime").JSX.Element;
9
6
  tags: string[];
10
7
  parameters: {
11
8
  layout: string;
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useState, useRef } from "react";
3
- import { Heading, Text, ThemeProvider, BaseStyles, Box, Button } from "@primer/react";
3
+ import { Heading, Text, Box, Button } from "@primer/react";
4
4
  import { Overlay } from '../..';
5
5
  const meta = {
6
6
  title: 'Components/Overlay',
@@ -18,7 +18,7 @@ const ThemedOverlay = (props) => {
18
18
  const openButtonRef = useRef(null);
19
19
  const closeButtonRef = useRef(null);
20
20
  const headingRef = useRef(null);
21
- return (_jsx(ThemeProvider, { colorMode: props.colorMode, children: _jsx(BaseStyles, { children: _jsx(Box, { p: 3, bg: "canvas.default", children: _jsxs(Box, { children: [_jsx(Box, { sx: { width: '100%', bg: 'darkgray' }, children: _jsx(Heading, { ref: headingRef, children: "Header" }) }), _jsxs(Box, { children: [_jsx(Button, { ref: openButtonRef, onClick: () => { setIsOpen(!isOpen); }, children: "Open overlay" }), _jsx(Overlay, { ...props, isOpen: isOpen, setIsOpen: setIsOpen, openButtonRef: openButtonRef, closeButtonRef: closeButtonRef, headingRef: props.direction === 'left' ? headingRef : undefined })] })] }) }) }) }));
21
+ return (_jsx(Box, { p: 3, bg: "canvas.default", children: _jsxs(Box, { children: [_jsx(Box, { sx: { width: '100%', bg: 'darkgray' }, children: _jsx(Heading, { ref: headingRef, children: "Header" }) }), _jsxs(Box, { children: [_jsx(Button, { ref: openButtonRef, onClick: () => { setIsOpen(!isOpen); }, children: "Open overlay" }), _jsx(Overlay, { ...props, isOpen: isOpen, setIsOpen: setIsOpen, openButtonRef: openButtonRef, closeButtonRef: closeButtonRef })] })] }) }));
22
22
  };
23
23
  export const OverlayDayLeft = {
24
24
  args: {
@@ -0,0 +1,13 @@
1
+ import type { StoryObj } from '@storybook/react';
2
+ import { SliderProps } from '../../index';
3
+ declare const meta: {
4
+ title: string;
5
+ component: ({ name, id, min, max, value, label, step, markers, displayValue, disabled, orientation, width, onChange, }: SliderProps) => import("react/jsx-runtime").JSX.Element;
6
+ tags: string[];
7
+ parameters: {
8
+ layout: string;
9
+ };
10
+ };
11
+ export default meta;
12
+ type Story = StoryObj<typeof meta>;
13
+ export declare const SliderStory: Story;
@@ -0,0 +1,33 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import { Box, CounterLabel } from "@primer/react";
4
+ import { Slider } from '../../index';
5
+ const meta = {
6
+ title: 'Components/Slider',
7
+ component: Slider,
8
+ // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/react/writing-docs/autodocs
9
+ tags: ['autodocs'],
10
+ parameters: {
11
+ // More on how to position stories at: https://storybook.js.org/docs/react/configure/story-layout
12
+ layout: 'fullscreen',
13
+ },
14
+ };
15
+ export default meta;
16
+ const ThemedSlider = (props) => {
17
+ const [value, setValue] = useState(50);
18
+ return (_jsxs(Box, { p: 3, bg: "canvas.default", children: [_jsx(Box, { children: _jsx(Slider, { ...props, value: value, onChange: setValue }) }), _jsx(Box, { children: _jsx(CounterLabel, { sx: { ml: 2 }, children: value }) })] }));
19
+ };
20
+ export const SliderStory = {
21
+ args: {
22
+ name: "slider-day",
23
+ id: "slider-day",
24
+ min: 0,
25
+ max: 100,
26
+ step: 10,
27
+ value: 50,
28
+ onChange: (value) => {
29
+ console.log('Slider value changed:', value);
30
+ },
31
+ },
32
+ render: (args) => _jsx(ThemedSlider, { ...args })
33
+ };
@@ -5,6 +5,15 @@
5
5
  import { registerPortalRoot } from '@primer/react';
6
6
  import '@primer/react-brand/lib/css/main.css';
7
7
  const PRIMER_PORTAL_ROOT_ID = '__primerPortalRoot__';
8
+ /**
9
+ * Resolve 'auto' colormode to the actual OS preference.
10
+ */
11
+ const resolveColormode = (colormode) => {
12
+ if (colormode === 'auto') {
13
+ return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
14
+ }
15
+ return colormode;
16
+ };
8
17
  /**
9
18
  * Ensure we define a root for Primer portal root.
10
19
  *
@@ -12,9 +21,10 @@ const PRIMER_PORTAL_ROOT_ID = '__primerPortalRoot__';
12
21
  * @see https://github.com/primer/react/blob/030fe020b48b7f12c2994c6614e5d4191fe764ee/src/Portal/Portal.tsx#L33
13
22
  */
14
23
  export const setupPrimerPortals = (colormode = 'light') => {
24
+ const resolved = resolveColormode(colormode);
15
25
  const body = document.body;
16
26
  body.dataset['portalRoot'] = 'true';
17
- body.dataset['colorMode'] = colormode;
27
+ body.dataset['colorMode'] = resolved;
18
28
  body.dataset['lightTheme'] = 'light';
19
29
  body.dataset['darkTheme'] = 'dark';
20
30
  body.id = PRIMER_PORTAL_ROOT_ID;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datalayer/primer-addons",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "license": "BSD-3-Clause",
5
5
  "scripts": {
6
6
  "dev": "vite",
@@ -33,23 +33,27 @@
33
33
  "styled-components": "^5.3.10"
34
34
  },
35
35
  "devDependencies": {
36
+ "@storybook/addon-docs": "^9.0.15",
36
37
  "@storybook/addon-essentials": "9.0.0-alpha.12",
37
- "@storybook/addon-interactions": "9.0.0-alpha.10",
38
+ "@storybook/addon-interactions": "^9.0.0-alpha.10",
38
39
  "@storybook/addon-links": "^9.0.15",
39
- "@storybook/blocks": "9.0.0-alpha.17",
40
+ "@storybook/blocks": "^9.0.0-alpha.17",
40
41
  "@storybook/react": "^9.0.15",
41
42
  "@storybook/react-vite": "^9.0.15",
42
43
  "@storybook/testing-library": "^0.2.2",
44
+ "@types/prettier": "^3.0.0",
43
45
  "@types/react": "18.3.20",
44
46
  "@types/react-dom": "18.3.6",
45
47
  "@types/styled-components": "^5.1.34",
46
48
  "@typescript-eslint/eslint-plugin": "^8.29.1",
47
49
  "@typescript-eslint/parser": "^8.29.1",
48
50
  "@vitejs/plugin-react": "^4.5.2",
51
+ "crypto-browserify": "^3.12.0",
49
52
  "eslint": "^9.0.0",
50
53
  "eslint-plugin-react-hooks": "^5.2.0",
51
54
  "eslint-plugin-react-refresh": "^0.3.4",
52
55
  "eslint-plugin-storybook": "^0.6.12",
56
+ "prettier": "^3.3.2",
53
57
  "prop-types": "^15.8.1",
54
58
  "storybook": "^9.0.15",
55
59
  "typescript": "^5.8.3",