@dxos/react-ui-stack 0.8.3 → 0.8.4-main.f9ba587

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 (56) hide show
  1. package/dist/lib/browser/index.mjs +60 -59
  2. package/dist/lib/browser/index.mjs.map +3 -3
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/browser/testing/index.mjs +1 -1
  5. package/dist/lib/node-esm/index.mjs +60 -59
  6. package/dist/lib/node-esm/index.mjs.map +3 -3
  7. package/dist/lib/node-esm/meta.json +1 -1
  8. package/dist/lib/node-esm/testing/index.mjs +1 -1
  9. package/dist/types/src/components/Stack/Stack.stories.d.ts +1 -1
  10. package/dist/types/src/components/Stack/Stack.stories.d.ts.map +1 -1
  11. package/dist/types/src/components/StackItem/StackItem.stories.d.ts +1 -1
  12. package/dist/types/src/components/StackItem/StackItem.stories.d.ts.map +1 -1
  13. package/dist/types/src/exemplars/Card/Card.d.ts +4 -9
  14. package/dist/types/src/exemplars/Card/Card.d.ts.map +1 -1
  15. package/dist/types/src/exemplars/Card/Card.stories.d.ts +13 -0
  16. package/dist/types/src/exemplars/Card/Card.stories.d.ts.map +1 -0
  17. package/dist/types/src/exemplars/Card/fragments.d.ts +1 -2
  18. package/dist/types/src/exemplars/Card/fragments.d.ts.map +1 -1
  19. package/dist/types/src/exemplars/CardStack/CardStack.d.ts +7 -1
  20. package/dist/types/src/exemplars/CardStack/CardStack.d.ts.map +1 -1
  21. package/dist/types/src/exemplars/CardStack/CardStack.stories.d.ts +9 -0
  22. package/dist/types/src/exemplars/CardStack/CardStack.stories.d.ts.map +1 -0
  23. package/dist/types/src/hooks/useStackDropForElements.d.ts.map +1 -1
  24. package/dist/types/src/index.d.ts +1 -1
  25. package/dist/types/src/index.d.ts.map +1 -1
  26. package/dist/types/src/playwright/playwright.config.d.ts +3 -0
  27. package/dist/types/src/playwright/playwright.config.d.ts.map +1 -0
  28. package/dist/types/src/translations.d.ts +13 -14
  29. package/dist/types/src/translations.d.ts.map +1 -1
  30. package/dist/types/tsconfig.tsbuildinfo +1 -1
  31. package/package.json +19 -21
  32. package/src/components/Stack/Stack.stories.tsx +1 -1
  33. package/src/components/StackItem/StackItem.stories.tsx +1 -1
  34. package/src/exemplars/Card/Card.stories.tsx +78 -0
  35. package/src/exemplars/Card/Card.tsx +13 -28
  36. package/src/exemplars/Card/CardDragPreview.tsx +2 -2
  37. package/src/exemplars/Card/fragments.ts +1 -3
  38. package/src/exemplars/CardStack/CardStack.stories.tsx +172 -0
  39. package/src/exemplars/CardStack/CardStack.tsx +18 -1
  40. package/src/hooks/useStackDropForElements.ts +4 -1
  41. package/src/index.ts +3 -4
  42. package/src/playwright/playwright.config.ts +17 -0
  43. package/src/playwright/smoke.spec.ts +6 -4
  44. package/src/translations.ts +5 -3
  45. package/dist/lib/node/index.cjs +0 -1220
  46. package/dist/lib/node/index.cjs.map +0 -7
  47. package/dist/lib/node/meta.json +0 -1
  48. package/dist/lib/node/testing/index.cjs +0 -81
  49. package/dist/lib/node/testing/index.cjs.map +0 -7
  50. package/dist/types/src/exemplars/Card/Card.stories-todo.d.ts +0 -1
  51. package/dist/types/src/exemplars/Card/Card.stories-todo.d.ts.map +0 -1
  52. package/dist/types/src/exemplars/CardStack/CardStack.stories-todo.d.ts +0 -1
  53. package/dist/types/src/exemplars/CardStack/CardStack.stories-todo.d.ts.map +0 -1
  54. package/src/exemplars/Card/Card.stories-todo.tsx +0 -135
  55. package/src/exemplars/CardStack/CardStack.stories-todo.tsx +0 -80
  56. package/src/playwright/playwright.config.cts +0 -18
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/react-ui-stack",
3
- "version": "0.8.3",
3
+ "version": "0.8.4-main.f9ba587",
4
4
  "description": "A stack component.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -49,37 +49,35 @@
