Skip to content

Commit 1e403fa

Browse files
authored
[Flyout System] Improve validation and error logging (#9091)
1 parent e252ac2 commit 1e403fa

3 files changed

Lines changed: 27 additions & 84 deletions

File tree

packages/eui/src/components/flyout/manager/flyout_managed.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import {
3333
useIsFlyoutActive,
3434
useParentFlyoutSize,
3535
} from './hooks';
36-
import { useIsFlyoutRegistered } from './selectors';
36+
import { useCurrentMainFlyout, useIsFlyoutRegistered } from './selectors';
3737
import type { EuiFlyoutLevel } from './types';
3838
import {
3939
createValidationErrorMessage,
@@ -86,6 +86,7 @@ export const EuiManagedFlyout = ({
8686
const { addFlyout, closeFlyout, setFlyoutWidth, goBack, getHistoryItems } =
8787
useFlyoutManager();
8888
const parentSize = useParentFlyoutSize(flyoutId);
89+
const parentFlyout = useCurrentMainFlyout();
8990
const layoutMode = useFlyoutLayoutMode();
9091
const styles = useEuiMemoizedStyles(euiManagedFlyoutStyles);
9192

@@ -105,6 +106,7 @@ export const EuiManagedFlyout = ({
105106
const combinationError = validateSizeCombination(parentSize, size);
106107
if (combinationError) {
107108
combinationError.flyoutId = flyoutId;
109+
combinationError.parentFlyoutId = parentFlyout?.flyoutId;
108110
combinationError.level = level;
109111
throw new Error(createValidationErrorMessage(combinationError));
110112
}

packages/eui/src/components/flyout/manager/validation.test.ts

Lines changed: 15 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import {
1010
isNamedSize,
1111
validateManagedFlyoutSize,
1212
validateSizeCombination,
13-
validateFlyoutSize,
1413
createValidationErrorMessage,
1514
FlyoutValidationError,
1615
validateFlyoutTitle,
@@ -90,7 +89,6 @@ describe('Flyout Size Validation', () => {
9089
expect(error).toEqual({
9190
type: 'INVALID_SIZE_COMBINATION',
9291
message: 'Parent and child flyouts cannot both be size "m"',
93-
size: 'm',
9492
});
9593
});
9694

@@ -99,7 +97,6 @@ describe('Flyout Size Validation', () => {
9997
expect(error).toEqual({
10098
type: 'INVALID_SIZE_COMBINATION',
10199
message: 'Parent and child flyouts cannot both be size "fill"',
102-
size: 'fill',
103100
});
104101
});
105102

@@ -109,105 +106,78 @@ describe('Flyout Size Validation', () => {
109106
type: 'INVALID_SIZE_COMBINATION',
110107
message:
111108
'Parent flyouts cannot be size "l" when there is a child flyout',
112-
size: 'l',
113109
});
114110
});
115111
});
116112

117-
describe('validateFlyoutSize', () => {
118-
it('should validate managed flyout size type', () => {
119-
const error = validateFlyoutSize('100px', 'test-id', 'main');
120-
expect(error?.type).toBe('INVALID_SIZE_TYPE');
121-
});
122-
123-
it('should validate size combinations for child flyouts', () => {
124-
// Parent and child both 'm' should fail
125-
const error1 = validateFlyoutSize('m', 'child-id', 'child', 'm');
126-
expect(error1?.type).toBe('INVALID_SIZE_COMBINATION');
127-
expect(error1?.message).toContain(
128-
'Parent and child flyouts cannot both be size "m"'
129-
);
130-
131-
// Parent and child both 'fill' should fail
132-
const error2 = validateFlyoutSize('fill', 'child-id', 'child', 'fill');
133-
expect(error2?.type).toBe('INVALID_SIZE_COMBINATION');
134-
expect(error2?.message).toContain(
135-
'Parent and child flyouts cannot both be size "fill"'
136-
);
113+
describe('createValidationErrorMessage', () => {
114+
let consoleSpy: jest.SpyInstance;
137115

138-
// Parent 'l' with child should fail
139-
const error3 = validateFlyoutSize('s', 'child-id', 'child', 'l');
140-
expect(error3?.type).toBe('INVALID_SIZE_COMBINATION');
141-
expect(error3?.message).toContain(
142-
'Parent flyouts cannot be size "l" when there is a child flyout'
143-
);
116+
beforeEach(() => {
117+
consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
144118
});
145119

146-
it('should return null for valid child flyout combinations', () => {
147-
expect(validateFlyoutSize('s', 'child-id', 'child', 'm')).toBeNull();
148-
expect(validateFlyoutSize('l', 'child-id', 'child', 'm')).toBeNull();
149-
expect(validateFlyoutSize('s', 'child-id', 'child', 's')).toBeNull();
150-
expect(validateFlyoutSize('fill', 'child-id', 'child', 'm')).toBeNull();
151-
expect(validateFlyoutSize('s', 'child-id', 'child', 'fill')).toBeNull();
120+
afterEach(() => {
121+
consoleSpy.mockRestore();
152122
});
153-
});
154123

155-
describe('createValidationErrorMessage', () => {
156124
it('should create error message for invalid size type', () => {
157125
const error: FlyoutValidationError = {
158126
type: 'INVALID_SIZE_TYPE',
159127
message:
160-
'Managed flyouts must use named sizes (s, m, l). Received: 100px',
128+
'Managed flyouts must use named sizes (s, m, l, fill). Received: 100px',
161129
flyoutId: 'test-id',
162130
level: 'main',
163131
size: '100px',
164132
};
165133

166134
const message = createValidationErrorMessage(error);
167135
expect(message).toBe(
168-
'EuiFlyout validation error: Managed flyouts must use named sizes (s, m, l). Received: 100px'
136+
'EuiFlyout validation error: Managed flyouts must use named sizes (s, m, l, fill). Received: 100px'
169137
);
138+
expect(consoleSpy).toHaveBeenCalledWith(error);
170139
});
171140

172141
it('should create error message for invalid size combination', () => {
173142
const error: FlyoutValidationError = {
174143
type: 'INVALID_SIZE_COMBINATION',
175144
message: 'Parent and child flyouts cannot both be size "m"',
176-
size: 'm',
177145
};
178146

179147
const message = createValidationErrorMessage(error);
180148
expect(message).toBe(
181149
'EuiFlyout validation error: Parent and child flyouts cannot both be size "m"'
182150
);
151+
expect(consoleSpy).toHaveBeenCalledWith(error);
183152
});
184153

185154
it('should create error message for invalid fill size combination', () => {
186155
const error: FlyoutValidationError = {
187156
type: 'INVALID_SIZE_COMBINATION',
188157
message: 'Parent and child flyouts cannot both be size "fill"',
189-
size: 'fill',
190158
};
191159

192160
const message = createValidationErrorMessage(error);
193161
expect(message).toBe(
194162
'EuiFlyout validation error: Parent and child flyouts cannot both be size "fill"'
195163
);
164+
expect(consoleSpy).toHaveBeenCalledWith(error);
196165
});
197166

198167
it('should create error message for invalid flyout menu title', () => {
199168
const error: FlyoutValidationError = {
200169
type: 'INVALID_FLYOUT_MENU_TITLE',
201170
message:
202-
"Managed flyouts require either a 'flyoutMenuProps' a 'title' property, or an 'aria-label' to provide the title.",
171+
"Managed flyouts require either a 'flyoutMenuProps.title' or an 'aria-label' to provide the flyout menu title.",
203172
flyoutId: 'test-id',
204173
level: 'main',
205174
};
206175

207176
const message = createValidationErrorMessage(error);
208177
expect(message).toBe(
209-
"EuiFlyout validation error: Managed flyouts require either a 'flyoutMenuProps' a 'title' property, or an 'aria-label' to provide the title."
178+
"EuiFlyout validation error: Managed flyouts require either a 'flyoutMenuProps.title' or an 'aria-label' to provide the flyout menu title."
210179
);
180+
expect(consoleSpy).toHaveBeenCalledWith(error);
211181
});
212182

213183
it('should handle unknown error types', () => {
@@ -222,6 +192,7 @@ describe('Flyout Size Validation', () => {
222192
expect(message).toBe(
223193
'EuiFlyout validation error: Unknown validation error'
224194
);
195+
expect(consoleSpy).toHaveBeenCalledWith(error);
225196
});
226197
});
227198
});

packages/eui/src/components/flyout/manager/validation.ts

Lines changed: 9 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import { EuiFlyoutSize, FLYOUT_SIZES } from '../const';
1010
import { EuiFlyoutComponentProps } from '../flyout.component';
1111
import { EuiFlyoutMenuProps } from '../flyout_menu';
12-
import { LEVEL_CHILD, LEVEL_MAIN } from './const';
12+
import { LEVEL_MAIN } from './const';
1313
import { EuiFlyoutLevel } from './types';
1414

1515
type FlyoutValidationErrorType =
@@ -21,8 +21,10 @@ export interface FlyoutValidationError {
2121
type: FlyoutValidationErrorType;
2222
message: string;
2323
flyoutId?: string;
24+
parentFlyoutId?: string;
2425
level?: EuiFlyoutLevel;
25-
size?: string; // Allow any string for error reporting
26+
size?: string | number;
27+
parentSize?: string | number;
2628
}
2729

2830
/**
@@ -47,7 +49,7 @@ export function validateManagedFlyoutSize(
4749
message: `Managed flyouts must use named sizes (${namedSizes}). Received: ${size}`,
4850
flyoutId,
4951
level,
50-
size: `${size}`,
52+
size,
5153
};
5254
}
5355
return null;
@@ -84,7 +86,6 @@ export function validateSizeCombination(
8486
return {
8587
type: 'INVALID_SIZE_COMBINATION',
8688
message: 'Parent and child flyouts cannot both be size "m"',
87-
size: childSize,
8889
};
8990
}
9091

@@ -93,7 +94,6 @@ export function validateSizeCombination(
9394
return {
9495
type: 'INVALID_SIZE_COMBINATION',
9596
message: 'Parent and child flyouts cannot both be size "fill"',
96-
size: childSize,
9797
};
9898
}
9999

@@ -102,57 +102,27 @@ export function validateSizeCombination(
102102
return {
103103
type: 'INVALID_SIZE_COMBINATION',
104104
message: 'Parent flyouts cannot be size "l" when there is a child flyout',
105-
size: parentSize,
106105
};
107106
}
108107

109108
return null;
110109
}
111110

112-
/**
113-
* Comprehensive validation for flyout size rules
114-
*/
115-
export function validateFlyoutSize(
116-
size: EuiFlyoutComponentProps['size'],
117-
flyoutId: string,
118-
level: EuiFlyoutLevel,
119-
parentSize?: EuiFlyoutSize
120-
): FlyoutValidationError | null {
121-
// First validate that managed flyouts use named sizes
122-
const sizeTypeError = validateManagedFlyoutSize(size, flyoutId, level);
123-
if (sizeTypeError) {
124-
return sizeTypeError;
125-
}
126-
127-
// If this is a child flyout and we have parent size, validate combination
128-
if (level === LEVEL_CHILD && parentSize && isNamedSize(size)) {
129-
const combinationError = validateSizeCombination(parentSize, size);
130-
if (combinationError) {
131-
combinationError.flyoutId = flyoutId;
132-
combinationError.level = level;
133-
return combinationError;
134-
}
135-
}
136-
137-
return null;
138-
}
139-
140111
/**
141112
* Creates a user-friendly error message for validation errors
142113
*/
143114
export function createValidationErrorMessage(
144115
error: FlyoutValidationError
145116
): string {
146-
const prefix = `EuiFlyout validation error: `;
117+
console.error(error);
118+
const prefix = `EuiFlyout validation error`;
147119

148120
switch (error.type) {
149121
case 'INVALID_SIZE_TYPE':
150-
return `${prefix}${error.message}`;
151122
case 'INVALID_SIZE_COMBINATION':
152-
return `${prefix}${error.message}`;
153123
case 'INVALID_FLYOUT_MENU_TITLE':
154-
return `${prefix}${error.message}`;
124+
return `${prefix}: ${error.message}`;
155125
default:
156-
return `${prefix}Unknown validation error`;
126+
return `${prefix}: Unknown validation error`;
157127
}
158128
}

0 commit comments

Comments
 (0)