reshaped 3.8.0-canary.5 → 3.8.0-canary.7

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.
Files changed (70) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/bundle.css +1 -1
  3. package/dist/bundle.js +11 -11
  4. package/dist/components/Autocomplete/Autocomplete.js +2 -2
  5. package/dist/components/Autocomplete/Autocomplete.types.d.ts +1 -1
  6. package/dist/components/DropdownMenu/DropdownMenu.js +4 -4
  7. package/dist/components/DropdownMenu/DropdownMenu.types.d.ts +2 -2
  8. package/dist/components/Flyout/Flyout.module.css +1 -1
  9. package/dist/components/Flyout/Flyout.types.d.ts +7 -7
  10. package/dist/components/Flyout/FlyoutContent.js +1 -1
  11. package/dist/components/Flyout/FlyoutControlled.js +4 -2
  12. package/dist/components/Flyout/tests/Flyout.stories.d.ts +8 -0
  13. package/dist/components/Flyout/tests/Flyout.stories.js +81 -33
  14. package/dist/components/Flyout/useFlyout.d.ts +1 -7
  15. package/dist/components/Flyout/useFlyout.js +5 -1
  16. package/dist/components/Flyout/utilities/calculatePosition.d.ts +3 -2
  17. package/dist/components/Flyout/utilities/calculatePosition.js +47 -22
  18. package/dist/components/Flyout/utilities/flyout.js +4 -2
  19. package/dist/components/Flyout/utilities/getPositionFallbacks.js +3 -3
  20. package/dist/components/Flyout/utilities/isFullyVisible.d.ts +0 -2
  21. package/dist/components/Flyout/utilities/isFullyVisible.js +5 -7
  22. package/dist/components/Hidden/Hidden.js +2 -1
  23. package/dist/components/Icon/Icon.js +2 -2
  24. package/dist/components/Icon/Icon.types.d.ts +1 -1
  25. package/dist/components/Icon/tests/Icon.stories.js +6 -1
  26. package/dist/components/MenuItem/MenuItem.module.css +1 -1
  27. package/dist/components/Popover/Popover.js +2 -2
  28. package/dist/components/Popover/Popover.module.css +1 -1
  29. package/dist/components/Popover/Popover.types.d.ts +3 -1
  30. package/dist/components/Reshaped/Reshaped.css +1 -1
  31. package/dist/components/Resizable/Resizable.js +4 -3
  32. package/dist/components/Select/Select.d.ts +8 -1
  33. package/dist/components/Select/Select.js +22 -48
  34. package/dist/components/Select/Select.module.css +1 -1
  35. package/dist/components/Select/Select.types.d.ts +56 -38
  36. package/dist/components/Select/SelectCustom.d.ts +3 -0
  37. package/dist/components/Select/SelectCustom.js +11 -0
  38. package/dist/components/Select/SelectCustomControlled.d.ts +4 -0
  39. package/dist/components/Select/SelectCustomControlled.js +88 -0
  40. package/dist/components/Select/SelectCustomUncontrolled.d.ts +4 -0
  41. package/dist/components/Select/SelectCustomUncontrolled.js +15 -0
  42. package/dist/components/Select/SelectEndContent.d.ts +3 -0
  43. package/dist/components/Select/SelectEndContent.js +12 -0
  44. package/dist/components/Select/SelectNative.d.ts +4 -0
  45. package/dist/components/Select/SelectNative.js +29 -0
  46. package/dist/components/Select/SelectOption.d.ts +4 -0
  47. package/dist/components/Select/SelectOption.js +15 -0
  48. package/dist/components/Select/SelectOptionGroup.d.ts +4 -0
  49. package/dist/components/Select/SelectOptionGroup.js +11 -0
  50. package/dist/components/Select/SelectRoot.d.ts +4 -0
  51. package/dist/components/Select/SelectRoot.js +21 -0
  52. package/dist/components/Select/SelectStartContent.d.ts +3 -0
  53. package/dist/components/Select/SelectStartContent.js +20 -0
  54. package/dist/components/Select/SelectTrigger.d.ts +4 -0
  55. package/dist/components/Select/SelectTrigger.js +16 -0
  56. package/dist/components/Select/tests/Select.stories.d.ts +35 -10
  57. package/dist/components/Select/tests/Select.stories.js +447 -175
  58. package/dist/components/Stepper/Stepper.js +2 -2
  59. package/dist/components/Stepper/Stepper.types.d.ts +2 -0
  60. package/dist/components/Stepper/tests/Stepper.stories.d.ts +1 -0
  61. package/dist/components/Stepper/tests/Stepper.stories.js +23 -0
  62. package/dist/components/Table/Table.js +6 -3
  63. package/dist/components/Timeline/Timeline.js +3 -2
  64. package/dist/components/Timeline/tests/Timeline.stories.js +0 -3
  65. package/dist/components/View/View.js +6 -4
  66. package/dist/utilities/props.d.ts +3 -0
  67. package/dist/utilities/props.js +19 -0
  68. package/package.json +1 -1
  69. package/dist/components/Select/tests/Select.test.stories.d.ts +0 -27
  70. package/dist/components/Select/tests/Select.test.stories.js +0 -132