49
49
  "@radix-ui/react-slot": "1.1.2",
50
50
  "@radix-ui/react-use-controllable-state": "1.1.0",
51
51
  "react-resize-detector": "^11.0.1",
52
- "@dxos/echo-schema": "0.8.3",
53
- "@dxos/keyboard": "0.8.3",
54
- "@dxos/react-ui-attention": "0.8.3",
55
- "@dxos/live-object": "0.8.3",
56
- "@dxos/react-ui-dnd": "0.8.3",
57
- "@dxos/util": "0.8.3"
52
+ "@dxos/echo-schema": "0.8.4-main.f9ba587",
53
+ "@dxos/keyboard": "0.8.4-main.f9ba587",
54
+ "@dxos/live-object": "0.8.4-main.f9ba587",
55
+ "@dxos/react-ui-attention": "0.8.4-main.f9ba587",
56
+ "@dxos/util": "0.8.4-main.f9ba587",
57
+ "@dxos/react-ui-dnd": "0.8.4-main.f9ba587"
58
58
  },
59
59
  "devDependencies": {
60
- "@phosphor-icons/react": "^2.1.5",
61
60
  "@types/react": "~18.2.0",
62
61
  "@types/react-dom": "~18.2.0",
63
62
  "react": "~18.2.0",
64
63
  "react-dom": "~18.2.0",
65
64
  "vite": "5.4.7",
66
- "@dxos/app-graph": "0.8.3",
67
- "@dxos/client": "0.8.3",
68
- "@dxos/echo-schema": "0.8.3",
69
- "@dxos/react-ui": "0.8.3",
70
- "@dxos/random": "0.8.3",
71
- "@dxos/react-ui-theme": "0.8.3",
72
- "@dxos/test-utils": "0.8.3",
73
- "@dxos/storybook-utils": "0.8.3"
65
+ "@dxos/app-graph": "0.8.4-main.f9ba587",
66
+ "@dxos/echo-schema": "0.8.4-main.f9ba587",
67
+ "@dxos/random": "0.8.4-main.f9ba587",
68
+ "@dxos/client": "0.8.4-main.f9ba587",
69
+ "@dxos/react-ui-theme": "0.8.4-main.f9ba587",
70
+ "@dxos/react-ui": "0.8.4-main.f9ba587",
71
+ "@dxos/storybook-utils": "0.8.4-main.f9ba587",
72
+ "@dxos/test-utils": "0.8.4-main.f9ba587"
74
73
  },
75
74
  "peerDependencies": {
76
- "@phosphor-icons/react": "^2.1.5",
77
75
  "react": "~18.2.0",
78
76
  "react-dom": "~18.2.0",
79
- "@dxos/client": "0.8.3",
80
- "@dxos/random": "0.8.3",
81
- "@dxos/react-ui-theme": "0.8.3",
82
- "@dxos/react-ui": "0.8.3"
77
+ "@dxos/client": "0.8.4-main.f9ba587",
78
+ "@dxos/random": "0.8.4-main.f9ba587",
79
+ "@dxos/react-ui": "0.8.4-main.f9ba587",
80
+ "@dxos/react-ui-theme": "0.8.4-main.f9ba587"
83
81
  },
