@lumx/react 3.0.1 → 3.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 (66) hide show
  1. package/esm/_internal/ClickAwayProvider.js +9 -5
  2. package/esm/_internal/ClickAwayProvider.js.map +1 -1
  3. package/esm/_internal/FlexBox.js.map +1 -1
  4. package/esm/_internal/HeadingLevelProvider.js +112 -0
  5. package/esm/_internal/HeadingLevelProvider.js.map +1 -0
  6. package/esm/_internal/ProgressTrackerStepPanel.js +2 -1
  7. package/esm/_internal/ProgressTrackerStepPanel.js.map +1 -1
  8. package/esm/_internal/Slides.js +270 -79
  9. package/esm/_internal/Slides.js.map +1 -1
  10. package/esm/_internal/TabPanel.js +2 -1
  11. package/esm/_internal/TabPanel.js.map +1 -1
  12. package/esm/_internal/Text2.js +63 -0
  13. package/esm/_internal/Text2.js.map +1 -0
  14. package/esm/_internal/_rollupPluginBabelHelpers.js +17 -1
  15. package/esm/_internal/_rollupPluginBabelHelpers.js.map +1 -1
  16. package/esm/_internal/components.js +1 -0
  17. package/esm/_internal/components.js.map +1 -1
  18. package/esm/_internal/heading.js +11 -0
  19. package/esm/_internal/heading.js.map +1 -0
  20. package/esm/_internal/progress-tracker.js +2 -1
  21. package/esm/_internal/progress-tracker.js.map +1 -1
  22. package/esm/_internal/slideshow.js +2 -0
  23. package/esm/_internal/slideshow.js.map +1 -1
  24. package/esm/_internal/state.js +145 -0
  25. package/esm/_internal/state.js.map +1 -0
  26. package/esm/_internal/tabs.js +1 -0
  27. package/esm/_internal/tabs.js.map +1 -1
  28. package/esm/_internal/text.js +10 -0
  29. package/esm/_internal/text.js.map +1 -0
  30. package/esm/_internal/useRovingTabIndex.js +9 -144
  31. package/esm/_internal/useRovingTabIndex.js.map +1 -1
  32. package/esm/index.js +5 -1
  33. package/esm/index.js.map +1 -1
  34. package/package.json +4 -5
  35. package/src/components/flex-box/FlexBox.stories.tsx +60 -4
  36. package/src/components/flex-box/FlexBox.tsx +7 -4
  37. package/src/components/flex-box/__snapshots__/FlexBox.test.tsx.snap +35 -0
  38. package/src/components/heading/Heading.stories.tsx +108 -0
  39. package/src/components/heading/Heading.test.tsx +77 -0
  40. package/src/components/heading/Heading.tsx +62 -0
  41. package/src/components/heading/HeadingLevelProvider.tsx +30 -0
  42. package/src/components/heading/constants.ts +16 -0
  43. package/src/components/heading/context.tsx +13 -0
  44. package/src/components/heading/index.ts +3 -0
  45. package/src/components/heading/useHeadingLevel.tsx +8 -0
  46. package/src/components/index.ts +1 -0
  47. package/src/components/slideshow/Slides.tsx +33 -3
  48. package/src/components/slideshow/Slideshow.stories.tsx +98 -2
  49. package/src/components/slideshow/Slideshow.tsx +15 -3
  50. package/src/components/slideshow/SlideshowControls.stories.tsx +1 -1
  51. package/src/components/slideshow/SlideshowControls.tsx +49 -11
  52. package/src/components/slideshow/SlideshowItem.tsx +0 -5
  53. package/src/components/slideshow/SlideshowItemGroup.tsx +63 -0
  54. package/src/components/slideshow/__snapshots__/Slideshow.test.tsx.snap +4 -1
  55. package/src/components/slideshow/useSlideFocusManagement.tsx +92 -0
  56. package/src/components/text/Text.stories.tsx +80 -0
  57. package/src/components/text/Text.test.tsx +62 -0
  58. package/src/components/text/Text.tsx +94 -0
  59. package/src/components/text/index.ts +1 -0
  60. package/src/hooks/useRovingTabIndex.tsx +9 -0
  61. package/src/index.ts +2 -0
  62. package/src/utils/focus/constants.ts +5 -0
  63. package/src/utils/focus/getFirstAndLastFocusable.ts +4 -10
  64. package/src/utils/focus/getFocusableElements.test.ts +151 -0
  65. package/src/utils/focus/getFocusableElements.ts +7 -0
  66. package/types.d.ts +94 -7