@@ -18,10 +18,10 @@ const StepperItemPrivate = (props) => {
18
18
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
19
19
  const StepperItem = (_) => null;
20
20
  const Stepper = (props) => {
21
- const { children, direction = "row", activeId, labelDisplay, className, attributes } = props;
21
+ const { children, direction = "row", activeId, labelDisplay, gap = 3, className, attributes, } = props;
22
22
  const vertical = direction === "column";
23
23
  const length = React.Children.count(children);
24
- return (_jsx(View, { attributes: attributes, direction: direction, align: vertical ? "stretch" : "center", className: className, gap: 3, wrap: false, children: React.Children.map(children, (child, index) => {
24
+ return (_jsx(View, { attributes: attributes, direction: direction, align: vertical ? "stretch" : "center", className: className, gap: gap, wrap: false, children: React.Children.map(children, (child, index) => {
25
25
  if (!React.isValidElement(child))
26
26
  return null;
27
27
  if (child.type !== StepperItem)
@@ -9,6 +9,8 @@ export type Props = {
9
9
  children?: React.ReactNode;
10
10
  /** Direction of the stepper */
11
11
  direction?: "row" | "column";
12
+ /** Gap between items (default: 3) */
13
+ gap?: G.Responsive<number>;
12
14
  /** Additional classname for the root element */
13
15
  className?: G.ClassName;
14
16
  /** Additional attributes for the root element */
@@ -16,4 +16,5 @@ declare const _default: {
16
16
  export default _default;
17
17
  export declare const direction: () => React.JSX.Element;
18
18
  export declare const labelDisplay: () => React.JSX.Element;
19
+ export declare const gap: () => React.JSX.Element;
19
20
  export declare const edgeCases: () => React.JSX.Element;
@@ -72,6 +72,29 @@ export const labelDisplay = () => (<Example>
72
72
  </Stepper>
73
73
  </Example.Item>
74
74
  </Example>);
75
+ export const gap = () => (<Example>
76
+ <Example.Item title="gap: 6, direction: row">
77
+ <Stepper activeId="1" gap={6}>
78
+ <Stepper.Item completed title="Step 1" subtitle="Step subtitle"/>
79
+ <Stepper.Item title="Step 2"/>
80
+ <Stepper.Item title="Step 3 very long title"/>
81
+ </Stepper>
82
+ </Example.Item>
83
+ <Example.Item title="gap: 6, direction: column">
84
+ <Stepper activeId="1" direction="column" gap={6}>
85
+ <Stepper.Item completed title="Step 1" subtitle="Step subtitle"/>
86
+ <Stepper.Item title="Step 2"/>
87
+ <Stepper.Item title="Step 3 very long title"/>
88
+ </Stepper>
89
+ </Example.Item>
90
+ <Example.Item title={["responsive gap", "[s] 2", "[m+] 5"]}>
91
+ <Stepper activeId="1" gap={{ s: 2, m: 5 }}>
92
+ <Stepper.Item completed title="Step 1" subtitle="Step subtitle"/>
93
+ <Stepper.Item title="Step 2"/>
94
+ <Stepper.Item title="Step 3 very long title"/>
95
+ </Stepper>
96
+ </Example.Item>
97
+ </Example>);
75
98
  export const edgeCases = () => (<Example>
76
99
  <Example.Item title={[
77
100
  "multiline subtitle",
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import React from "react";
4
- import { classNames, responsiveVariables } from "../../utilities/props.js";
4
+ import { classNames, isMatchingComponentChildId, responsiveVariables, setComponentChildId, } from "../../utilities/props.js";
5
5
  import { resolveMixin } from "../../styles/mixin.js";
6
6
  import s from "./Table.module.css";
7
7
  import useFadeSide from "../../hooks/_private/useFadeSide.js";
@@ -43,14 +43,17 @@ const Table = (props) => {
43
43
  const fadeSide = useFadeSide(rootRef);
44
44
  const rootClassNames = classNames(s.root, className, border && s["--border-outer"], columnBorder && s["--border-column"], (fadeSide === "start" || fadeSide === "both") && s["--fade-start"], (fadeSide === "end" || fadeSide === "both") && s["--fade-end"]);
45
45
  const [firstChild] = React.Children.toArray(children);
46
- return (_jsx("div", { ...attributes, className: rootClassNames, ref: rootRef, children: _jsx("table", { className: s.table, children: React.isValidElement(firstChild) &&
47
- (firstChild.type === TableBody || firstChild.type === TableHead) ? (children) : (_jsx(TableBody, { children: children })) }) }));
46
+ const isBody = isMatchingComponentChildId(firstChild, "Table.Body");
47
+ const isHead = isMatchingComponentChildId(firstChild, "Table.Head");
48
+ return (_jsx("div", { ...attributes, className: rootClassNames, ref: rootRef, children: _jsx("table", { className: s.table, children: isBody || isHead ? children : _jsx(TableBody, { children: children }) }) }));
48
49
  };
49
50
  Table.Cell = TableCell;
50
51
  Table.Heading = TableHeading;
51
52
  Table.Row = TableRow;
52
53
  Table.Body = TableBody;
53
54
  Table.Head = TableHead;
55
+ setComponentChildId(Table.Body, "Table.Body");
56
+ setComponentChildId(Table.Head, "Table.Head");
54
57
  Table.displayName = "Table";
55
58
  TableCell.displayName = "TableCell";
56
59
  TableHeading.displayName = "TableHeading";
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import React from "react";
3
- import { classNames } from "../../utilities/props.js";
3
+ import { classNames, isMatchingComponentChildId, setComponentChildId } from "../../utilities/props.js";
4
4
  import View from "../View/index.js";
5
5
  import s from "./Timeline.module.css";
6
6
  const TimelineItem = (props) => {
@@ -13,10 +13,11 @@ const Timeline = (props) => {
13
13
  const { children, className, attributes } = props;
14
14
  const rootClassNames = classNames(className);
15
15
  return (_jsx("ul", { ...attributes, className: rootClassNames, children: React.Children.map(children, (child, index) => {
16
- return React.isValidElement(child) && child.type === TimelineItem ? (child) : (_jsx(TimelineItem, { children: child }, index));
16
+ return isMatchingComponentChildId(child, "Timeline.Item") ? (child) : (_jsx(TimelineItem, { children: child }, index));
17
17
  }) }));
18
18
  };
19
19
  Timeline.Item = TimelineItem;
20
+ setComponentChildId(Timeline.Item, "Timeline.Item");
20
21
  Timeline.displayName = "Timeline";
21
22
  TimelineItem.displayName = "Timeline.Item";
22
23
  export default Timeline;
@@ -54,9 +54,6 @@ export const marker = () => (<Example>
54
54
  <Timeline.Item markerSlot={null}>
55
55
  <Placeholder />
56
56
  </Timeline.Item>
57
- <Timeline.Item>
58
- <Placeholder />
59
- </Timeline.Item>
60
57
  </Timeline>
61
58
  </Example.Item>
62
59
  </Example>);
@@ -1,7 +1,7 @@
1
1
  import { createElement as _createElement } from "react";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import React from "react";
4
- import { classNames, responsiveClassNames, responsiveVariables } from "../../utilities/props.js";
4
+ import { classNames, isMatchingComponentChildId, responsiveClassNames, responsiveVariables, setComponentChildId, } from "../../utilities/props.js";
5
5
  import Divider from "../Divider/index.js";
6
6
  import Hidden from "../Hidden/index.js";
7
7
  import s from "./View.module.css";
@@ -100,8 +100,8 @@ const View = (props) => {
100
100
  return (_jsx("div", { className: dividerClassName, children: _jsx(Divider, { vertical: isDividerVertical, blank: true }) }, `${key}-divider`));
101
101
  };
102
102
  const renderItem = ({ className, child, index }) => {
103
- const isItem = child.type === ViewItem;
104
- const isView = child.type === View;
103
+ const isItem = isMatchingComponentChildId(child, "View.Item");
104
+ const isView = isMatchingComponentChildId(child, "View");
105
105
  const key = child.key;
106
106
  const dividerElement = !!index && divided && renderDivider({ className, key });
107
107
  let itemElement;
@@ -135,7 +135,7 @@ const View = (props) => {
135
135
  return null;
136
136
  const usedIndex = renderedItemIndex;
137
137
  renderedItemIndex += 1;
138
- if (child.type === Hidden) {
138
+ if (isMatchingComponentChildId(child, "Hidden")) {
139
139
  const { children: hiddenChild, ...hiddenProps } = child.props;
140
140
  const key = child.key || index;
141
141
  return (_createElement(Hidden, { ...hiddenProps, key: key }, renderItem({ child: hiddenChild, index: usedIndex })));
@@ -167,6 +167,8 @@ const View = (props) => {
167
167
  return (_jsx(TagName, { ...attributes, className: rootClassNames, style: rootVariables, children: formattedChildren }));
168
168
  };
169
169
  View.Item = ViewItem;
170
+ setComponentChildId(View.Item, "View.Item");
171
+ setComponentChildId(View, "View");
170
172
  View.displayName = "View";
171
173
  ViewItem.displayName = "View.Item";
172
174
  export default View;
@@ -1,3 +1,4 @@
1
+ import React from "react";
1
2
  import type * as G from "../types/global";
2
3
  type Value = string | boolean | number | undefined;
3
4
  type ClassNameResolver = string | ((value: Value) => string);
@@ -10,4 +11,6 @@ export declare const responsiveClassNames: <V extends G.Responsive<Value>>(s: Re
10
11
  }) => string[];
11
12
  export declare const responsiveVariables: <V extends Value = Value>(variableName: G.CSSVariable, value?: G.Responsive<V>) => Record<G.CSSVariable, V>;
12
13
  export declare const responsivePropDependency: <Result, T>(prop: G.Responsive<T>, resolver: (value: T, key: G.Viewport) => Result) => Result;
14
+ export declare const setComponentChildId: (Component: React.FC<any>, id: string) => string;
15
+ export declare const isMatchingComponentChildId: (child: React.ReactNode, id: string) => boolean | null;
13
16
  export {};
@@ -1,3 +1,4 @@
1
+ import React from "react";
1
2
  /**
2
3
  * Resolve an array of values into a classname string
3
4
  */
@@ -81,3 +82,21 @@ export const responsivePropDependency = (prop, resolver) => {
81
82
  return { ...acc, [viewport]: resolver(viewportValue, viewport) };
82
83
  }, {});
83
84
  };
85
+ /**
86
+ * Add unique ids to components relying on React.Children.map
87
+ * Relying on an id instead of child.type makes it work in dev mode during HMR
88
+ */
89
+ const getComponentChildId = (id) => `__rs-child-${id}`;
90
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
91
+ export const setComponentChildId = (Component, id) => {
92
+ /* eslint-disable-next-line @typescript-eslint/ban-ts-comment */
93
+ // @ts-ignore
94
+ return (Component.__reshapedChildId = getComponentChildId(id));
95
+ };
96
+ export const isMatchingComponentChildId = (child, id) => {
97
+ if (!React.isValidElement(child))
98
+ return null;
99
+ /* eslint-disable-next-line @typescript-eslint/ban-ts-comment */
100
+ // @ts-ignore
101
+ return child.type.__reshapedChildId === getComponentChildId(id);
102
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "reshaped",
3
3
  "description": "Professionally crafted design system in React & Figma for building products of any scale and complexity",
4
- "version": "3.8.0-canary.5",
4
+ "version": "3.8.0-canary.7",
5
5
  "license": "MIT",
6
6
  "email": "hello@reshaped.so",
7
7
  "homepage": "https://reshaped.so",
@@ -1,27 +0,0 @@
1
- import { StoryObj } from "@storybook/react-vite";
2
- import { fn } from "storybook/test";
3
- declare const _default: {
4
- title: string;
5
- component: import("react").FC<import("./..").SelectProps>;
6
- parameters: {
7
- iframe: {
8
- url: string;
9
- };
10
- chromatic: {
11
- disableSnapshot: boolean;
12
- };
13
- };
14
- };
15
- export default _default;
16
- export declare const render: StoryObj;
17
- export declare const startSlot: StoryObj;
18
- export declare const placeholder: StoryObj;
19
- export declare const id: StoryObj;
20
- export declare const disabled: StoryObj;
21
- export declare const defaultValue: StoryObj<{
22
- handleChange: ReturnType<typeof fn>;
23
- }>;
24
- export declare const value: StoryObj<{
25
- handleChange: ReturnType<typeof fn>;
26
- }>;
27
- export declare const className: StoryObj;
@@ -1,132 +0,0 @@
1
- import { expect, userEvent, fn } from "storybook/test";
2
- import Select from "../index.js";
3
- export default {
4
- title: "Components/Select/tests",
5
- component: Select,
6
- parameters: {
7
- iframe: {
8
- url: "https://reshaped.so/docs/components/select",
9
- },
10
- chromatic: { disableSnapshot: true },
11
- },
12
- };
13
- export const render = {
14
- name: "rendering",
15
- render: () => (<Select name="test-name" options={[
16
- { label: "Option 1", value: "1" },
17
- { label: "Option 2", value: "2" },
18
- ]} inputAttributes={{ "aria-label": "test select" }}/>),
19
- play: async ({ canvas }) => {
20
- const elInput = canvas.getByRole("combobox");
21
- const elOptions = canvas.getAllByRole("option");
22
- expect(elInput).toHaveAttribute("name", "test-name");
23
- expect(elInput).toHaveValue("1");
24
- expect(elOptions.length).toBe(2);
25
- expect(elOptions[0]).toHaveValue("1");
26
- expect(elOptions[0].textContent).toBe("Option 1");
27
- expect(elOptions[1]).toHaveValue("2");
28
- expect(elOptions[1].textContent).toBe("Option 2");
29
- },
30
- };
31
- export const startSlot = {
32
- name: "startSlot",
33
- render: () => (<Select name="test-name" startSlot="Slot" options={[]} inputAttributes={{ "aria-label": "test select" }}/>),
34
- play: async ({ canvas }) => {
35
- const slot = canvas.getByText("Slot");
36
- expect(slot).toBeInTheDocument();
37
- },
38
- };
39
- export const placeholder = {
40
- name: "placeholder",
41
- render: () => (<Select name="test-name" placeholder="Placeholder" options={[
42
- { label: "Option 1", value: "1" },
43
- { label: "Option 2", value: "2" },
44
- ]} inputAttributes={{ "aria-label": "test select" }}/>),
45
- play: async ({ canvas }) => {
46
- const input = canvas.getByRole("combobox");
47
- const options = canvas.getAllByRole("option");
48
- expect(input).toHaveValue("");
49
- expect(options.length).toBe(3);
50
- expect(options[0].textContent).toBe("Placeholder");
51
- expect(options[0]).toHaveValue("");
52
- },
53
- };
54
- export const id = {
55
- name: "id",
56
- render: () => (<Select name="test-name" id="test-id" options={[
57
- { label: "Option 1", value: "1" },
58
- { label: "Option 2", value: "2" },
59
- ]} inputAttributes={{ "aria-label": "test select" }}/>),
60
- play: async ({ canvas }) => {
61
- const input = canvas.getByRole("combobox");
62
- expect(input).toHaveAttribute("id", "test-id");
63
- },
64
- };
65
- export const disabled = {
66
- name: "disabled",
67
- render: () => (<Select name="test-name" disabled options={[
68
- { label: "Option 1", value: "1" },
69
- { label: "Option 2", value: "2" },
70
- ]} inputAttributes={{ "aria-label": "test select" }}/>),
71
- play: async ({ canvas }) => {
72
- const input = canvas.getByRole("combobox");
73
- expect(input).toBeDisabled();
74
- },
75
- };
76
- export const defaultValue = {
77
- name: "defaultValue, uncontrolled",
78
- args: {
79
- handleChange: fn(),
80
- },
81
- render: (args) => (<Select name="test-name" defaultValue="2" onChange={args.handleChange} options={[
82
- { label: "Option 1", value: "1" },
83
- { label: "Option 2", value: "2" },
84
- ]} inputAttributes={{ "aria-label": "test select" }}/>),
85
- play: async ({ canvas, args }) => {
86
- const input = canvas.getByRole("combobox");
87
- expect(input).toHaveValue("2");
88
- await userEvent.selectOptions(input, "1");
89
- expect(args.handleChange).toBeCalledTimes(1);
90
- expect(args.handleChange).toHaveBeenCalledWith({
91
- name: "test-name",
92
- value: "1",
93
- event: expect.objectContaining({ target: input }),
94
- });
95
- expect(input).toHaveValue("1");
96
- },
97
- };
98
- export const value = {
99
- name: "value, controlled",
100
- args: {
101
- handleChange: fn(),
102
- },
103
- render: (args) => (<Select name="test-name" value="2" onChange={args.handleChange} options={[
104
- { label: "Option 1", value: "1" },
105
- { label: "Option 2", value: "2" },
106
- ]} inputAttributes={{ "aria-label": "test select" }}/>),
107
- play: async ({ canvas, args }) => {
108
- const input = canvas.getByRole("combobox");
109
- expect(input).toHaveValue("2");
110
- await userEvent.selectOptions(input, "1");
111
- expect(args.handleChange).toBeCalledTimes(1);
112
- expect(args.handleChange).toHaveBeenCalledWith({
113
- name: "test-name",
114
- value: "1",
115
- event: expect.objectContaining({ target: input }),
116
- });
117
- expect(input).toHaveValue("2");
118
- },
119
- };
120
- export const className = {
121
- name: "className, attributes",
122
- render: () => (<div data-testid="root">
123
- <Select className="test-classname" attributes={{ id: "test-id" }} inputAttributes={{ "aria-label": "test select", id: "test-input-id" }} options={[]} name="name"/>
124
- </div>),
125
- play: async ({ canvas }) => {
126
- const root = canvas.getByTestId("root").firstChild;
127
- const input = canvas.getByRole("combobox");
128
- expect(root).toHaveClass("test-classname");
129
- expect(root).toHaveAttribute("id", "test-id");
130
- expect(input).toHaveAttribute("id", "test-input-id");
131
- },
132
- };