84
82
  "publishConfig": {
85
83
  "access": "public"
@@ -5,7 +5,7 @@
5
5
  import '@dxos-theme';
6
6
 
7
7
  import { type Edge } from '@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge';
8
- import { type Meta, type StoryObj } from '@storybook/react';
8
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
9
9
  import React, { useState, useCallback } from 'react';
10
10
 
11
11
  import { faker } from '@dxos/random';
@@ -4,7 +4,7 @@
4
4
 
5
5
  import '@dxos-theme';
6
6
 
7
- import { type Meta, type StoryObj } from '@storybook/react';
7
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
8
8
  import React from 'react';
9
9
 
10
10
  import { Icon, DropdownMenu } from '@dxos/react-ui';
@@ -0,0 +1,78 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import '@dxos-theme';
6
+
7
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
8
+ import React from 'react';
9
+
10
+ import { faker } from '@dxos/random';
11
+ import { withTheme } from '@dxos/storybook-utils';
12
+
13
+ import { Card } from './Card';
14
+
15
+ // Set a seed for reproducible random values
16
+ faker.seed(0);
17
+
18
+ type CardStoryProps = {
19
+ title: string;
20
+ description: string;
21
+ image: string;
22
+ showImage: boolean;
23
+ showIcon: boolean;
24
+ };
25
+
26
+ const meta: Meta<CardStoryProps> = {
27
+ title: 'ui/react-ui-stack/Card',
28
+ decorators: [withTheme],
29
+ argTypes: {
30
+ title: {
31
+ control: 'text',
32
+ description: 'Card title',
33
+ },
34
+ description: {
35
+ control: 'text',
36
+ description: 'Card description',
37
+ },
38
+ image: {
39
+ control: 'text',
40
+ description: 'URL for the poster image',
41
+ },
42
+ showImage: {
43
+ control: 'boolean',
44
+ description: 'Whether to show the image',
45
+ },
46
+ showIcon: {
47
+ control: 'boolean',
48
+ description: 'Whether to show an icon (when image is not shown)',
49
+ },
50
+ },
51
+ args: {
52
+ title: faker.commerce.productName(),
53
+ description: faker.lorem.paragraph(),
54
+ image: faker.image.url(),
55
+ showImage: true,
56
+ showIcon: true,
57
+ },
58
+ };
59
+
60
+ export default meta;
61
+
62
+ export const Default: StoryObj<CardStoryProps> = {
63
+ render: ({ title, description, image, showImage, showIcon }: CardStoryProps) => (
64
+ <div className='max-is-md'>
65
+ <Card.StaticRoot>
66
+ <Card.Toolbar>
67
+ <Card.DragHandle toolbarItem />
68
+ <Card.ToolbarSeparator variant='gap' />
69
+ <Card.ToolbarIconButton iconOnly variant='ghost' icon='ph--x--regular' label={'remove card label'} />
70
+ </Card.Toolbar>
71
+ {showImage && <Card.Poster alt={title} image={image} />}
72
+ {!showImage && showIcon && <Card.Poster alt={title} icon='ph--building-office--regular' />}
73
+ <Card.Heading>{title}</Card.Heading>
74
+ {description && <Card.Text classNames='line-clamp-2'>{description}</Card.Text>}
75
+ </Card.StaticRoot>
76
+ </div>
77
+ ),
78
+ };
@@ -15,30 +15,16 @@ import React, {
15
15
  import { Icon, IconButton, type ThemedClassName, Toolbar, type ToolbarRootProps, useTranslation } from '@dxos/react-ui';
16
16
  import { hoverableControls, mx } from '@dxos/react-ui-theme';
17
17
 
18
- import { cardChrome, cardContent, cardHeading, cardRoot, cardText, cardSpacing } from './fragments';
18
+ import { cardChrome, cardRoot, cardHeading, cardText, cardSpacing } from './fragments';
19
19
  import { StackItem } from '../../components';
20
20
  import { translationKey } from '../../translations';
21
21
 
22
22
  type SharedCardProps = ThemedClassName<ComponentPropsWithoutRef<'div'>> & { asChild?: boolean };
23
23
 
24
- const CardRoot = forwardRef<HTMLDivElement, SharedCardProps>(
25
- ({ children, classNames, asChild, role = 'none', ...props }, forwardedRef) => {
26
- const Root = asChild ? Slot : 'div';
27
- const rootProps = asChild ? { classNames: [cardRoot, classNames] } : { className: mx(cardRoot, classNames), role };
28
- return (
29
- <Root {...props} {...rootProps} ref={forwardedRef}>
30
- {children}
31
- </Root>
32
- );
33
- },
34
- );
35
-
36
- const CardContent = forwardRef<HTMLDivElement, SharedCardProps>(
24
+ const CardStaticRoot = forwardRef<HTMLDivElement, SharedCardProps>(
37
25
  ({ children, classNames, asChild, role = 'group', ...props }, forwardedRef) => {
38
26
  const Root = asChild ? Slot : 'div';
39
- const rootProps = asChild
40
- ? { classNames: [cardContent, classNames] }
41
- : { className: mx(cardContent, classNames), role };
27
+ const rootProps = asChild ? { classNames: [cardRoot, classNames] } : { className: mx(cardRoot, classNames), role };
42
28
  return (
43
29
  <Root {...props} {...rootProps} ref={forwardedRef}>
44
30
  {children}
@@ -49,11 +35,11 @@ const CardContent = forwardRef<HTMLDivElement, SharedCardProps>(
49
35
 
50
36
  /**
51
37
  * This should be used by Surface fulfillments in cases where the content may or may not already be encapsulated (e.g.
52
- * in a Popover) and knows this based on the `role` it receives. This will render a `Card.Content` by default, otherwise
38
+ * in a Popover) and knows this based on the `role` it receives. This will render a `Card.StaticRoot` by default, otherwise
53
39
  * it will render a `div` primitive with the appropriate styling for specific handled situations.
54
40
  */
55
- const CardConditionalContent = ({ role, children }: PropsWithChildren<{ role?: string }>) => {
56
- if (['popover', 'card--kanban'].includes(role ?? 'never')) {
41
+ const CardSurfaceRoot = ({ role = 'never', children }: PropsWithChildren<{ role?: string }>) => {
42
+ if (['popover', 'card--kanban'].includes(role)) {
57
43
  return (
58
44
  <div className={role === 'popover' ? 'popover-card-width' : role === 'card--kanban' ? 'contents' : ''}>
59
45
  {children}
@@ -61,9 +47,9 @@ const CardConditionalContent = ({ role, children }: PropsWithChildren<{ role?: s
61
47
  );
62
48
  } else {
63
49
  return (
64
- <CardContent {...(role === 'card--document' && { classNames: ['mlb-[1em]', hoverableControls] })}>
50
+ <CardStaticRoot {...(role === 'card--document' && { classNames: ['mlb-[1em]', hoverableControls] })}>
65
51
  {children}
66
- </CardContent>
52
+ </CardStaticRoot>
67
53
  );
68
54
  }
69
55
  };
@@ -84,7 +70,7 @@ const CardHeading = forwardRef<HTMLDivElement, SharedCardProps>(
84
70
 
85
71
  const CardToolbar = forwardRef<HTMLDivElement, ToolbarRootProps>(({ children, classNames, ...props }, forwardedRef) => {
86
72
  return (
87
- <Toolbar.Root {...props} classNames={['bg-transparent', classNames]} ref={forwardedRef}>
73
+ <Toolbar.Root {...props} classNames={['bg-transparent density-coarse', classNames]} ref={forwardedRef}>
88
74
  {children}
89
75
  </Toolbar.Root>
90
76
  );
@@ -101,7 +87,7 @@ const CardDragHandle = forwardRef<HTMLButtonElement, { toolbarItem?: boolean }>(
101
87
  iconOnly
102
88
  icon='ph--dots-six-vertical--regular'
103
89
  variant='ghost'
104
- label={t('card drag handle label')}
90
+ label={t('drag handle label')}
105
91
  classNames='pli-2'
106
92
  ref={forwardedRef}
107
93
  />
@@ -164,9 +150,8 @@ const CardText = forwardRef<HTMLParagraphElement, SharedCardProps>(
164
150
  );
165
151
 
166
152
  export const Card = {
167
- Root: CardRoot,
168
- Content: CardContent,
169
- Container: CardConditionalContent,
153
+ StaticRoot: CardStaticRoot,
154
+ SurfaceRoot: CardSurfaceRoot,
170
155
  Heading: CardHeading,
171
156
  Toolbar: CardToolbar,
172
157
  ToolbarIconButton: CardToolbarIconButton,
@@ -179,4 +164,4 @@ export const Card = {
179
164
  Text: CardText,
180
165
  };
181
166
 
182
- export { cardRoot, cardContent, cardHeading, cardText, cardChrome, cardSpacing };
167
+ export { cardRoot, cardHeading, cardText, cardChrome, cardSpacing };
@@ -6,14 +6,14 @@ import React, { type PropsWithChildren } from 'react';
6
6
 
7
7
  import { mx } from '@dxos/react-ui-theme';
8
8
 
9
- import { cardContent } from './fragments';
9
+ import { cardRoot } from './fragments';
10
10
 
11
11
  const CardDragPreviewRoot = ({ children }: PropsWithChildren<{}>) => {
12
12
  return <div className='p-2'>{children}</div>;
13
13
  };
14
14
 
15
15
  const CardDragPreviewContent = ({ children }: PropsWithChildren<{}>) => {
16
- return <div className={mx(cardContent, 'ring-focusLine ring-neutralFocusIndicator')}>{children}</div>;
16
+ return <div className={mx(cardRoot, 'ring-focusLine ring-neutralFocusIndicator')}>{children}</div>;
17
17
  };
18
18
 
19
19
  export const CardDragPreview = {
@@ -2,9 +2,7 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- export const cardRoot = 'contain-layout pli-2 plb-1 first-of-type:pbs-0 last-of-type:pbe-0';
6
-
7
- export const cardContent =
5
+ export const cardRoot =
8
6
  'rounded overflow-hidden bg-cardSurface border border-separator dark:border-subduedSeparator dx-focus-ring-group-y-indicator relative min-bs-[--rail-item] group/card';
9
7
 
10
8
  export const cardSpacing = 'pli-cardSpacingInline mlb-cardSpacingBlock';
@@ -0,0 +1,172 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import '@dxos-theme';
6
+
7
+ import { type Edge } from '@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge';
8
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
9
+ import React, { useState, useCallback } from 'react';
10
+
11
+ import { faker } from '@dxos/random';
12
+ import { IconButton } from '@dxos/react-ui';
13
+ import { withLayout, withTheme } from '@dxos/storybook-utils';
14
+
15
+ import { CardStack } from './CardStack';
16
+ import { StackItem } from '../../components';
17
+ import { Card, CardDragPreview } from '../Card';
18
+
19
+ // Set a seed for reproducible random values
20
+ faker.seed(0);
21
+
22
+ type CardItem = {
23
+ id: string;
24
+ title: string;
25
+ description: string;
26
+ image: string;
27
+ };
28
+
29
+ type StackItemData = {
30
+ id: string;
31
+ type?: 'column' | 'card';
32
+ };
33
+
34
+ const CardStackStory = () => {
35
+ const [column, setColumn] = useState<CardItem[]>(
36
+ faker.helpers.multiple(
37
+ () => ({
38
+ id: faker.string.uuid(),
39
+ title: faker.commerce.productName(),
40
+ description: faker.lorem.paragraph(),
41
+ image: faker.image.url(),
42
+ }),
43
+ { count: 12 },
44
+ ),
45
+ );
46
+
47
+ const handleRearrange = useCallback((source: StackItemData, target: StackItemData, closestEdge: Edge | null) => {
48
+ setColumn((prevColumn) => {
49
+ const newColumns = [...prevColumn];
50
+ // Reordering cards within a column
51
+ const sourceCardIndex = prevColumn.findIndex((card) => card.id === source.id);
52
+ const targetCardIndex = prevColumn.findIndex((card) => card.id === target.id);
53
+
54
+ if (typeof sourceCardIndex === 'number' && typeof targetCardIndex === 'number') {
55
+ const [movedCard] = newColumns.splice(sourceCardIndex, 1);
56
+
57
+ let insertIndex;
58
+ if (sourceCardIndex < targetCardIndex) {
59
+ insertIndex = closestEdge === 'bottom' ? targetCardIndex : targetCardIndex - 1;
60
+ } else {
61
+ insertIndex = closestEdge === 'bottom' ? targetCardIndex + 1 : targetCardIndex;
62
+ }
63
+ newColumns.splice(insertIndex, 0, movedCard);
64
+ }
65
+ return newColumns;
66
+ });
67
+ }, []);
68
+
69
+ const handleAddCard = useCallback(() => {
70
+ setColumn((prevColumn) => {
71
+ const newColumn = [...prevColumn];
72
+ const newCard = {
73
+ id: faker.string.uuid(),
74
+ title: faker.commerce.productName(),
75
+ description: faker.lorem.paragraph(),
76
+ image: faker.image.url(),
77
+ } satisfies CardItem;
78
+ newColumn.push(newCard);
79
+ console.log('[add card]', prevColumn.length, newColumn.length);
80
+ return newColumn;
81
+ });
82
+ }, []);
83
+
84
+ const handleRemoveCard = useCallback((cardId: string) => {
85
+ setColumn((prevColumn) => {
86
+ const newColumn = [...prevColumn];
87
+
88
+ const cardIndex = prevColumn.findIndex((card) => card.id === cardId);
89
+ if (cardIndex !== -1) {
90
+ newColumn.splice(cardIndex, 1);
91
+ }
92
+
93
+ return newColumn;
94
+ });
95
+ }, []);
96
+
97
+ return (
98
+ <CardStack.Root classNames='is-96'>
99
+ <CardStack.Content>
100
+ <CardStack.Stack id='story column' onRearrange={handleRearrange} itemsCount={column.length}>
101
+ {column.map((card, cardIndex, cardsArray) => {
102
+ const cardItem = { id: card.id, type: 'card' as const };
103
+ const prevCardId = cardIndex > 0 ? cardsArray[cardIndex - 1].id : undefined;
104
+ const nextCardId = cardIndex < cardsArray.length - 1 ? cardsArray[cardIndex + 1].id : undefined;
105
+
106
+ return (
107
+ <CardStack.Item asChild key={card.id}>
108
+ <StackItem.Root
109
+ item={cardItem}
110
+ focusIndicatorVariant='group'
111
+ prevSiblingId={prevCardId}
112
+ nextSiblingId={nextCardId}
113
+ >
114
+ <Card.StaticRoot>
115
+ <Card.Toolbar>
116
+ <StackItem.DragHandle asChild>
117
+ <Card.DragHandle toolbarItem />
118
+ </StackItem.DragHandle>
119
+ <Card.ToolbarSeparator variant='gap' />
120
+ <Card.ToolbarIconButton
121
+ iconOnly
122
+ variant='ghost'
123
+ icon='ph--x--regular'
124
+ label='Remove card'
125
+ onClick={() => handleRemoveCard(card.id)}
126
+ />
127
+ </Card.Toolbar>
128
+ <Card.Poster alt={card.title} image={card.image} />
129
+ <Card.Heading>{card.title}</Card.Heading>
130
+ <Card.Text classNames='line-clamp-2'>{card.description}</Card.Text>
131
+ </Card.StaticRoot>
132
+ <StackItem.DragPreview>
133
+ {({ item }) => (
134
+ <CardDragPreview.Root>
135
+ <CardDragPreview.Content>
136
+ <Card.Toolbar>
137
+ <Card.DragHandle toolbarItem />
138
+ </Card.Toolbar>
139
+ <Card.Poster alt={card.title} image={card.image} />
140
+ <Card.Heading>{card.title}</Card.Heading>
141
+ <Card.Text classNames='line-clamp-2'>{card.description}</Card.Text>
142
+ </CardDragPreview.Content>
143
+ </CardDragPreview.Root>
144
+ )}
145
+ </StackItem.DragPreview>
146
+ </StackItem.Root>
147
+ </CardStack.Item>
148
+ );
149
+ })}
150
+ </CardStack.Stack>
151
+
152
+ <CardStack.Footer>
153
+ <IconButton icon='ph--plus--regular' label='Add card' onClick={handleAddCard} classNames='is-full' />
154
+ </CardStack.Footer>
155
+
156
+ <CardStack.Heading>{faker.company.name()}</CardStack.Heading>
157
+ </CardStack.Content>
158
+ </CardStack.Root>
159
+ );
160
+ };
161
+
162
+ const meta: Meta<typeof CardStackStory> = {
163
+ title: 'ui/react-ui-stack/CardStack',
164
+ component: CardStackStory,
165
+ decorators: [withTheme, withLayout({ fullscreen: true })],
166
+ };
167
+
168
+ export default meta;
169
+
170
+ type Story = StoryObj<typeof CardStackStory>;
171
+
172
+ export const Default: Story = {};
@@ -107,6 +107,22 @@ const CardStackRoot = forwardRef<HTMLDivElement, SharedCardStackProps>(
107
107
  },
108
108
  );
109
109
 
110
+ const cardStackItem = 'contain-layout pli-2 plb-1 first-of-type:pbs-0 last-of-type:pbe-0';
111
+
112
+ const CardStackItem = forwardRef<HTMLDivElement, SharedCardStackProps>(
113
+ ({ children, classNames, asChild, role = 'none', ...props }, forwardedRef) => {
114
+ const Root = asChild ? Slot : 'div';
115
+ const rootProps = asChild
116
+ ? { classNames: [cardStackItem, classNames] }
117
+ : { className: mx(cardStackItem, classNames), role };
118
+ return (
119
+ <Root {...props} {...rootProps} ref={forwardedRef}>
120
+ {children}
121
+ </Root>
122
+ );
123
+ },
124
+ );
125
+
110
126
  export const CardStack = {
111
127
  Root: CardStackRoot,
112
128
  Content: CardStackContent,
@@ -114,6 +130,7 @@ export const CardStack = {
114
130
  Heading: CardStackHeading,
115
131
  Footer: CardStackFooter,
116
132
  DragHandle: CardStackDragHandle,
133
+ Item: CardStackItem,
117
134
  };
118
135
 
119
- export { cardStackRoot, cardStackFooter, cardStackHeading, cardStackContent };
136
+ export { cardStackRoot, cardStackFooter, cardStackHeading, cardStackContent, cardStackItem };
@@ -66,7 +66,10 @@ export const useStackDropForElements = ({
66
66
  }
67
67
  },
68
68
  }),
69
- autoScrollForElements({ element: scrollElement as Element, getAllowedAxis: () => orientation }),
69
+ autoScrollForElements({
70
+ element: scrollElement as Element,
71
+ getAllowedAxis: () => orientation,
72
+ }),
70
73
  );
71
74
  }, [element, scrollElement, selfDroppable, orientation, id, onRearrange]);
72
75
 
package/src/index.ts CHANGED
@@ -2,9 +2,8 @@
2
2
  // Copyright 2022 DXOS.org
3
3
  //
4
4
 
5
- export * from './components';
6
-
7
5
  // TODO(thure): Consider exporting exemplars from separate endpoints.
8
- export * from './exemplars';
9
6
 
10
- export { default as translations } from './translations';
7
+ export * from './components';
8
+ export * from './exemplars';
9
+ export * from './translations';
@@ -0,0 +1,17 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { defineConfig } from '@playwright/test';
6
+
7
+ import { e2ePreset } from '@dxos/test-utils/playwright';
8
+
9
+ export default defineConfig({
10
+ ...e2ePreset(import.meta.dirname),
11
+ // TODO(wittjosiah): Avoid hard-coding ports.
12
+ webServer: {
13
+ command: 'moon run storybook:serve-e2e -- --port=9003',
14
+ port: 9003,
15
+ reuseExistingServer: false,
16
+ },
17
+ });
@@ -8,10 +8,12 @@ import { setupPage, storybookUrl } from '@dxos/test-utils/playwright';
8
8
 
9
9
  import { StackManager } from '../testing';
10
10
 
11
+ const PORT = 9003;
12
+
11
13
  // TODO(wittjosiah): Update for new stack.
12
14
  test.describe.skip('Stack', () => {
13
15
  test('remove', async ({ browser }) => {
14
- const { page } = await setupPage(browser, { url: storybookUrl('ui-react-ui-stack-stack--transfer') });
16
+ const { page } = await setupPage(browser, { url: storybookUrl('ui-react-ui-stack-stack--transfer', PORT) });
15
17
  await page.getByTestId('stack-transfer').waitFor({ state: 'visible' });
16
18
 
17
19
  const stack = new StackManager(page.getByTestId('stack-1'));
@@ -24,7 +26,7 @@ test.describe.skip('Stack', () => {
24
26
  });
25
27
 
26
28
  test('rearrange', async ({ browser }) => {
27
- const { page } = await setupPage(browser, { url: storybookUrl('ui-react-ui-stack-stack--transfer') });
29
+ const { page } = await setupPage(browser, { url: storybookUrl('ui-react-ui-stack-stack--transfer', PORT) });
28
30
  await page.getByTestId('stack-transfer').waitFor({ state: 'visible' });
29
31
 
30
32
  const stack = new StackManager(page.getByTestId('stack-1'));
@@ -41,7 +43,7 @@ test.describe.skip('Stack', () => {
41
43
  test.skip();
42
44
  }
43
45
 
44
- const { page } = await setupPage(browser, { url: storybookUrl('ui-react-ui-stack-stack--transfer') });
46
+ const { page } = await setupPage(browser, { url: storybookUrl('ui-react-ui-stack-stack--transfer', PORT) });
45
47
  await page.getByTestId('stack-transfer').waitFor({ state: 'visible' });
46
48
 
47
49
  const stack1 = new StackManager(page.getByTestId('stack-1'));
@@ -61,7 +63,7 @@ test.describe.skip('Stack', () => {
61
63
  });
62
64
 
63
65
  test('copy', async ({ browser }) => {
64
- const { page } = await setupPage(browser, { url: storybookUrl('ui-react-ui-stack-stack--copy') });
66
+ const { page } = await setupPage(browser, { url: storybookUrl('ui-react-ui-stack-stack--copy', PORT) });
65
67
  await page.getByTestId('stack-copy').waitFor({ state: 'visible' });
66
68
 
67
69
  const stack1 = new StackManager(page.getByTestId('stack-1'));
@@ -2,9 +2,11 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- export const translationKey = 'stack';
5
+ import { type Resource } from '@dxos/react-ui';
6
6
 
7
- export default [
7
+ export const translationKey = 'react-ui-stack';
8
+
9
+ export const translations = [
8
10
  {
9
11
  'en-US': {
10
12
  [translationKey]: {
@@ -19,4 +21,4 @@ export default [
19
21
  },
20
22
  },
21
23
  },
22
- ];
24
+ ] as const satisfies Resource[];