@@ -0,0 +1,151 @@
1
+ import { getFocusableElements } from '@lumx/react/utils/focus/getFocusableElements';
2
+
3
+ function htmlToElement(html: string): any {
4
+ const template = document.createElement('template');
5
+ template.innerHTML = html.trim();
6
+ return template.content.firstChild;
7
+ }
8
+
9
+ describe(getFocusableElements.name, () => {
10
+ it('should get empty', () => {
11
+ const element = htmlToElement(`<div></div>`);
12
+ const focusable = getFocusableElements(element);
13
+ expect(focusable).toEqual([]);
14
+ });
15
+
16
+ it('should get single item', () => {
17
+ const element = htmlToElement(`<div><button /></div>`);
18
+ const focusable = getFocusableElements(element);
19
+ expect(focusable).toMatchInlineSnapshot(`
20
+ Array [
21
+ <button />,
22
+ ]
23
+ `);
24
+ });
25
+
26
+ describe('match focusable elements', () => {
27
+ it('should match input element', () => {
28
+ const element = htmlToElement(`<div><input /></div>`);
29
+ const focusable = getFocusableElements(element);
30
+ expect(focusable).toMatchInlineSnapshot(`
31
+ Array [
32
+ <input />,
33
+ ]
34
+ `);
35
+ });
36
+
37
+ it('should match link element', () => {
38
+ const element = htmlToElement(`<div><a href="#" /></div>`);
39
+ const focusable = getFocusableElements(element);
40
+ expect(focusable).toMatchInlineSnapshot(`
41
+ Array [
42
+ <a
43
+ href="#"
44
+ />,
45
+ ]
46
+ `);
47
+ });
48
+
49
+ it('should match textarea element', () => {
50
+ const element = htmlToElement(`<div><textarea /></div>`);
51
+ const focusable = getFocusableElements(element);
52
+ expect(focusable).toMatchInlineSnapshot(`
53
+ Array [
54
+ <textarea>
55
+ &lt;/div&gt;
56
+ </textarea>,
57
+ ]
58
+ `);
59
+ });
60
+
61
+ it('should match element with tabindex', () => {
62
+ const element = htmlToElement(`<div><span tabindex="0" /></div>`);
63
+ const focusable = getFocusableElements(element);
64
+ expect(focusable).toMatchInlineSnapshot(`
65
+ Array [
66
+ <span
67
+ tabindex="0"
68
+ />,
69
+ ]
70
+ `);
71
+ });
72
+
73
+ it('should keep disabled=false', () => {
74
+ const element = htmlToElement(`<div><button disabled="false" /><button /></div>`);
75
+ const focusable = getFocusableElements(element);
76
+ expect(focusable).toMatchInlineSnapshot(`
77
+ Array [
78
+ <button
79
+ disabled="false"
80
+ />,
81
+ <button />,
82
+ ]
83
+ `);
84
+ });
85
+
86
+ it('should keep aria-disabled=false', () => {
87
+ const element = htmlToElement(`<div><button aria-disabled="false" /><button /></div>`);
88
+ const focusable = getFocusableElements(element);
89
+ expect(focusable).toMatchInlineSnapshot(`
90
+ Array [
91
+ <button
92
+ aria-disabled="false"
93
+ />,
94
+ <button />,
95
+ ]
96
+ `);
97
+ });
98
+ });
99
+
100
+ describe('skip disabled elements', () => {
101
+ it('should skip disabled', () => {
102
+ const element = htmlToElement(`<div><button disabled /><button /></div>`);
103
+ const focusable = getFocusableElements(element);
104
+ expect(focusable).toMatchInlineSnapshot(`
105
+ Array [
106
+ <button />,
107
+ ]
108
+ `);
109
+ });
110
+
111
+ it('should skip aria-disabled', () => {
112
+ const element = htmlToElement(`<div><button aria-disabled /><button /></div>`);
113
+ const focusable = getFocusableElements(element);
114
+ expect(focusable).toMatchInlineSnapshot(`
115
+ Array [
116
+ <button />,
117
+ ]
118
+ `);
119
+ });
120
+
121
+ it('should skip tabindex=-1', () => {
122
+ const element = htmlToElement(`<div><button tabindex="-1" /><button /></div>`);
123
+ const focusable = getFocusableElements(element);
124
+ expect(focusable).toMatchInlineSnapshot(`
125
+ Array [
126
+ <button />,
127
+ ]
128
+ `);
129
+ });
130
+
131
+ it('should skip input type hidden', () => {
132
+ const element = htmlToElement(`<div><input type="hidden" /><button /></div>`);
133
+ const focusable = getFocusableElements(element);
134
+ expect(focusable).toMatchInlineSnapshot(`
135
+ Array [
136
+ <button />,
137
+ ]
138
+ `);
139
+ });
140
+
141
+ it('should skip hidden input', () => {
142
+ const element = htmlToElement(`<div><input hidden /><button /></div>`);
143
+ const focusable = getFocusableElements(element);
144
+ expect(focusable).toMatchInlineSnapshot(`
145
+ Array [
146
+ <button />,
147
+ ]
148
+ `);
149
+ });
150
+ });
151
+ });
@@ -0,0 +1,7 @@
1
+ import { DISABLED_SELECTOR, TABBABLE_ELEMENTS_SELECTOR } from './constants';
2
+
3
+ const isNotDisabled = (element: HTMLElement) => !element.matches(DISABLED_SELECTOR);
4
+
5
+ export function getFocusableElements(element: HTMLElement): HTMLElement[] {
6
+ return Array.from(element.querySelectorAll<HTMLElement>(TABBABLE_ELEMENTS_SELECTOR)).filter(isNotDisabled);
7
+ }
package/types.d.ts CHANGED
@@ -66,6 +66,7 @@ export declare const Alignment: {
66
66
  readonly right: "right";
67
67
  readonly spaceAround: "space-around";
68
68
  readonly spaceBetween: "space-between";
69
+ readonly spaceEvenly: "space-evenly";
69
70
  readonly start: "start";
70
71
  readonly top: "top";
71
72
  };
