Skip to content

Commit

Permalink
Merge pull request #17800 from storybookjs/addon-highlight
Browse files Browse the repository at this point in the history
Essentials: Add highlight addon
  • Loading branch information
shilman authored Jun 29, 2022
2 parents d27c826 + 8396fc8 commit 4eefe54
Show file tree
Hide file tree
Showing 59 changed files with 859 additions and 36 deletions.
1 change: 1 addition & 0 deletions addons/a11y/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addon-highlight": "7.0.0-alpha.6",
"@storybook/addons": "7.0.0-alpha.6",
"@storybook/api": "7.0.0-alpha.6",
"@storybook/channels": "7.0.0-alpha.6",
Expand Down
3 changes: 2 additions & 1 deletion addons/a11y/src/components/A11yContext.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { AxeResults } from 'axe-core';
import { render, act } from '@testing-library/react';
import * as api from '@storybook/api';
import { STORY_CHANGED } from '@storybook/core-events';
import { HIGHLIGHT } from '@storybook/addon-highlight';

import { A11yContextProvider, useA11yContext } from './A11yContext';
import { EVENTS } from '../constants';
Expand Down Expand Up @@ -97,7 +98,7 @@ describe('A11YPanel', () => {
const { rerender } = render(<A11yContextProvider active />);
rerender(<A11yContextProvider active={false} />);
expect(emit).toHaveBeenLastCalledWith(
EVENTS.HIGHLIGHT,
HIGHLIGHT,
expect.objectContaining({
color: expect.any(String),
elements: [],
Expand Down
5 changes: 3 additions & 2 deletions addons/a11y/src/components/A11yContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { themes, convert } from '@storybook/theming';
import { Result } from 'axe-core';
import { useChannel, useAddonState, useStorybookApi } from '@storybook/api';
import { STORY_CHANGED, STORY_RENDERED } from '@storybook/core-events';
import { HIGHLIGHT } from '@storybook/addon-highlight';
import { ADDON_ID, EVENTS } from '../constants';

export interface Results {
Expand Down Expand Up @@ -77,7 +78,7 @@ export const A11yContextProvider: React.FC<A11yContextProviderProps> = ({ active
const handleReset = React.useCallback(() => {
setTab(0);
setResults(defaultResult);
// Highlights is cleared by a11yHighlights.ts
// Highlights is cleared by addon-highlight
}, []);

const emit = useChannel({
Expand All @@ -86,7 +87,7 @@ export const A11yContextProvider: React.FC<A11yContextProviderProps> = ({ active
});

React.useEffect(() => {
emit(EVENTS.HIGHLIGHT, { elements: highlighted, color: colorsByType[tab] });
emit(HIGHLIGHT, { elements: highlighted, color: colorsByType[tab] });
}, [highlighted, tab]);

React.useEffect(() => {
Expand Down
4 changes: 1 addition & 3 deletions addons/a11y/src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
export const ADDON_ID = 'storybook/a11y';
export const PANEL_ID = `${ADDON_ID}/panel`;
export const PARAM_KEY = `a11y`;
export const HIGHLIGHT_STYLE_ID = 'a11yHighlight';
const RESULT = `${ADDON_ID}/result`;
const REQUEST = `${ADDON_ID}/request`;
const RUNNING = `${ADDON_ID}/running`;
const ERROR = `${ADDON_ID}/error`;
const MANUAL = `${ADDON_ID}/manual`;
const HIGHLIGHT = `${ADDON_ID}/highlight`;

export const EVENTS = { RESULT, REQUEST, RUNNING, ERROR, MANUAL, HIGHLIGHT };
export const EVENTS = { RESULT, REQUEST, RUNNING, ERROR, MANUAL };
11 changes: 0 additions & 11 deletions addons/a11y/src/highlight.ts

This file was deleted.

1 change: 0 additions & 1 deletion addons/a11y/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import deprecate from 'util-deprecate';
import dedent from 'ts-dedent';

export { PARAM_KEY } from './constants';
export * from './highlight';
export * from './params';

if (module && module.hot && module.hot.decline) {
Expand Down
1 change: 0 additions & 1 deletion addons/a11y/src/preview.tsx
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
import './a11yRunner';
import './a11yHighlight';
1 change: 1 addition & 0 deletions addons/essentials/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"@storybook/addon-backgrounds": "7.0.0-alpha.6",
"@storybook/addon-controls": "7.0.0-alpha.6",
"@storybook/addon-docs": "7.0.0-alpha.6",
"@storybook/addon-highlight": "7.0.0-alpha.6",
"@storybook/addon-measure": "7.0.0-alpha.6",
"@storybook/addon-outline": "7.0.0-alpha.6",
"@storybook/addon-toolbars": "7.0.0-alpha.6",
Expand Down
12 changes: 11 additions & 1 deletion addons/essentials/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,17 @@ export function addons(options: PresetOptions) {

const main = requireMain(options.configDir);
return (
['docs', 'controls', 'actions', 'backgrounds', 'viewport', 'toolbars', 'measure', 'outline']
[
'docs',
'controls',
'actions',
'backgrounds',
'viewport',
'toolbars',
'measure',
'outline',
'highlight',
]
.filter((key) => (options as any)[key] !== false)
.map((key) => `@storybook/addon-${key}`)
.filter((addon) => !checkInstalled(addon, main))
Expand Down
64 changes: 64 additions & 0 deletions addons/highlight/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Storybook Addon Highlight

Storybook addon allows for highlighting specific DOM nodes within your story.

Use it to call attention to particular parts of the story. Or use it to enhance other addons that you might be building. For example, [Accessibility](https://storybook.js.org/addons/@storybook/addon-a11y/) addon uses it to highlight DOM nodes that are failing accessibility checks.

![](https://user-images.githubusercontent.com/42671/160146801-179eaa79-fff8-4bff-b17c-9262fdc94918.png)

## Usage

This addon requires Storybook 6.5 or later. Highlight is part of [essentials](https://storybook.js.org/docs/react/essentials/introduction) and so is installed in all new Storybooks by default. If you need to add it to your Storybook, you can run:

```sh
npm i -D @storybook/addon-highlight
```

Add `"@storybook/addon-highlight"` to the addons array in your `.storybook/main.js`:

```js
module.exports = {
addons: ['@storybook/addon-highlight'],
};
```

### Apply or clear highlights

Highlight DOM nodes by emitting the `HIGHLIGHT` event from within a story or an addon. The event payload must contain a list of selectors you want to highlight.

```js
import React, { useEffect } from 'react';
import { useChannel } from '@storybook/addons';
import { HIGHLIGHT, RESET_HIGHLIGHT } from '@storybook/addon-highlight';
import { MyComponent } form './MyComponent';

export default { component: MyComponent };

export const MyStory = () => {
const emit = useChannel({});

useEffect(() => {
emit(HIGHLIGHT, {
elements: ['.title', '.subtitle'],
});
}, []);

return <MyComponent />;
};
```

Highlights are automatically cleared when the story changes. You can also manually clear them by emitting the `RESET_HIGHLIGHT` event.

```js
emit(RESET_HIGHLIGHT);
```

### Customize style

```js
emit(HIGHLIGHT, {
elements: ['.title', '.subtitle'],
color: 'red',
style: 'solid', // 'dotted' | 'dashed' | 'solid' | 'double'
});
```
59 changes: 59 additions & 0 deletions addons/highlight/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
{
"name": "@storybook/addon-highlight",
"version": "7.0.0-alpha.6",
"description": "Highlight DOM nodes within your stories",
"keywords": [
"storybook-addons",
"essentials",
"style",
"appearance"
],
"homepage": "https://github.com/storybookjs/storybook/tree/main/addons/highlight",
"bugs": {
"url": "https://github.com/storybookjs/storybook/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/storybookjs/storybook.git",
"directory": "addons/highlight"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/storybook"
},
"license": "MIT",
"author": "winkerVSbecks",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
"types": "dist/types/index.d.ts",
"files": [
"dist/**/*",
"README.md",
"*.js",
"*.d.ts"
],
"scripts": {
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "7.0.0-alpha.6",
"@storybook/core-events": "7.0.0-alpha.6",
"core-js": "^3.8.2",
"global": "^4.4.0"
},
"devDependencies": {
"@types/webpack-env": "^1.16.0"
},
"publishConfig": {
"access": "public"
},
"gitHead": "6cf4571e5a1200613de94aa066fe93f75aec6ad1",
"sbmodern": "dist/modern/index.js",
"storybook": {
"displayName": "Highlight",
"unsupportedFrameworks": [
"react-native"
],
"icon": "https://user-images.githubusercontent.com/42671/162045505-9d06fe2e-8fcb-4c41-9486-e0553bce10cc.png"
}
}
1 change: 1 addition & 0 deletions addons/highlight/preview.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './dist/esm/highlight';
5 changes: 5 additions & 0 deletions addons/highlight/src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const ADDON_ID = 'storybook/highlight';
export const HIGHLIGHT_STYLE_ID = 'storybookHighlight';

export const HIGHLIGHT = `${ADDON_ID}/add`;
export const RESET_HIGHLIGHT = `${ADDON_ID}/reset`;
Original file line number Diff line number Diff line change
@@ -1,12 +1,25 @@
/* eslint-env browser */
import global from 'global';
import { addons } from '@storybook/addons';
import { STORY_CHANGED } from '@storybook/core-events';
import { EVENTS, HIGHLIGHT_STYLE_ID } from './constants';

import { highlightStyle } from './highlight';
import { HIGHLIGHT, RESET_HIGHLIGHT, HIGHLIGHT_STYLE_ID } from './constants';

const { document } = global;

type OutlineStyle = 'dotted' | 'dashed' | 'solid' | 'double';

export const highlightStyle = (color = '#FF4785', style: OutlineStyle = 'dashed') => `
outline: 2px ${style} ${color};
outline-offset: 2px;
box-shadow: 0 0 0 6px rgba(255,255,255,0.6);
`;

export const highlightObject = (color: string) => ({
outline: `2px dashed ${color}`,
outlineOffset: 2,
boxShadow: '0 0 0 6px rgba(255,255,255,0.6)',
});

if (module && module.hot && module.hot.decline) {
module.hot.decline();
}
Expand All @@ -15,6 +28,7 @@ interface HighlightInfo {
/** html selector of the element */
elements: string[];
color: string;
style: OutlineStyle;
}

const channel = addons.getChannel();
Expand All @@ -32,7 +46,7 @@ const highlight = (infos: HighlightInfo) => {
.map(
(target) =>
`${target}{
${highlightStyle(infos.color)}
${highlightStyle(infos.color, infos.style)}
}`
)
.join(' ');
Expand All @@ -48,4 +62,5 @@ const resetHighlight = () => {
};

channel.on(STORY_CHANGED, resetHighlight);
channel.on(EVENTS.HIGHLIGHT, highlight);
channel.on(RESET_HIGHLIGHT, resetHighlight);
channel.on(HIGHLIGHT, highlight);
8 changes: 8 additions & 0 deletions addons/highlight/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export { HIGHLIGHT, RESET_HIGHLIGHT } from './constants';

if (module && module.hot && module.hot.decline) {
module.hot.decline();
}

// make it work with --isolatedModules
export default {};
1 change: 1 addition & 0 deletions addons/highlight/src/typings.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
declare module 'global';
20 changes: 20 additions & 0 deletions addons/highlight/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"rootDir": "./src",
"types": [
"webpack-env"
]
},
"include": [
"src/**/*"
],
"exclude": [
"src/**/*.test.*",
"src/**/tests/**/*",
"src/**/__tests__/**/*",
"src/**/*.stories.*",
"src/**/*.mockdata.*",
"src/**/__testfixtures__/**"
]
}
50 changes: 50 additions & 0 deletions docs/essentials/highlight.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---
title: 'Highlight'
---

Storybook's [Highlight](https://storybook.js.org/addons/@storybook/addon-highlight/) addon allows you to highlight specific DOM nodes within your story. You can use it to call attention to particular parts of the story.

![](highlight.png)

This addon can be used to enhance other addons. For example, [Accessibility](https://storybook.js.org/addons/@storybook/addon-a11y/) addon uses it to highlight DOM nodes that are failing accessibility checks.

## Apply or clear highlights

Highlight DOM nodes by emitting the `HIGHLIGHT` event from within a story or an addon. The event payload must contain a list of selectors you want to highlight.

<!-- prettier-ignore-start -->

<CodeSnippets
paths={[
'react/component-story-highlight-addon.js.mdx',
'angular/component-story-highlight-addon.ts.mdx',
'vue/component-story-highlight-addon.js.mdx',
'web-components/component-story-highlight-addon.js.mdx',
]}
/>

<!-- prettier-ignore-end -->

Highlights are automatically cleared when the story changes. You can also manually clear them by emitting the `RESET_HIGHLIGHT` event.

<!-- prettier-ignore-start -->

<CodeSnippets
paths={[
'common/addon-highlight-reset.js.mdx',
]}
/>

<!-- prettier-ignore-end -->

## Customize style

<!-- prettier-ignore-start -->

<CodeSnippets
paths={[
'common/addon-highlight-customize.js.mdx',
]}
/>

<!-- prettier-ignore-end -->
Binary file added docs/essentials/highlight.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 4eefe54

Please sign in to comment.