Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Core: Make sure StorybookError message shows up in browser console and interactions panel #28464

Merged
merged 13 commits into from
Jul 7, 2024
2 changes: 1 addition & 1 deletion code/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ module.exports = {
},
},
{
files: ['**/core-events/src/**/*'],
files: ['./core/src/preview-errors.ts'],
excludedFiles: ['**/*.test.*'],
rules: {
'local-rules/no-duplicated-error-codes': 'error',
Expand Down
44 changes: 23 additions & 21 deletions code/core/src/ERRORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,15 @@ Second use the `StorybookError` class to define custom errors with specific code
```typescript
import { StorybookError } from './storybook-error';
export class YourCustomError extends StorybookError {
readonly category: Category; // The category to which the error belongs. Check the source in client-errors.ts or server-errors.ts for reference.
readonly code: number; // The numeric code for the error.

template(): string {
// A function that returns the error message.
constructor() {
super({
// The category to which the error belongs. Check the source in client-errors.ts or server-errors.ts for reference.
category: Category,
// The numeric code for the error.
code: number,
// The error message.
message: string,
});
}
}
```
Expand All @@ -42,7 +46,7 @@ export class YourCustomError extends StorybookError {
| ------------- | --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| category | `Category` | The category to which the error belongs. |
| code | `number` | The numeric code for the error. |
| template | `() => string` | Function that returns a properly written error message. |
| message | `string` | The error message. |
| data | `Object` | Optional. Data associated with the error. Used to provide additional information in the error message or to be passed to telemetry. |
| documentation | `boolean` or `string` | Optional. Should be set to `true` **if the error is documented on the Storybook website**. If defined as string, it should be a custom documentation link. |

Expand All @@ -51,28 +55,26 @@ export class YourCustomError extends StorybookError {
```typescript
// Define a custom error with a numeric code and a static error message template.
export class StorybookIndexGenerationError extends StorybookError {
category = Category.Generic;
code = 1;

template(): string {
return `Storybook failed when generating an index for your stories. Check the stories field in your main.js`;
constructor() {
super({
category: Category.Generic,
code: 1,
message: `Storybook failed when generating an index for your stories. Check the stories field in your main.js`,
});
}
}

// Define a custom error with a numeric code and a dynamic error message template based on properties from the constructor.
// Define a custom error with a numeric code and a dynamic error message based on properties from the constructor.
export class InvalidFileExtensionError extends StorybookError {
category = Category.Validation;
code = 2;
documentation = 'https://some-custom-documentation.com/validation-errors';

// extra properties are defined in the constructor via a data property, which is available in any class method
// always use this data Object notation!
constructor(public data: { extension: string }) {
super();
}

template(): string {
return `Invalid file extension found: ${this.data.extension}.`;
super({
category: Category.Validation,
code: 2,
documentation: 'https://some-custom-documentation.com/validation-errors',
message: `Invalid file extension found: ${data.extension}.`,
});
}
}

Expand Down
1 change: 0 additions & 1 deletion code/core/src/__tests/preview-errors.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ describe('UnknownFlowArgTypesError', () => {
const typeError = new UnknownArgTypesError({ type, language: 'Typescript' });
expect(typeError.message).toMatchInlineSnapshot(`
"There was a failure when generating detailed ArgTypes in Typescript for:

{
"name": "signature",
"raw": "SomeType['someProperty']"
Expand Down
24 changes: 11 additions & 13 deletions code/core/src/__tests/storybook-error.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ import { StorybookError } from '../storybook-error';

describe('StorybookError', () => {
class TestError extends StorybookError {
category = 'TEST_CATEGORY';

code = 123;

template() {
return 'This is a test error.';
constructor(documentation?: StorybookError['documentation']) {
super({
category: 'TEST_CATEGORY',
code: 123,
message: 'This is a test error.',
documentation,
});
}
}

Expand All @@ -24,16 +25,14 @@ describe('StorybookError', () => {
});

it('should generate the correct message with internal documentation link', () => {
const error = new TestError();
error.documentation = true;
const error = new TestError(true);
const expectedMessage =
'This is a test error.\n\nMore info: https://storybook.js.org/error/SB_TEST_CATEGORY_0123\n';
expect(error.message).toBe(expectedMessage);
});

it('should generate the correct message with external documentation link', () => {
const error = new TestError();
error.documentation = 'https://example.com/docs/test-error';
const error = new TestError('https://example.com/docs/test-error');
expect(error.message).toMatchInlineSnapshot(`
"This is a test error.

Expand All @@ -43,11 +42,10 @@ describe('StorybookError', () => {
});

it('should generate the correct message with multiple external documentation links', () => {
const error = new TestError();
error.documentation = [
const error = new TestError([
'https://example.com/docs/first-error',
'https://example.com/docs/second-error',
];
]);
expect(error.message).toMatchInlineSnapshot(`
"This is a test error.

Expand Down
26 changes: 11 additions & 15 deletions code/core/src/manager-errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,30 +21,26 @@ export enum Category {
}

export class ProviderDoesNotExtendBaseProviderError extends StorybookError {
readonly category = Category.MANAGER_UI;

readonly code = 1;

template() {
return `The Provider passed into Storybook's UI is not extended from the base Provider. Please check your Provider implementation.`;
constructor() {
super({
category: Category.MANAGER_UI,
code: 1,
message: `The Provider passed into Storybook's UI is not extended from the base Provider. Please check your Provider implementation.`,
});
}
}

export class UncaughtManagerError extends StorybookError {
readonly category = Category.MANAGER_UNCAUGHT;

readonly code = 1;

constructor(
public data: {
error: Error;
}
) {
super(data.error.message);
super({
category: Category.MANAGER_UNCAUGHT,
code: 1,
message: data.error.message,
});
this.stack = data.error.stack;
}

template() {
return this.message;
}
}
Loading