@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.
- package/esm/_internal/ClickAwayProvider.js +9 -5
- package/esm/_internal/ClickAwayProvider.js.map +1 -1
- package/esm/_internal/FlexBox.js.map +1 -1
- package/esm/_internal/HeadingLevelProvider.js +112 -0
- package/esm/_internal/HeadingLevelProvider.js.map +1 -0
- package/esm/_internal/ProgressTrackerStepPanel.js +2 -1
- package/esm/_internal/ProgressTrackerStepPanel.js.map +1 -1
- package/esm/_internal/Slides.js +270 -79
- package/esm/_internal/Slides.js.map +1 -1
- package/esm/_internal/TabPanel.js +2 -1
- package/esm/_internal/TabPanel.js.map +1 -1
- package/esm/_internal/Text2.js +63 -0
- package/esm/_internal/Text2.js.map +1 -0
- package/esm/_internal/_rollupPluginBabelHelpers.js +17 -1
- package/esm/_internal/_rollupPluginBabelHelpers.js.map +1 -1
- package/esm/_internal/components.js +1 -0
- package/esm/_internal/components.js.map +1 -1
- package/esm/_internal/heading.js +11 -0
- package/esm/_internal/heading.js.map +1 -0
- package/esm/_internal/progress-tracker.js +2 -1
- package/esm/_internal/progress-tracker.js.map +1 -1
- package/esm/_internal/slideshow.js +2 -0
- package/esm/_internal/slideshow.js.map +1 -1
- package/esm/_internal/state.js +145 -0
- package/esm/_internal/state.js.map +1 -0
- package/esm/_internal/tabs.js +1 -0
- package/esm/_internal/tabs.js.map +1 -1
- package/esm/_internal/text.js +10 -0
- package/esm/_internal/text.js.map +1 -0
- package/esm/_internal/useRovingTabIndex.js +9 -144
- package/esm/_internal/useRovingTabIndex.js.map +1 -1
- package/esm/index.js +5 -1
- package/esm/index.js.map +1 -1
- package/package.json +4 -5
- package/src/components/flex-box/FlexBox.stories.tsx +60 -4
- package/src/components/flex-box/FlexBox.tsx +7 -4
- package/src/components/flex-box/__snapshots__/FlexBox.test.tsx.snap +35 -0
- package/src/components/heading/Heading.stories.tsx +108 -0
- package/src/components/heading/Heading.test.tsx +77 -0
- package/src/components/heading/Heading.tsx +62 -0
- package/src/components/heading/HeadingLevelProvider.tsx +30 -0
- package/src/components/heading/constants.ts +16 -0
- package/src/components/heading/context.tsx +13 -0
- package/src/components/heading/index.ts +3 -0
- package/src/components/heading/useHeadingLevel.tsx +8 -0
- package/src/components/index.ts +1 -0
- package/src/components/slideshow/Slides.tsx +33 -3
- package/src/components/slideshow/Slideshow.stories.tsx +98 -2
- package/src/components/slideshow/Slideshow.tsx +15 -3
- package/src/components/slideshow/SlideshowControls.stories.tsx +1 -1
- package/src/components/slideshow/SlideshowControls.tsx +49 -11
- package/src/components/slideshow/SlideshowItem.tsx +0 -5
- package/src/components/slideshow/SlideshowItemGroup.tsx +63 -0
- package/src/components/slideshow/__snapshots__/Slideshow.test.tsx.snap +4 -1
- package/src/components/slideshow/useSlideFocusManagement.tsx +92 -0
- package/src/components/text/Text.stories.tsx +80 -0
- package/src/components/text/Text.test.tsx +62 -0
- package/src/components/text/Text.tsx +94 -0
- package/src/components/text/index.ts +1 -0
- package/src/hooks/useRovingTabIndex.tsx +9 -0
- package/src/index.ts +2 -0
- package/src/utils/focus/constants.ts +5 -0
- package/src/utils/focus/getFirstAndLastFocusable.ts +4 -10
- package/src/utils/focus/getFocusableElements.test.ts +151 -0
- package/src/utils/focus/getFocusableElements.ts +7 -0
- 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
|
+
</div>
|
|
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?:
|
|
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?:
|
|
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
|
-
/**
|
|
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.
|