@redis-ui/components 43.2.1 → 44.0.2

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 (86) hide show
  1. package/README.md +53 -429
  2. package/dist/Badge/Badge.types.cjs +1 -1
  3. package/dist/Badge/Badge.types.d.ts +1 -1
  4. package/dist/Badge/Badge.types.js +1 -1
  5. package/dist/Badge/BadgeIcon.cjs +1 -0
  6. package/dist/Badge/BadgeIcon.js +2 -1
  7. package/dist/BoxSelectionGroup/components/Item/components/Compose/Compose.style.cjs +1 -1
  8. package/dist/BoxSelectionGroup/components/Item/components/Compose/Compose.style.js +1 -1
  9. package/dist/Button/Button.style.cjs +4 -0
  10. package/dist/Button/Button.style.js +4 -0
  11. package/dist/ButtonGroup/components/Button/Button.style.cjs +1 -1
  12. package/dist/ButtonGroup/components/Button/Button.style.js +1 -1
  13. package/dist/Drawer/Drawer.cjs +12 -2
  14. package/dist/Drawer/Drawer.js +12 -2
  15. package/dist/Drawer/components/Content/Content.style.cjs +1 -1
  16. package/dist/Drawer/components/Content/Content.style.js +1 -1
  17. package/dist/Drawer/components/Overlay/Overlay.style.cjs +1 -1
  18. package/dist/Drawer/components/Overlay/Overlay.style.js +1 -1
  19. package/dist/Drawer/hooks/useReducedMotionLifecycleSync.cjs +41 -0
  20. package/dist/Drawer/hooks/useReducedMotionLifecycleSync.d.ts +15 -0
  21. package/dist/Drawer/hooks/useReducedMotionLifecycleSync.js +41 -0
  22. package/dist/HighlightedIcon/HighlightedIcon.cjs +2 -2
  23. package/dist/HighlightedIcon/HighlightedIcon.d.ts +1 -1
  24. package/dist/HighlightedIcon/HighlightedIcon.js +2 -2
  25. package/dist/HighlightedIcon/HighlightedIcon.style.cjs +6 -6
  26. package/dist/HighlightedIcon/HighlightedIcon.style.d.ts +2 -2
  27. package/dist/HighlightedIcon/HighlightedIcon.style.js +6 -6
  28. package/dist/HighlightedIcon/HighlightedIcon.types.d.ts +2 -2
  29. package/dist/Inputs/components/Compose/Compose.style.cjs +1 -1
  30. package/dist/Inputs/components/Compose/Compose.style.js +1 -1
  31. package/dist/Loader/Loader.cjs +2 -2
  32. package/dist/Loader/Loader.js +2 -2
  33. package/dist/Loader/Loader.types.d.ts +2 -1
  34. package/dist/Loader/index.d.ts +1 -0
  35. package/dist/Menu/components/Trigger/components/Arrow/Arrow.style.cjs +1 -1
  36. package/dist/Menu/components/Trigger/components/Arrow/Arrow.style.js +1 -1
  37. package/dist/Popover/components/Content/components/WindowBar/WindowBar.types.d.ts +3 -2
  38. package/dist/Popover/components/Content/components/WindowBar/components/Compose/Compose.style.d.ts +2 -3
  39. package/dist/Popover/components/Content/components/WindowBar/components/Compose/Compose.types.d.ts +2 -2
  40. package/dist/ProgressBar/ProgressBar.style.cjs +1 -1
  41. package/dist/ProgressBar/ProgressBar.style.js +1 -1
  42. package/dist/SearchBar/components/FiltersSection/FiltersSection.style.cjs +1 -1
  43. package/dist/SearchBar/components/FiltersSection/FiltersSection.style.js +1 -1
  44. package/dist/SearchBar/components/SearchSection/components/Actions/components/ColumnSelector/ColumnSelector.cjs +2 -0
  45. package/dist/SearchBar/components/SearchSection/components/Actions/components/ColumnSelector/ColumnSelector.js +2 -0
  46. package/dist/Section/components/Header/components/CollapseButton/CollapseButton.style.cjs +1 -1
  47. package/dist/Section/components/Header/components/CollapseButton/CollapseButton.style.js +1 -1
  48. package/dist/Select/components/Trigger/components/Arrow/Arrow.style.cjs +1 -1
  49. package/dist/Select/components/Trigger/components/Arrow/Arrow.style.js +1 -1
  50. package/dist/Select/components/Trigger/components/Compose/Compose.style.cjs +1 -1
  51. package/dist/Select/components/Trigger/components/Compose/Compose.style.js +1 -1
  52. package/dist/SideBar/SideBar.cjs +3 -1
  53. package/dist/SideBar/SideBar.js +3 -1
  54. package/dist/SideBar/SideBar.style.cjs +1 -1
  55. package/dist/SideBar/SideBar.style.js +1 -1
  56. package/dist/SideBar/components/Button/Button.style.cjs +3 -3
  57. package/dist/SideBar/components/Button/Button.style.js +3 -3
  58. package/dist/SideBar/components/Divider/Divider.style.cjs +1 -1
  59. package/dist/SideBar/components/Divider/Divider.style.js +1 -1
  60. package/dist/SideBar/components/Header/Header.style.cjs +4 -0
  61. package/dist/SideBar/components/Header/Header.style.js +4 -0
  62. package/dist/SideBar/components/Item/Item.style.cjs +1 -1
  63. package/dist/SideBar/components/Item/Item.style.js +1 -1
  64. package/dist/SideBar/components/Item/components/Text/Text.style.cjs +1 -1
  65. package/dist/SideBar/components/Item/components/Text/Text.style.js +1 -1
  66. package/dist/SideBar/components/SideBarLogo/SideBarLogo.style.cjs +1 -1
  67. package/dist/SideBar/components/SideBarLogo/SideBarLogo.style.js +1 -1
  68. package/dist/Stepper/components/Step/components/Icon/Icon.style.cjs +1 -1
  69. package/dist/Stepper/components/Step/components/Icon/Icon.style.js +1 -1
  70. package/dist/Switch/components/Switcher/Switcher.style.cjs +3 -0
  71. package/dist/Switch/components/Switcher/Switcher.style.js +3 -0
  72. package/dist/ThemeModeSwitch/ThemeModeSwitch.style.cjs +1 -1
  73. package/dist/ThemeModeSwitch/ThemeModeSwitch.style.js +1 -1
  74. package/dist/Toast/Toaster.cjs +30 -4
  75. package/dist/Toast/Toaster.js +30 -4
  76. package/dist/Toast/Toaster.style.cjs +1 -1
  77. package/dist/Toast/Toaster.style.js +1 -1
  78. package/dist/Toast/components/ReducedMotionTransition/ReducedMotionTransition.cjs +22 -0
  79. package/dist/Toast/components/ReducedMotionTransition/ReducedMotionTransition.d.ts +3 -0
  80. package/dist/Toast/components/ReducedMotionTransition/ReducedMotionTransition.js +22 -0
  81. package/dist/Toast/core/mapping.helpers.cjs +9 -0
  82. package/dist/Toast/core/mapping.helpers.js +9 -0
  83. package/package.json +3 -3
  84. package/skills/redis-ui-components/references/Loader.md +14 -8
  85. package/skills/redis-ui-components/references/Popover.md +1 -1
  86. package/skills/redis-ui-components/references/Tooltip.md +1 -1