@@ -1100,6 +1101,9 @@ export interface FlagProps extends GenericProps, HasTheme {
1100
1101
  export declare const Flag: Comp<FlagProps, HTMLDivElement>;
1101
1102
  export declare type MarginAutoAlignment = Extract<Alignment, "top" | "bottom" | "right" | "left">;
1102
1103
  export declare type GapSize = Extract<Size, "tiny" | "regular" | "medium" | "big" | "huge">;
1104
+ export declare type SpaceAlignment = Extract<Alignment, "space-between" | "space-evenly" | "space-around">;
1105
+ export declare type FlexVerticalAlignment = VerticalAlignment | SpaceAlignment;
1106
+ export declare type FlexHorizontalAlignment = HorizontalAlignment | SpaceAlignment;
1103
1107
  /**
1104
1108
  * Defines the props of the component.
1105
1109
  */
@@ -1111,7 +1115,7 @@ export interface FlexBoxProps extends GenericProps {
1111
1115
  /** Gap space between flexbox items. */
1112
1116
  gap?: GapSize;
1113
1117
  /** Flex horizontal alignment. */
1114
- hAlign?: VerticalAlignment;
1118
+ hAlign?: FlexVerticalAlignment;
1115
1119
  /** Whether the "auto margin" is enabled all around or not. */
1116
1120
  marginAuto?: MarginAutoAlignment | MarginAutoAlignment[];
1117
1121
  /** Whether the "content shrink" is disabled or not. */
@@ -1119,7 +1123,7 @@ export interface FlexBoxProps extends GenericProps {
1119
1123
  /** Flex direction. */
1120
1124
  orientation?: Orientation;
1121
1125
  /** Flex vertical alignment. */
1122
- vAlign?: HorizontalAlignment;
1126
+ vAlign?: FlexHorizontalAlignment;
1123
1127
  /** Whether the "flex wrap" is enabled or not. */
1124
1128
  wrap?: boolean;
1125
1129
  }
@@ -1191,6 +1195,73 @@ export interface GenericBlock extends BaseGenericBlock {
1191
1195
  }
1192
1196
  declare const BaseGenericBlock: BaseGenericBlock;
1193
1197
  export declare const GenericBlock: GenericBlock;
1198
+ export declare type TextComponents = "span" | "p" | HeadingElement;
1199
+ /**
1200
+ * Defines the props of the component.
1201
+ */
1202
+ export interface TextProps extends GenericProps {
1203
+ /**
1204
+ * Color variant.
1205
+ */
1206
+ color?: Color;
1207
+ /**
1208
+ * Lightened or darkened variant of the selected color.
1209
+ */
1210
+ colorVariant?: ColorVariant;
1211
+ /**
1212
+ * Typography variant.
1213
+ */
1214
+ typography?: Typography;
1215
+ /**
1216
+ * Custom component to render the text.
1217
+ */
1218
+ as: TextComponents;
1219
+ /**
1220
+ * Control whether the text should truncate or not.
1221
+ * Setting as `true` will make the text truncate on a single line.
1222
+ * Setting as `{ lines: number }` will make the text truncate on a multiple lines.
1223
+ */
1224
+ truncate?: boolean | {
1225
+ lines: number;
1226
+ };
1227
+ }
1228
+ /**
1229
+ * Text component.
1230
+ *
1231
+ * @param props Component props.
1232
+ * @param ref Component ref.
1233
+ * @return React element.
1234
+ */
1235
+ export declare const Text: Comp<TextProps>;
1236
+ /**
1237
+ * Defines the props of the component.
1238
+ */
1239
+ export interface HeadingProps extends Partial<TextProps> {
1240
+ /**
1241
+ * Display a specific heading level instead of the one provided by parent context provider.
1242
+ */
1243
+ as?: HeadingElement;
1244
+ }
1245
+ /**
1246
+ * Renders a heading component.
1247
+ * Extends the `Text` Component with the heading level automatically computed based on
1248
+ * the current level provided by the context.
1249
+ */
1250
+ export declare const Heading: Comp<HeadingProps>;
1251
+ export interface HeadingLevelProviderProps {
1252
+ /** The heading level to start at. If left undefined, the parent context will be used, if any. */
1253
+ level?: number;
1254
+ /** The children to display */
1255
+ children: ReactNode;
1256
+ }
1257
+ /**
1258
+ * Provide a new heading level context.
1259
+ */
1260
+ export declare const HeadingLevelProvider: React.FC<HeadingLevelProviderProps>;
1261
+ export declare const useHeadingLevel: () => {
1262
+ level: number;
1263
+ headingElement: import("../../utils").HeadingElement;
1264
+ };
1194
1265
  export declare type GridGutterSize = Extract<Size, "regular" | "big" | "huge">;
1195
1266
  /**
1196
1267
  * Defines the props of the component.
@@ -2135,13 +2206,13 @@ export declare const clamp: (value: number, min: number, max: number) => number;
2135
2206
  /**
2136
2207
  * Defines the props of the component.
2137
2208
  */
2138
- export interface SlideshowProps extends GenericProps, Pick<SlidesProps, "autoPlay" | "slidesId" | "id" | "theme" | "fillHeight" | "groupBy"> {
2209
+ export interface SlideshowProps extends GenericProps, Pick<SlidesProps, "autoPlay" | "slidesId" | "id" | "theme" | "fillHeight" | "groupBy" | "slideGroupLabel"> {
2139
2210
  /** current slide active */
2140
2211
  activeIndex?: SlidesProps["activeIndex"];
2141
2212
  /** Interval between each slide when automatic rotation is enabled. */
2142
2213
  interval?: number;
2143
2214
  /** Props to pass to the slideshow controls (minus those already set by the Slideshow props). */
2144
- slideshowControlsProps?: Pick<SlideshowControlsProps, "nextButtonProps" | "previousButtonProps"> & Omit<SlideshowControlsProps, "activeIndex" | "onPaginationClick" | "onNextClick" | "onPreviousClick" | "slidesCount" | "parentRef" | "theme">;
2215
+ slideshowControlsProps?: Pick<SlideshowControlsProps, "nextButtonProps" | "previousButtonProps" | "paginationItemProps"> & Omit<SlideshowControlsProps, "activeIndex" | "onPaginationClick" | "onNextClick" | "onPreviousClick" | "slidesCount" | "parentRef" | "theme">;
2145
2216
  /** Callback when slide changes */
2146
2217
  onChange?(index: number): void;
2147
2218
  }
@@ -2157,8 +2228,6 @@ export declare const Slideshow: Comp<SlideshowProps, HTMLDivElement>;
2157
2228
  * Defines the props of the component.
2158
2229
  */
2159
2230
  export interface SlideshowItemProps extends GenericProps {
2160
- /** whether the slideshow item is currently visible */
2161
- isCurrentlyVisible?: boolean;
2162
2231
  /** interval in which slides are automatically shown */
2163
2232
  interval?: number;
2164
2233
  }
@@ -2242,6 +2311,8 @@ export interface SlideshowControlsProps extends GenericProps, HasTheme {
2242
2311
  parentRef?: RefObject<HTMLDivElement> | HTMLDivElement | null;
2243
2312
  /** Props to pass to the previous button (minus those already set by the SlideshowControls props). */
2244
2313
  previousButtonProps: Pick<IconButtonProps, "label"> & Omit<IconButtonProps, "label" | "onClick" | "icon" | "emphasis" | "color">;
2314
+ /** Props to pass to the pagination wrapper */
2315
+ paginationProps?: Omit<React.HTMLAttributes<HTMLDivElement>, "className" | "style" | "role">;
2245
2316
  /** Number of slides. */
2246
2317
  slidesCount: number;
2247
2318
  /** On next button click callback. */
@@ -2252,8 +2323,17 @@ export interface SlideshowControlsProps extends GenericProps, HasTheme {
2252
2323
  onPreviousClick?(loopback?: boolean): void;
2253
2324
  /** whether the slideshow is currently playing */
2254
2325
  isAutoPlaying?: boolean;
2255
- /** function to be executed in order to retrieve the label for the pagination item */
2326
+ /**
2327
+ * function to be executed in order to retrieve the label for the pagination item
2328
+ * @deprecated Use paginationItemProps instead.
2329
+ * */
2256
2330
  paginationItemLabel?: (index: number) => string;
2331
+ /**
2332
+ * function to be executed in order to retrieve the props for a pagination item.
2333
+ */
2334
+ paginationItemProps?: (itemIndex: number) => React.HTMLAttributes<HTMLButtonElement> & {
2335
+ label?: string;
2336
+ };
2257
2337
  /** Props to pass to the lay button (minus those already set by the SlideshowControls props). */
2258
2338
  playButtonProps?: Pick<IconButtonProps, "label"> & Omit<IconButtonProps, "label" | "onClick" | "icon" | "emphasis" | "color">;
2259
2339
  }
@@ -2280,6 +2360,13 @@ export interface SlidesProps extends GenericProps, HasTheme {
2280
2360
  toggleAutoPlay: () => void;
2281
2361
  /** component to be rendered after the slides */
2282
2362
  afterSlides?: React.ReactNode;
2363
+ /** Whether the slides have controls linked */
2364
+ hasControls?: boolean;
2365
+ /**
2366
+ * Accessible label to set on a slide group.
2367
+ * Receives the group position starting from 1 and the total number of groups.
2368
+ * */
2369
+ slideGroupLabel?: (groupPosition: number, groupTotal: number) => string;
2283
2370
  }
2284
2371
  /**
2285
2372
  * Slides component.