package/README.md CHANGED
@@ -1,455 +1,79 @@
1
- # Creating New Components Guide
1
+ # @redis-ui/components
2
2
 
3
- This guide covers best practices, conventions, and patterns for creating new components in the `@redislabsdev/redis-ui-components` library.
3
+ The Redis UI component library a themed, accessible React component library built on [styled-components](https://styled-components.com/) and [Radix UI](https://www.radix-ui.com/).
4
4
 
5
- ## Table of Contents
5
+ ## Installation
6
6
 
7
- - [Radix UI Foundation](#radix-ui-foundation)
8
- - [Component Structure](#component-structure)
9
- - [File Organization](#file-organization)
10
- - [Component Composition](#component-composition)
11
- - [Testing](#testing)
7
+ Install the components package and its required peer dependencies:
12
8
 
13
- ## Radix UI Foundation
14
-
15
- This component library is built on top of [Radix UI](https://www.radix-ui.com/), a low-level UI component library that provides unstyled, accessible primitives for building high-quality design systems and web applications.
16
-
17
- > **Note:** Not all components need Radix. Simple presentational components (like Button, Badge, Card) can be built from scratch using styled-components.
18
-
19
- > **Note:** When using Radix, or any other external library for that matter, make sure you don't expose Radix types in public API. Always wrap them in our own types.
20
-
21
- ## Component Structure
22
-
23
- ### Basic Component Anatomy
24
-
25
- Every component should follow this folder structure:
26
-
27
- ```
28
- src/ComponentName/
29
- ├── ComponentName.tsx # Main component implementation
30
- ├── ComponentName.types.ts # Types and interfaces
31
- ├── ComponentName.style.ts # Styles
32
- ├── ComponentName.test.tsx # Unit tests
33
- ├── index.ts # Public exports
34
- └── components/ # Sub-components (if needed)
35
- └── SubComponent/
36
- ├── SubComponent.tsx
37
- ├── SubComponent.types.ts
38
- └── SubComponent.style.ts
39
- ```
40
-
41
- The tree is recurisve, so you can have sub-components inside sub-components, etc.
42
-
43
- ### Example: Simple Component
44
-
45
- ```tsx
46
- // Card.tsx
47
- import * as S from './Card.style';
48
- import { CardProps } from './Card.types';
49
-
50
- const Card = (props: CardProps) => <S.Card {...props} />;
51
-
52
- export default Card;
53
-
54
- ```
55
-
56
- ---
57
-
58
- ## File Organization
59
-
60
- ### 1. Component File (`ComponentName.tsx`)
61
-
62
- **✅ DO:**
63
- - Use `React.forwardRef` when the component should allow access to the DOM element underneath
64
- - Use `forwardRefWithGenerics`, if component has generics params (also works as standard `forwardRef`)
65
- - Provide default props for optional parameters
66
- - Import styled components as `* as S` for namespacing
67
- - Keep the main component focused and delegate to sub-components
68
- - Extract complex logic into separate hooks, separate different logic to separate hooks
69
-
70
- **❌ DON'T:**
71
- - Mix business logic with presentation
72
- - Hardcode theme values
73
-
74
- ### 2. Types File (`ComponentName.types.ts`)
75
-
76
- **Conventions:**
77
- - Always use separate file for types
78
- - Extend HTML element attributes when wrapping native elements
79
- - Use generics for flexible, reusable components
80
- - If there are sub-components, create separate types file for each of them and compose the styles at the root level
81
- - Use fully qualified names for exported types
82
- - It is preferable to split component props into `Own` and `Rest` props,
83
- which can simplify property composition and their use in special cases.
84
- - Use `Own*Props` primarily to define actively processed props and props for direct inheritance
85
- - Use `Rest*Props` for other props that you plan to pass as-is to inner components (e.g. HTMLAttributes<>)
86
-
87
- ```tsx
88
- // Button.types.ts
89
- import { ButtonHTMLAttributes } from 'react';
90
-
91
- export const buttonSizes = ['large', 'medium', 'small'] as const;
92
- export const buttonVariants = ['primary', 'destructive', 'secondary-fill'] as const;
93
-
94
- export type ButtonSizes = (typeof buttonSizes)[number];
95
- export type ButtonVariants = (typeof buttonVariants)[number];
96
-
97
- export type OwnButtonProps = {
98
- size?: ButtonSizes;
99
- variant?: ButtonVariants;
100
- disabled?: boolean;
101
- };
102
-
103
- export type RestButtonProps = ButtonHTMLAttributes<HTMLButtonElement>;
104
-
105
- export type ButtonProps = OwnButtonProps & RestButtonProps;
9
+ ```bash
10
+ npm install @redis-ui/components @redis-ui/styles @redis-ui/icons styled-components
106
11
  ```
107
12
 
108
- ### 3. Style File (`ComponentName.style.ts`)
109
-
110
- **✅ DO:**
111
- - Access theme values via `useTheme().components.componentName`
112
- - Use token maps for consistent styling (`styleFromTokens`)
113
- - Use fully qualified names for exported styled components (simplifies component debugging with React DevTools)
114
-
115
- ```tsx
116
- // Button.style.ts
117
- import styled, { css } from 'styled-components/macro';
118
- import { getFocusStyle, useTheme } from '@redislabsdev/redis-ui-styles';
119
- import { ButtonProps } from './Button.types';
120
-
121
- export const baseButtonStyle = css`
122
- border: 0;
123
- display: inline-flex;
124
- justify-content: center;
125
- align-items: center;
126
- cursor: pointer;
13
+ ## Setup
127
14
 
128
- ${getFocusStyle()};
15
+ ### 1. Import global styles
129
16
 
130
- &:disabled {
131
- cursor: not-allowed;
132
- }
133
- `;
17
+ Import the normalized styles and fonts from `@redis-ui/styles` in your app entry point:
134
18
 
135
- export const Button = styled.button<Required<Pick<ButtonProps, 'size' | 'variant'>>>`
136
- ${baseButtonStyle};
137
- gap: ${() => useTheme().components.button.gap};
138
- padding: ${({ size }) => useTheme().components.button.sizes[size].padding};
139
- background-color: ${({ variant }) => useTheme().components.button.variants[variant].bgColor};
140
- `;
19
+ ```ts
20
+ import '@redis-ui/styles/normalized-styles.css';
21
+ import '@redis-ui/styles/fonts.css';
141
22
  ```
142
23
 
143
- **❌ DON'T:**
144
- - Hardcode colors, spacing, or other design tokens
145
- - Use inline styles
24
+ ### 2. Provide the theme and render global styles
146
25
 
147
- ### 4. Index File (`index.ts`)
26
+ Components read all design tokens (colors, spacing, typography, etc.) from a styled-components `ThemeProvider`. You **must** wrap your application in one — without it, components will not render correctly.
148
27
 
149
- **Conventions:**
150
- - Export the default component
151
- - Export all types using `export *`
152
- - Export utility hooks/functions if applicable
153
- - **Do not** export styled components
154
-
155
- ## Component Composition
156
-
157
- > **Note:** It’s best to discuss whether composition is appropriate early on, ideally during the design phase. By the time a PR is under review, changing this decision will involve substantial changes. Feel free to reach out to FE Infra team for advice.
158
-
159
- **Benefits:**
160
- - Allows both simple and advanced usage patterns.
161
- - Note that composed components should also provide simple props for non-advanced usage.
162
- - Provides flexibility for custom layouts
163
- - Maintains clean API surface
164
- - Supports core Design System and allows custom application styling
165
-
166
- ### Object.assign Pattern
167
-
168
- For components with sub-components and composition hierarchy, use `Object.assign`:
28
+ You also need to render the `CommonStyles` global style component inside the theme provider. It applies theme-aware base styles to the body, scrollbars, and reduced-motion preferences.
169
29
 
170
30
  ```tsx
171
- // Tabs.tsx
172
- import { TabsProps } from './Tabs.types';
173
- import ContentPane from './components/ContentPane/ContentPane';
174
- import TabBar from './components/TabBar/TabBar';
175
- import Compose from './components/Compose/Compose';
176
-
177
- const Tabs = Object.assign(
178
- ({ tabs, variant, ...restProps }: TabsProps) => (
179
- <Tabs.Compose {...restProps}>
180
- <Tabs.TabBar tabs={tabs} variant={variant} />
181
- <Tabs.ContentPane tabs={tabs} />
182
- </Tabs.Compose>
183
- ),
184
- { Compose, TabBar, ContentPane }
185
- );
31
+ import { ThemeProvider } from 'styled-components';
32
+ import { themesRebrand, CommonStyles } from '@redis-ui/styles';
186
33
 
187
- export default Tabs;
34
+ function App() {
35
+ return (
36
+ <ThemeProvider theme={themesRebrand.light}>
37
+ <CommonStyles />
38
+ {/* your app */}
39
+ </ThemeProvider>
40
+ );
41
+ }
188
42
  ```
189
43
 
190
- **Benefits:**
191
- - The `assign()` function works better with TypeScript and code completion than direct assignment.
192
-
193
- ### Context Pattern
44
+ #### Dark mode support
194
45
 
195
- For sharing state and props between component and descendants.
46
+ For automatic light/dark mode switching with persistence, use the `SwitchableModeThemeProvider`:
196
47
 
197
- Create component contexts in separate file: `ComponentName.context.ts(x)`
198
-
199
- For most cases you can use standard component context generator with first two returned values: context hook and provider
200
48
  ```tsx
201
- import { createNullableContext } from '/Helpers';
49
+ import { SwitchableModeThemeProvider, themesRebrand, CommonStyles } from '@redis-ui/styles';
202
50
 
203
- export const [useMyComponentContext, MyComponentContextProvider, MyComponentContext] =
204
- createNullableContext<MyComponentContextType>({
205
- forComponentName: 'MyComponent'
206
- });
51
+ function App() {
52
+ return (
53
+ <SwitchableModeThemeProvider
54
+ lightTheme={themesRebrand.light}
55
+ darkTheme={themesRebrand.dark}
56
+ storageKey="redis-ui-theme-mode"
57
+ >
58
+ <CommonStyles />
59
+ {/* your app */}
60
+ </SwitchableModeThemeProvider>
61
+ );
62
+ }
207
63
  ```
208
64
 
209
- There is also primitive value state context generator (see usages for details.). It generates 3 things (each of them allows update state):
210
- - Context hook, returning the state
211
- - Context Provider, holding the state
212
- - TransProvider, which acts as the Provider if there is no ascending Provider, or becomes transparent if there is one.
65
+ ### 3. Use components
213
66
 
214
67
  ```tsx
215
- import { createPrimitiveContextState } from '/Helpers';
216
-
217
- export const [useFieldStatus, FieldStatusProvider, FieldStatusTransProvider] =
218
- createPrimitiveContextState<FieldStatus>();
219
- ```
220
-
221
- If none of the generators meets the requirements, a custom context can be created based on the regular React Context.<br/>
222
- In this case, export the Provider and a hook that retrieves, validates, processes the context values and returns the result.
223
-
224
- ```tsx
225
- // Component.context.ts
226
- import { createContext, useContext } from 'react';
227
-
228
- export const ComponentContext = createContext<ComponentContextParams>({
229
- variant: 'default',
230
- size: 'medium'
231
- });
232
-
233
- export const useComponentParams = (
234
- customParams: Partial<ComponentContextParams> = {}
235
- ): ComponentContextParams => {
236
- const context = useContext(ComponentContext);
237
- return customParams ? assignWith({}, context, customParams, skipUndefined) : context;
238
- };
239
- ```
240
-
241
- **✅ DO:**
242
- - Use context for deeply nested component communication
243
- - Memoize context values to prevent unnecessary re-renders
244
- - Provide sensible defaults
245
-
246
- **❌ DON'T:**
247
- - Overuse context for simple prop drilling
248
- - Forget to memoize context values
249
-
250
- ### `Compose` pattern
251
-
252
- Start component life from pure composition tree.
253
-
254
- ```tsx
255
- <Tabs.Compose value={value} onChange={onChange}>
256
- <Tabs.TabBar.Compose>
257
- {tabs.map(({ value, label, ...rest }) => (
258
- <Tabs.TabBar.Tab key={value} value={value} {...rest}>
259
- {label}
260
- </Tabs.TabBar.Tab>
261
- ))}
262
- <Tabs.TabBar.Marker/>
263
- </Tabs.TabBar.Compose>
264
- <Tabs.ContentPane.Compose>
265
- {tabs.map(({ value, content }) => (
266
- <Tabs.ContentPane.Content key={value} value={value}>
267
- {content}
268
- </Tabs.ContentPane.Content>
269
- ))}
270
- </Tabs.ContentPane.Compose>
271
- </Tabs.Compose>
272
- ```
273
-
274
- Each non-leaf node in the tree should be `Compose` node.
275
-
276
- `Compose` component receives configuration props, defines states, contexts and core logic and optionally adds container, styled with DS.
277
-
278
- Leaf components remain pure renderers, reading context and their own props.
279
-
280
- Place components in directory tree, where each node component has its own directory and all sub-components are placed in a `components` directory within the component directory
281
-
282
- When composition is ready, wrap each Compose component and sub-components of the level with node component, which exposes simple configuration API
283
-
284
- `Compose` and nested components should be assigned to the node component (and used in composition in it).
285
-
286
- ```tsx
287
- const Tabs = Object.assign(
288
- ({ tabs, ...restProps }: TabsProps) => (
289
- <Tabs.Compose {...restProps}>
290
- <Tabs.TabBar tabs={tabs} />
291
- <Tabs.ContentPane tabs={tabs} />
292
- </Tabs.Compose>
293
- ),
294
- { Compose, TabBar, ContentPane }
295
- );
296
- ```
297
-
298
- **✅ DO:**
299
- - Define context state and logic (policy/behavior/rules) only in `Compose`
300
- - Keep nested and especially leaf components focused
301
- - Keep node components simple (they should only contain simple composition using `Compose` and nested components)
302
- - When using Radix library, use their node components in `Compose` and their leaf components in leaf
303
- - Extract and separate complex logic into separate hooks
304
-
305
- **❌ DON'T:**
306
- - Add logic, contexts, states to node components
307
- - Make leaf components overcomplicated
308
- - Overload composition hierarchy
309
-
310
- **Benefits:**
311
- - Configuration logic doesn’t leak
312
- - Nested components stay focused (“lego bricks”)
313
- - Flexibility
314
- - Each NodeComponent works as configuration entry point
315
- - Each node can be extracted/rearranged at any level safely
316
- - Simple by default, powerful when needed
317
- - node components limit configuration options to what’s most necessary
318
- - if you need more tuning — go to composition
319
- - DS-based by default, restylable via composition
320
- - Consumers can build custom composition nodes that still respect invariants
321
-
322
- ### Form Fields
323
-
324
- If you're developing another form component that can be used with the `FormField` component,
325
- be sure to connect it with the `FormField` contexts (those that are applicable)
326
- - SharedId
327
- - FieldStatus
328
- - FieldRequired
329
- - FieldDisabled
330
- - FieldReadonly
331
- - FieldAdditionText
332
-
333
- ## Testing
334
-
335
- ### Test File Structure
336
-
337
- ```tsx
338
- // Component.test.tsx
339
- import { render, screen } from '@testing-library/react';
340
- import Component from './Component';
341
-
342
- describe('Component', () => {
343
- describe('Basic rendering', () => {
344
- it('should render with default props', () => {
345
- render(<Component>Test content</Component>);
346
- expect(screen.getByText('Test content')).toBeInTheDocument();
347
- });
348
- });
349
-
350
- describe('Variants', () => {
351
- it('should render primary variant', () => {
352
- render(<Component variant="primary">Primary</Component>);
353
- expect(screen.getByText('Primary')).toBeInTheDocument();
354
- });
355
- });
356
-
357
- describe('Composition mode', () => {
358
- it('should render with composition components', () => {
359
- render(
360
- <Component.Compose>
361
- <Component.Header>Header</Component.Header>
362
- <Component.Body>Body</Component.Body>
363
- </Component.Compose>
364
- );
365
- expect(screen.getByText('Header')).toBeInTheDocument();
366
- });
367
- });
368
- });
369
- ```
370
-
371
- ### **Testing Library Queries**
372
-
373
- Prefer queries in this order:
374
- 1. `getByRole` (most accessible)
375
- 2. `getByLabelText`
376
- 3. `getByPlaceholderText`
377
- 4. `getByText`
378
- 5. `getByTestId` (last resort)
379
-
380
- You can find more info in [Testing Library docs](https://testing-library.com/docs/queries/about/).
381
-
382
- ## Tips & Tricks
383
-
384
- ### 1. **Transient Props in Styled Components**
385
-
386
- Use `$` prefix for transient props:
387
-
388
- ```tsx
389
- // ✅ Good - won't pass $variant to DOM
390
- const Button = styled.button<{ $variant: string }>`
391
- color: ${({ $variant }) => $variant === 'primary' ? 'blue' : 'gray'};
392
- `;
393
-
394
- // ❌ Bad - will pass variant to DOM and can cause warnings
395
- const Button = styled.button<{ variant: string }>`
396
- color: ${({ variant }) => variant === 'primary' ? 'blue' : 'gray'};
397
- `;
398
- ```
399
-
400
- ### 2. **Theme Type Safety**
401
-
402
- Create theme types for your component in `packages/styles`:
403
-
404
- ```tsx
405
- // packages/styles/src/themes/types/theme/components/myComponent.types.ts
406
- export type MyComponentTheme = {
407
- padding: string;
408
- gap: string;
409
- variants: {
410
- primary: { bgColor: string; textColor: string };
411
- secondary: { bgColor: string; textColor: string };
412
- };
413
- };
414
- ```
415
-
416
- Then add to all theme files (light, dark, light2, dark2).
417
-
418
- ### 3. **Composition Helpers**
419
-
420
- Use helper types from `Helpers`:
421
-
422
- ```tsx
423
- import { ComposeElementProps, ChildFree } from '../Helpers';
424
-
425
- // ComposeElementProps: Standard props for composition components
426
- // ChildFree: Omits children from props
427
- type MyProps = ChildFree<ComposeElementProps>;
428
- ```
429
-
430
- ### 4. **Storybook Integration**
431
-
432
- Create stories in `docs/stories/ComponentName/`:
433
-
434
- ```tsx
435
- // ComponentName.stories.tsx
436
- import { Meta, StoryObj } from '@storybook/react';
437
- import { ComponentName } from '@redislabsdev/redis-ui-components';
438
-
439
- const meta: Meta<typeof ComponentName> = {
440
- component: ComponentName,
441
- title: 'Components/ComponentName'
442
- };
443
-
444
- export default meta;
445
- type Story = StoryObj<typeof ComponentName>;
446
-
447
- export const Playground: Story = {
448
- args: {
449
- variant: 'primary',
450
- size: 'medium'
451
- }
452
- };
453
- ```
454
-
455
- > **Note:** For composition components, create a separate `ComponentNameCompose.mdx` file.
68
+ import { Button } from '@redis-ui/components';
69
+
70
+ function MyPage() {
71
+ return (
72
+ <div>
73
+ <Button variant="primary" size="medium">
74
+ Click me
75
+ </Button>
76
+ </div>
77
+ );
78
+ }
79
+ ```
@@ -1,4 +1,4 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const badgeVariants = ["white", "light", "default", "informative", "success", "notice", "danger", "attention", "disabled"];
3
+ const badgeVariants = ["white", "light", "default", "informative", "success", "notice", "danger", "attention", "upsell", "disabled"];
4
4
  exports.badgeVariants = badgeVariants;
@@ -1,6 +1,6 @@
1
1
  import { IconType } from '@redislabsdev/redis-ui-icons';
2
2
  import { Theme } from '@redislabsdev/redis-ui-styles';
3
- export declare const badgeVariants: readonly ["white", "light", "default", "informative", "success", "notice", "danger", "attention", "disabled"];
3
+ export declare const badgeVariants: readonly ["white", "light", "default", "informative", "success", "notice", "danger", "attention", "upsell", "disabled"];
4
4
  export type BadgeVariants = (typeof badgeVariants)[number];
5
5
  export interface BadgeProps extends React.HTMLAttributes<HTMLDivElement> {
6
6
  label: string;
@@ -1,4 +1,4 @@
1
- const badgeVariants = ["white", "light", "default", "informative", "success", "notice", "danger", "attention", "disabled"];
1
+ const badgeVariants = ["white", "light", "default", "informative", "success", "notice", "danger", "attention", "upsell", "disabled"];
2
2
  export {
3
3
  badgeVariants
4
4
  };
@@ -11,6 +11,7 @@ const BADGE_ICON_RECORD = {
11
11
  notice: redisUiIcons.ToastNotificationIcon,
12
12
  danger: redisUiIcons.ToastDangerIcon,
13
13
  attention: redisUiIcons.ToastPendingIcon,
14
+ upsell: redisUiIcons.CrownIcon,
14
15
  disabled: redisUiIcons.ToastCheckIcon
15
16
  };
16
17
  const BadgeIcon = ({
@@ -1,5 +1,5 @@
1
1
  import { j as jsxRuntimeExports } from "../node_modules/react/jsx-runtime.js";
2
- import { ToastCheckIcon, ToastInfoIcon, ToastNotificationIcon, ToastDangerIcon, ToastPendingIcon } from "@redislabsdev/redis-ui-icons";
2
+ import { ToastCheckIcon, ToastInfoIcon, ToastNotificationIcon, ToastDangerIcon, ToastPendingIcon, CrownIcon } from "@redislabsdev/redis-ui-icons";
3
3
  const BADGE_ICON_RECORD = {
4
4
  white: ToastCheckIcon,
5
5
  light: ToastCheckIcon,
@@ -9,6 +9,7 @@ const BADGE_ICON_RECORD = {
9
9
  notice: ToastNotificationIcon,
10
10
  danger: ToastDangerIcon,
11
11
  attention: ToastPendingIcon,
12
+ upsell: CrownIcon,
12
13
  disabled: ToastCheckIcon
13
14
  };
14
15
  const BadgeIcon = ({
@@ -59,7 +59,7 @@ const getStateStyle = (theme, isChecked = false) => {
59
59
  const BoxSelectionGroupItemCompose = _styled__default.default(index.Root).withConfig({
60
60
  displayName: "Composestyle__BoxSelectionGroupItemCompose",
61
61
  componentId: "RedisUI__sc-17mo3lu-1"
62
- })(["align-items:unset;justify-content:center;line-height:1.25;text-align:unset;position:relative;padding-inline:0;padding-block:0;flex:1;white-space:nowrap;cursor:", ";overflow:hidden;transition:all 0.1s;border-radius:", ";border-width:", ";border-style:", ";", ";", ";"], ({
62
+ })(["align-items:unset;justify-content:center;line-height:1.25;text-align:unset;position:relative;padding-inline:0;padding-block:0;flex:1;white-space:nowrap;cursor:", ";overflow:hidden;transition:all 0.1s;@media (prefers-reduced-motion:reduce){transition:none;}border-radius:", ";border-width:", ";border-style:", ";", ";", ";"], ({
63
63
  disabled
64
64
  }) => !disabled ? "pointer" : "not-allowed", () => redisUiStyles.useTheme().components.boxSelectionGroup.item.borderRadius, () => redisUiStyles.useTheme().components.boxSelectionGroup.item.borderWidth, () => redisUiStyles.useTheme().components.boxSelectionGroup.item.borderStyle, ({
65
65
  pressed
@@ -55,7 +55,7 @@ const getStateStyle = (theme, isChecked = false) => {
55
55
  const BoxSelectionGroupItemCompose = _styled(Root).withConfig({
56
56
  displayName: "Composestyle__BoxSelectionGroupItemCompose",
57
57
  componentId: "RedisUI__sc-17mo3lu-1"
58
- })(["align-items:unset;justify-content:center;line-height:1.25;text-align:unset;position:relative;padding-inline:0;padding-block:0;flex:1;white-space:nowrap;cursor:", ";overflow:hidden;transition:all 0.1s;border-radius:", ";border-width:", ";border-style:", ";", ";", ";"], ({
58
+ })(["align-items:unset;justify-content:center;line-height:1.25;text-align:unset;position:relative;padding-inline:0;padding-block:0;flex:1;white-space:nowrap;cursor:", ";overflow:hidden;transition:all 0.1s;@media (prefers-reduced-motion:reduce){transition:none;}border-radius:", ";border-width:", ";border-style:", ";", ";", ";"], ({
59
59
  disabled
60
60
  }) => !disabled ? "pointer" : "not-allowed", () => useTheme().components.boxSelectionGroup.item.borderRadius, () => useTheme().components.boxSelectionGroup.item.borderWidth, () => useTheme().components.boxSelectionGroup.item.borderStyle, ({
61
61
  pressed
@@ -18,6 +18,10 @@ const baseButtonStyle = _styled.css`
18
18
  transition: all 130ms ease-in-out, outline-offset 0ms, outline 0ms;
19
19
  cursor: pointer;
20
20
 
21
+ @media (prefers-reduced-motion: reduce) {
22
+ transition: none;
23
+ }
24
+
21
25
  ${redisUiStyles.getFocusStyle()};
22
26
 
23
27
  &:disabled {
@@ -14,6 +14,10 @@ const baseButtonStyle = css`
14
14
  transition: all 130ms ease-in-out, outline-offset 0ms, outline 0ms;
15
15
  cursor: pointer;
16
16
 
17
+ @media (prefers-reduced-motion: reduce) {
18
+ transition: none;
19
+ }
20
+
17
21
  ${getFocusStyle()};
18
22
 
19
23
  &:disabled {
@@ -9,7 +9,7 @@ const _styled__default = /* @__PURE__ */ _interopDefault(_styled);
9
9
  const Button = _styled__default.default.button.withConfig({
10
10
  displayName: "Buttonstyle__Button",
11
11
  componentId: "RedisUI__sc-m6kucg-0"
12
- })(["cursor:pointer;border:none;font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;line-height:1.25;max-width:100%;min-width:", ";text-align:center;text-decoration:none;transition:color 0.13s ease-in-out,background-color 0.13s ease-in-out,border-color 0.13s ease-in-out,box-shadow 0.13s ease-in-out;user-select:none;vertical-align:middle;white-space:nowrap;display:flex;border-radius:", ";padding:", ";justify-content:center;align-items:center;gap:", ";&:not(:last-child){border-inline-end:", ";}&:disabled{cursor:not-allowed;}", " ", ";"], () => redisUiStyles.useTheme().components.buttonGroup.button.minWidth, () => redisUiStyles.useTheme().components.buttonGroup.button.borderRadius, () => redisUiStyles.useTheme().components.buttonGroup.button.padding, () => redisUiStyles.useTheme().components.buttonGroup.button.gap, () => redisUiStyles.useTheme().components.buttonGroup.button.border, ({
12
+ })(["cursor:pointer;border:none;font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;line-height:1.25;max-width:100%;min-width:", ";text-align:center;text-decoration:none;transition:color 0.13s ease-in-out,background-color 0.13s ease-in-out,border-color 0.13s ease-in-out,box-shadow 0.13s ease-in-out;@media (prefers-reduced-motion:reduce){transition:none;}user-select:none;vertical-align:middle;white-space:nowrap;display:flex;border-radius:", ";padding:", ";justify-content:center;align-items:center;gap:", ";&:not(:last-child){border-inline-end:", ";}&:disabled{cursor:not-allowed;}", " ", ";"], () => redisUiStyles.useTheme().components.buttonGroup.button.minWidth, () => redisUiStyles.useTheme().components.buttonGroup.button.borderRadius, () => redisUiStyles.useTheme().components.buttonGroup.button.padding, () => redisUiStyles.useTheme().components.buttonGroup.button.gap, () => redisUiStyles.useTheme().components.buttonGroup.button.border, ({
13
13
  isSelected
14
14
  }) => Button_style_utils.getButtonStatesStyle(redisUiStyles.useTheme().components.buttonGroup.button.toggleStates[isSelected ? "on" : "off"], [css_utils.tokenMaps.bgColor, css_utils.tokenMaps.textColor]), redisUiStyles.getFocusStyle("-2px"));
15
15
  exports.Button = Button;
@@ -5,7 +5,7 @@ import { tokenMaps } from "../../../Helpers/css.utils.js";
5
5
  const Button = _styled.button.withConfig({
6
6
  displayName: "Buttonstyle__Button",
7
7
  componentId: "RedisUI__sc-m6kucg-0"
8
- })(["cursor:pointer;border:none;font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;line-height:1.25;max-width:100%;min-width:", ";text-align:center;text-decoration:none;transition:color 0.13s ease-in-out,background-color 0.13s ease-in-out,border-color 0.13s ease-in-out,box-shadow 0.13s ease-in-out;user-select:none;vertical-align:middle;white-space:nowrap;display:flex;border-radius:", ";padding:", ";justify-content:center;align-items:center;gap:", ";&:not(:last-child){border-inline-end:", ";}&:disabled{cursor:not-allowed;}", " ", ";"], () => useTheme().components.buttonGroup.button.minWidth, () => useTheme().components.buttonGroup.button.borderRadius, () => useTheme().components.buttonGroup.button.padding, () => useTheme().components.buttonGroup.button.gap, () => useTheme().components.buttonGroup.button.border, ({
8
+ })(["cursor:pointer;border:none;font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;line-height:1.25;max-width:100%;min-width:", ";text-align:center;text-decoration:none;transition:color 0.13s ease-in-out,background-color 0.13s ease-in-out,border-color 0.13s ease-in-out,box-shadow 0.13s ease-in-out;@media (prefers-reduced-motion:reduce){transition:none;}user-select:none;vertical-align:middle;white-space:nowrap;display:flex;border-radius:", ";padding:", ";justify-content:center;align-items:center;gap:", ";&:not(:last-child){border-inline-end:", ";}&:disabled{cursor:not-allowed;}", " ", ";"], () => useTheme().components.buttonGroup.button.minWidth, () => useTheme().components.buttonGroup.button.borderRadius, () => useTheme().components.buttonGroup.button.padding, () => useTheme().components.buttonGroup.button.gap, () => useTheme().components.buttonGroup.button.border, ({
9
9
  isSelected
10
10
  }) => getButtonStatesStyle(useTheme().components.buttonGroup.button.toggleStates[isSelected ? "on" : "off"], [tokenMaps.bgColor, tokenMaps.textColor]), getFocusStyle("-2px"));
11
11
  export {
@@ -4,6 +4,7 @@ const jsxRuntime = require("../node_modules/react/jsx-runtime.cjs");
4
4
  const index = require("../node_modules/@radix-ui/react-dialog/dist/index.cjs");
5
5
  const redisUiStyles = require("@redislabsdev/redis-ui-styles");
6
6
  const Drawer_context = require("./Drawer.context.cjs");
7
+ const useReducedMotionLifecycleSync = require("./hooks/useReducedMotionLifecycleSync.cjs");
7
8
  const Overlay = require("./components/Overlay/Overlay.cjs");
8
9
  const Content = require("./components/Content/Content.cjs");
9
10
  const Header = require("./components/Header/Header.cjs");
@@ -24,6 +25,15 @@ const Drawer = Object.assign(({
24
25
  ...props
25
26
  }) => {
26
27
  const theme = redisUiStyles.useTheme();
28
+ const {
29
+ prefersReducedMotion
30
+ } = useReducedMotionLifecycleSync.useReducedMotionLifecycleSync({
31
+ open,
32
+ onDrawerWillOpen,
33
+ onDrawerDidOpen,
34
+ onDrawerWillClose,
35
+ onDrawerDidClose
36
+ });
27
37
  const handleOnAnimationStart = () => {
28
38
  if (open) {
29
39
  onDrawerWillOpen == null ? void 0 : onDrawerWillOpen();
@@ -52,8 +62,8 @@ const Drawer = Object.assign(({
52
62
  children: jsxRuntime.jsxRuntimeExports.jsxs(index.Portal, {
53
63
  container: containerElement,
54
64
  children: [persistent && jsxRuntime.jsxRuntimeExports.jsx(Overlay.default, {}), jsxRuntime.jsxRuntimeExports.jsx(Content.default, {
55
- onAnimationStart: handleOnAnimationStart,
56
- onAnimationEndCapture: handleOnAnimationEnd,
65
+ onAnimationStart: prefersReducedMotion ? void 0 : handleOnAnimationStart,
66
+ onAnimationEndCapture: prefersReducedMotion ? void 0 : handleOnAnimationEnd,
57
67
  ...props,
58
68
  children
59
69
  })]