From 7c0af5f12e65d03a5d4bb212a920ef721b5df560 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 31 Mar 2020 09:09:51 +0000 Subject: [PATCH 1/6] Bump @types/react-select from 2.0.19 to 3.0.11 Bumps [@types/react-select](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-select) from 2.0.19 to 3.0.11. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-select) Signed-off-by: dependabot-preview[bot] --- addons/knobs/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/addons/knobs/package.json b/addons/knobs/package.json index 2de0cdfaf80..235c800db59 100644 --- a/addons/knobs/package.json +++ b/addons/knobs/package.json @@ -54,7 +54,7 @@ "@types/enzyme": "^3.10.5", "@types/escape-html": "0.0.20", "@types/react-lifecycles-compat": "^3.0.1", - "@types/react-select": "^2.0.19", + "@types/react-select": "^3.0.11", "@types/webpack-env": "^1.15.1", "enzyme": "^3.11.0" }, diff --git a/yarn.lock b/yarn.lock index 2709a610bd0..6d4b8f74754 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4299,10 +4299,10 @@ hoist-non-react-statics "^3.3.0" redux "^4.0.0" -"@types/react-select@^2.0.19": - version "2.0.19" - resolved "https://registry.yarnpkg.com/@types/react-select/-/react-select-2.0.19.tgz#59a80ef81a4a5cb37f59970c53a4894d15065199" - integrity sha512-5GGBO3npQ0G/poQmEn+kI3Vn3DoJ9WjRXCeGcpwLxd5rYmjYPH235lbYPX5aclXE2RqEXyFxd96oh0wYwPXYpg== +"@types/react-select@^3.0.11": + version "3.0.11" + resolved "https://registry.yarnpkg.com/@types/react-select/-/react-select-3.0.11.tgz#b69b6fe1999bedfb05bd7499327206e16a7fb00e" + integrity sha512-ggUsAdZuRFtLMjGMcdf9SeeE678TRq3lAKj1fbwGM8JAZTIzCu1CED0dvJgFVCPT2bDs8TcBD6+6SN6i4e7JYQ== dependencies: "@types/react" "*" "@types/react-dom" "*" From d0eb9f33eaf83731f5960d44b17f634eefa9d657 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 7 Apr 2020 07:34:54 +0000 Subject: [PATCH 2/6] Bump strip-json-comments from 3.0.1 to 3.1.0 Bumps [strip-json-comments](https://github.com/sindresorhus/strip-json-comments) from 3.0.1 to 3.1.0. - [Release notes](https://github.com/sindresorhus/strip-json-comments/releases) - [Commits](https://github.com/sindresorhus/strip-json-comments/compare/v3.0.1...v3.1.0) Signed-off-by: dependabot-preview[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index a9215745796..033388609dc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -28101,9 +28101,9 @@ strip-json-comments@^2.0.1, strip-json-comments@~2.0.1: integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= strip-json-comments@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7" - integrity sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw== + version "3.1.0" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.0.tgz#7638d31422129ecf4457440009fba03f9f9ac180" + integrity sha512-e6/d0eBu7gHtdCqFt0xJr642LdToM5/cN4Qb9DbHjVx1CP5RyeM+zH7pbecEmDv/lBqb0QH+6Uqq75rxFPkM0w== strong-log-transformer@^2.0.0: version "2.1.0" From 277c801ebaa7f8034561d9e53a2e230bb66e59e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Dungler?= Date: Sat, 11 Apr 2020 00:02:50 +0200 Subject: [PATCH 3/6] feat(A11y): remove decorator in favor of configuration --- MIGRATION.md | 18 ++- addons/a11y/README.md | 3 - addons/a11y/src/a11yRunner.ts | 56 +++++++++ addons/a11y/src/components/A11YPanel.tsx | 2 +- addons/a11y/src/index.ts | 116 ------------------ addons/a11y/src/preset.ts | 7 ++ addons/a11y/src/preset/addDecorator.ts | 3 - addons/a11y/src/preset/index.ts | 15 --- .../pages/configurations/overview/index.md | 4 +- docs/src/pages/formats/storiesof-api/index.md | 2 +- .../ember-cli/stories/addon-a11y.stories.js | 2 - 11 files changed, 80 insertions(+), 148 deletions(-) create mode 100644 addons/a11y/src/a11yRunner.ts create mode 100644 addons/a11y/src/preset.ts delete mode 100644 addons/a11y/src/preset/addDecorator.ts delete mode 100644 addons/a11y/src/preset/index.ts diff --git a/MIGRATION.md b/MIGRATION.md index eed750c6987..2862d3d33fb 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -18,6 +18,8 @@ - [Actions Addon API changes](#actions-addon-api-changes) - [Actions Addon uses parameters](#actions-addon-uses-parameters) - [Removed action decorator APIs](#removed-action-decorator-apis) + - [Removed addon centered](#removed-addon-centered) + - [Removed withA11y decorator](#removed-witha11y-decorator) - [From version 5.2.x to 5.3.x](#from-version-52x-to-53x) - [To main.js configuration](#to-mainjs-configuration) - [Using main.js](#using-mainjs) @@ -213,9 +215,9 @@ module.exports = { In earlier versions of Storybook, this would automatically call `@storybook/addon-a11y/register`, which adds the the a11y panel to the Storybook UI. As a user you would also add a decorator: ```js -import { withA11y } from '../index'; +import { withKnobs } from '../index'; -addDecorator(withA11y); +addDecorator(withKnobs); ``` Now in 6.0, `addon-a11y` comes with a preset, `@storybook/addon-a11y/preset`, that does this automatically for you. This change simplifies configuration, since now you don't need to add that decorator. @@ -386,10 +388,16 @@ export const MyStory = () =>
my story
; MyStory.story = { parameters: { layout: 'centered' }, }; - ``` + Other possible values are: `padded` (default) and `fullscreen`. +#### Removed withA11y decorator + +In 6.0 we removed the `withA11y` decorator. The code that runs accessibility checks is now directly injected in the preview. + +Remove the addon-centered decorator. Nothing else to do + ## From version 5.2.x to 5.3.x ### To main.js configuration @@ -726,7 +734,7 @@ var sortedModules = modules.slice().sort((a, b) => { }); // execute them -sortedModules.forEach(key => { +sortedModules.forEach((key) => { context(key); }); ``` @@ -1290,7 +1298,7 @@ Here's an example of using Notes and Info in 3.2 with the new API. storiesOf('composition', module).add( 'new addons api', withInfo('see Notes panel for composition info')( - withNotes({ text: 'Composition: Info(Notes())' })(context => ( + withNotes({ text: 'Composition: Info(Notes())' })((context) => ( )) ) diff --git a/addons/a11y/README.md b/addons/a11y/README.md index 60699897d88..6ac661f2ba9 100755 --- a/addons/a11y/README.md +++ b/addons/a11y/README.md @@ -56,11 +56,8 @@ You can override these options [at story level too](https://storybook.js.org/doc import React from 'react'; import { storiesOf, addDecorator, addParameters } from '@storybook/react'; -import { withA11y } from '@storybook/addon-a11y'; - export default { title: 'button', - decorators: [withA11y], parameters: { a11y: { // optional selector which element to inspect diff --git a/addons/a11y/src/a11yRunner.ts b/addons/a11y/src/a11yRunner.ts new file mode 100644 index 00000000000..15fc85d585c --- /dev/null +++ b/addons/a11y/src/a11yRunner.ts @@ -0,0 +1,56 @@ +import { document, window } from 'global'; +import { STORY_RENDERED } from '@storybook/core-events'; +import axe, { ElementContext, RunOptions, Spec } from 'axe-core'; +import addons from '@storybook/addons'; +import { EVENTS } from './constants'; + +interface Setup { + element?: ElementContext; + config: Spec; + options: RunOptions; +} + +const channel = addons.getChannel(); +let active = false; + +const getElement = () => { + const storyRoot = document.getElementById('story-root'); + return storyRoot ? storyRoot.children : document.getElementById('root'); +}; + +const run = async (storyId: string) => { + try { + const input = getParams(storyId); + + if (!active) { + active = true; + const { + element = getElement(), + config, + options = { + restoreScroll: true, + }, + } = input; + axe.reset(); + if (config) { + axe.configure(config); + } + + const result = await axe.run(element, options); + channel.emit(EVENTS.RESULT, result); + } + } catch (error) { + channel.emit(EVENTS.ERROR, error); + } finally { + active = false; + } +}; + +const getParams = (storyId: string): Setup => { + // eslint-disable-next-line no-underscore-dangle + const { parameters } = window.__STORYBOOK_STORY_STORE__._stories[storyId] || {}; + return parameters.a11y; +}; + +channel.on(STORY_RENDERED, run); +channel.on(EVENTS.REQUEST, run); diff --git a/addons/a11y/src/components/A11YPanel.tsx b/addons/a11y/src/components/A11YPanel.tsx index 2befa30cc5a..0e21c351aa2 100644 --- a/addons/a11y/src/components/A11YPanel.tsx +++ b/addons/a11y/src/components/A11YPanel.tsx @@ -169,7 +169,7 @@ export class A11YPanel extends Component { status: 'running', }, () => { - api.emit(EVENTS.REQUEST); + api.emit(EVENTS.REQUEST, api.getCurrentStoryData().id); // removes all elements from the redux map in store from the previous panel store.dispatch(clearElements()); } diff --git a/addons/a11y/src/index.ts b/addons/a11y/src/index.ts index 6a55d9c7e8d..b9ba57b9181 100644 --- a/addons/a11y/src/index.ts +++ b/addons/a11y/src/index.ts @@ -1,119 +1,3 @@ -import { document } from 'global'; -import debounce from 'lodash/debounce'; -import memoize from 'memoizerific'; -import axe, { AxeResults, ElementContext, RunOptions, Spec } from 'axe-core'; - -import addons, { DecoratorFunction } from '@storybook/addons'; -import { STORY_RENDERED } from '@storybook/core-events'; -import { Listener } from '@storybook/channels'; -import { EVENTS, PARAM_KEY } from './constants'; - -interface Setup { - element?: ElementContext; - config: Spec; - options: RunOptions; - manual: boolean; -} - -const setup: Setup = { element: undefined, config: {}, options: {}, manual: false }; - -const getElement = () => { - const storyRoot = document.getElementById('story-root'); - - if (storyRoot) { - return storyRoot.children; - } - return document.getElementById('root'); -}; - -const performRun = (() => { - let isRunning = false; - - return debounce(async (s, callback) => { - if (isRunning) { - return; - } - - isRunning = true; - - await run(s) - .then( - (result) => callback(undefined, result), - (error) => callback(error) - ) - .then(() => { - isRunning = false; - }); - }, 100); -})(); - -const run = async (input: Setup) => { - const { - element = getElement(), - config, - options = { - restoreScroll: true, - }, - } = input; - - await axe.reset(); - - if (config) { - await axe.configure(config); - } - - return axe.run(element, options); -}; - if (module && module.hot && module.hot.decline) { module.hot.decline(); } - -let storedDefaultSetup: Setup | null = null; - -const performSetup = (parameter: Partial | undefined) => { - if (parameter) { - if (storedDefaultSetup === null) { - storedDefaultSetup = { ...setup }; - } - Object.assign(setup, parameter); - } - if (storedDefaultSetup !== null) { - Object.assign(setup, storedDefaultSetup); - storedDefaultSetup = null; - } -}; - -const usePermanentChannel = memoize(1)((eventMap: Record) => { - const channel = addons.getChannel(); - const emit = channel.emit.bind(channel); - - Object.entries(eventMap).forEach(([type, handler]) => { - channel.on(type, handler); - }); - - return emit; -}); - -export const withA11y: DecoratorFunction = (storyFn, storyContext) => { - const respond = () => { - const parameter = storyContext.parameters[PARAM_KEY] as Partial; - - performSetup(parameter); - - performRun(setup, (error: Error, result: AxeResults) => { - if (error) { - emit(EVENTS.ERROR, String(error)); - } else { - emit(EVENTS.RESULT, result); - } - }); - }; - - const emit = usePermanentChannel({ - [EVENTS.REQUEST]: respond, - [STORY_RENDERED]: respond, - }); - - return storyFn(storyContext); -}; diff --git a/addons/a11y/src/preset.ts b/addons/a11y/src/preset.ts new file mode 100644 index 00000000000..609bda3f7c9 --- /dev/null +++ b/addons/a11y/src/preset.ts @@ -0,0 +1,7 @@ +export function managerEntries(entry: any[] = []) { + return [...entry, require.resolve('./register')]; +} + +export function config(entry: any[] = []) { + return [...entry, require.resolve('./a11yRunner')]; +} diff --git a/addons/a11y/src/preset/addDecorator.ts b/addons/a11y/src/preset/addDecorator.ts deleted file mode 100644 index be3b27b26f7..00000000000 --- a/addons/a11y/src/preset/addDecorator.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { withA11y } from '../index'; - -export const decorators = [withA11y]; diff --git a/addons/a11y/src/preset/index.ts b/addons/a11y/src/preset/index.ts deleted file mode 100644 index 2dac2590598..00000000000 --- a/addons/a11y/src/preset/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -type A11yOptions = { - addDecorator?: boolean; -}; - -export function managerEntries(entry: any[] = []) { - return [...entry, require.resolve('../register')]; -} - -export function config(entry: any[] = [], { addDecorator = true }: A11yOptions = {}) { - const a11yConfig = []; - if (addDecorator) { - a11yConfig.push(require.resolve('./addDecorator')); - } - return [...entry, ...a11yConfig]; -} diff --git a/docs/src/pages/configurations/overview/index.md b/docs/src/pages/configurations/overview/index.md index 36e272d11fa..d523b7331f2 100644 --- a/docs/src/pages/configurations/overview/index.md +++ b/docs/src/pages/configurations/overview/index.md @@ -48,9 +48,9 @@ In `preview.js` you can add global [decorators](../../basics/writing-stories/#de ```js // preview.js import { addDecorator } from '@storybook/svelte'; -import { withA11y } from '@storybook/addon-a11y'; +import { withKnobs } from '@storybook/addon-knobs'; -addDecorator(withA11y); +addDecorator(withKnobs); ``` In `manager.js` you can add [UI options](../options-parameter/#global-options). diff --git a/docs/src/pages/formats/storiesof-api/index.md b/docs/src/pages/formats/storiesof-api/index.md index c0d8e11af1a..11c1e08054e 100644 --- a/docs/src/pages/formats/storiesof-api/index.md +++ b/docs/src/pages/formats/storiesof-api/index.md @@ -71,7 +71,7 @@ And finally, story-level decorators are provided via parameters: storiesOf('Button', module).add( 'with text', () => , - { decorators: withA11y } + { decorators: withKnobs } ); ``` diff --git a/examples/ember-cli/stories/addon-a11y.stories.js b/examples/ember-cli/stories/addon-a11y.stories.js index 842e4b570e5..e09bf7c19b1 100644 --- a/examples/ember-cli/stories/addon-a11y.stories.js +++ b/examples/ember-cli/stories/addon-a11y.stories.js @@ -1,9 +1,7 @@ import { hbs } from 'ember-cli-htmlbars'; -import { withA11y } from '@storybook/addon-a11y'; export default { title: 'Addon/a11y', - decorators: [withA11y], parameters: { options: { selectedPanel: '@storybook/a11y/panel' }, From 86d9e3b0e344ab7312540178121d53ce3a1248b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Dungler?= Date: Mon, 13 Apr 2020 23:02:30 +0200 Subject: [PATCH 4/6] cleanup migration --- MIGRATION.md | 32 ++++++++++++++++++++++---------- addons/a11y/src/a11yRunner.ts | 4 ++++ 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index 2862d3d33fb..8adcd4880b5 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -203,16 +203,16 @@ In Storybook 5.3 we introduced a declarative [main.js configuration](#to-mainjs- This breaking change currently applies to: `addon-a11y`, `addon-actions`, `addon-knobs`, `addon-links`, `addon-queryparams`. -Consider the following `main.js` config for the accessibility addon, `addon-a11y`: +Consider the following `main.js` config for the accessibility addon, `addon-knobs`: ```js module.exports = { stories: ['../**/*.stories.js'], - addons: ['@storybook/addon-a11y'], + addons: ['@storybook/addon-knobs'], }; ``` -In earlier versions of Storybook, this would automatically call `@storybook/addon-a11y/register`, which adds the the a11y panel to the Storybook UI. As a user you would also add a decorator: +In earlier versions of Storybook, this would automatically call `@storybook/addon-knobs/register`, which adds the the knobs panel to the Storybook UI. As a user you would also add a decorator: ```js import { withKnobs } from '../index'; @@ -220,24 +220,24 @@ import { withKnobs } from '../index'; addDecorator(withKnobs); ``` -Now in 6.0, `addon-a11y` comes with a preset, `@storybook/addon-a11y/preset`, that does this automatically for you. This change simplifies configuration, since now you don't need to add that decorator. +Now in 6.0, `addon-knobs` comes with a preset, `@storybook/addon-knobs/preset`, that does this automatically for you. This change simplifies configuration, since now you don't need to add that decorator. If you wish to disable this new behavior, you can modify your `main.js` to force it to use the `register` logic rather than the `preset`: ```js module.exports = { stories: ['../**/*.stories.js'], - addons: ['@storybook/addon-a11y/register'], + addons: ['@storybook/addon-knobs/register'], }; ``` -If you wish to selectively disable `a11y` checks for a subset of stories, you can control this with story parameters: +If you wish to selectively disable `knobs` checks for a subset of stories, you can control this with story parameters: ```js export const MyNonCheckedStory = () => ; MyNonCheckedStory.story = { parameters: { - a11y: { disable: true }, + knobs: { disable: true }, }, }; ``` @@ -396,7 +396,19 @@ Other possible values are: `padded` (default) and `fullscreen`. In 6.0 we removed the `withA11y` decorator. The code that runs accessibility checks is now directly injected in the preview. -Remove the addon-centered decorator. Nothing else to do +Remove the addon-a11y decorator. +To configure a11y now, you have to specify configuration using `addParameters`. + +```js +addParameters({ + a11y: { + element: "#root", + config: {}, + options: {}, + manual: true, + } +}; +``` ## From version 5.2.x to 5.3.x @@ -734,7 +746,7 @@ var sortedModules = modules.slice().sort((a, b) => { }); // execute them -sortedModules.forEach((key) => { +sortedModules.forEach(key => { context(key); }); ``` @@ -1298,7 +1310,7 @@ Here's an example of using Notes and Info in 3.2 with the new API. storiesOf('composition', module).add( 'new addons api', withInfo('see Notes panel for composition info')( - withNotes({ text: 'Composition: Info(Notes())' })((context) => ( + withNotes({ text: 'Composition: Info(Notes())' })(context => ( )) ) diff --git a/addons/a11y/src/a11yRunner.ts b/addons/a11y/src/a11yRunner.ts index 15fc85d585c..21c44261cdb 100644 --- a/addons/a11y/src/a11yRunner.ts +++ b/addons/a11y/src/a11yRunner.ts @@ -4,6 +4,10 @@ import axe, { ElementContext, RunOptions, Spec } from 'axe-core'; import addons from '@storybook/addons'; import { EVENTS } from './constants'; +if (module && module.hot && module.hot.decline) { + module.hot.decline(); +} + interface Setup { element?: ElementContext; config: Spec; From f92f3b3af209a5bc6dc0c83a36f8a3d83cc25187 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Tue, 14 Apr 2020 11:37:36 +0200 Subject: [PATCH 5/6] FIX tests --- addons/a11y/src/components/A11YPanel.test.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addons/a11y/src/components/A11YPanel.test.js b/addons/a11y/src/components/A11YPanel.test.js index bc54777e3f4..300fdef83fb 100644 --- a/addons/a11y/src/components/A11YPanel.test.js +++ b/addons/a11y/src/components/A11YPanel.test.js @@ -12,6 +12,8 @@ function createApi() { jest.spyOn(emitter, 'emit'); jest.spyOn(emitter, 'on'); jest.spyOn(emitter, 'off'); + + emitter.getCurrentStoryData = () => ({ id: '1' }); return emitter; } @@ -130,7 +132,7 @@ describe('A11YPanel', () => { // then expect(wrapper.text()).toMatch(/Please wait while the accessibility scan is running/); - expect(api.emit).toHaveBeenCalledWith(EVENTS.REQUEST); + expect(api.emit).toHaveBeenCalledWith(EVENTS.REQUEST, '1'); }); it('should handle "ran" status', () => { From 415813bd96ef4bb89621f9daa2e5c77aa550e898 Mon Sep 17 00:00:00 2001 From: Valery Bugakov Date: Tue, 14 Apr 2020 21:08:21 +0800 Subject: [PATCH 6/6] fix(to-require-context): fixed regex to glob conversion --- .../src/frameworks/configure.ts | 7 +- lib/core/package.json | 2 +- .../src/server/preview/to-require-context.js | 8 +-- .../server/preview/to-require-context.test.js | 68 +++++++++++++++++++ yarn.lock | 2 +- 5 files changed, 73 insertions(+), 14 deletions(-) create mode 100644 lib/core/src/server/preview/to-require-context.test.js diff --git a/addons/storyshots/storyshots-core/src/frameworks/configure.ts b/addons/storyshots/storyshots-core/src/frameworks/configure.ts index 9c1f7673585..abd5b75559a 100644 --- a/addons/storyshots/storyshots-core/src/frameworks/configure.ts +++ b/addons/storyshots/storyshots-core/src/frameworks/configure.ts @@ -54,12 +54,7 @@ function getConfigPathParts(input: string): Output { (pattern: string | { path: string; recursive: boolean; match: string }) => { const { path: basePath, recursive, match } = toRequireContext(pattern); // eslint-disable-next-line no-underscore-dangle - return global.__requireContext( - configDir, - basePath, - recursive, - new RegExp(match.slice(1, -1)) - ); + return global.__requireContext(configDir, basePath, recursive, match); } ); } diff --git a/lib/core/package.json b/lib/core/package.json index bc66a7900db..e70972907cf 100644 --- a/lib/core/package.json +++ b/lib/core/package.json @@ -85,7 +85,7 @@ "ip": "^1.1.5", "json5": "^2.1.1", "lazy-universal-dotenv": "^3.0.1", - "micromatch": "^4.0.2", + "nanomatch": "^1.2.13", "node-fetch": "^2.6.0", "pkg-dir": "^4.2.0", "pnp-webpack-plugin": "1.6.4", diff --git a/lib/core/src/server/preview/to-require-context.js b/lib/core/src/server/preview/to-require-context.js index 316733976b6..cc4b81a0c03 100644 --- a/lib/core/src/server/preview/to-require-context.js +++ b/lib/core/src/server/preview/to-require-context.js @@ -1,16 +1,12 @@ import globBase from 'glob-base'; -import { makeRe } from 'micromatch'; +import { makeRe } from 'nanomatch'; const isObject = (val) => val != null && typeof val === 'object' && Array.isArray(val) === false; export const toRequireContext = (input) => { switch (true) { case typeof input === 'string': { const { base, glob } = globBase(input); - const regex = makeRe(glob) - .toString() - // webpack prepends the relative path with './' - .replace(/^\/\^/, '/^\\.\\/') - .replace(/\?:\^/g, '?:'); + const regex = makeRe(glob); return { path: base, recursive: glob.startsWith('**'), match: regex }; } diff --git a/lib/core/src/server/preview/to-require-context.test.js b/lib/core/src/server/preview/to-require-context.test.js new file mode 100644 index 00000000000..30611a66c67 --- /dev/null +++ b/lib/core/src/server/preview/to-require-context.test.js @@ -0,0 +1,68 @@ +import path from 'path'; +import { toRequireContext } from './to-require-context'; + +const testCases = [ + { + glob: '**/*.stories.tsx', + validPaths: [ + './Icon.stories.tsx', + './src/Icon.stories.tsx', + './src/components/Icon.stories.tsx', + './src/components/Icon.stories/Icon.stories.tsx', + ], + invalidPaths: [ + './stories.tsx', + './Icon.stories.ts', + './Icon.stories.js', + './src/components/stories.tsx', + './src/components/Icon.stories/stories.tsx', + './src/components/Icon.stories.ts', + './src/components/Icon.stories.js', + ], + }, + { + glob: '../src/stories/**/*.stories.(js|mdx)', + validPaths: [ + '../src/stories/components/Icon.stories.js', + '../src/stories/Icon.stories.js', + '../src/stories/Icon.stories.mdx', + '../src/stories/components/Icon/Icon.stories.js', + '../src/stories/components/Icon.stories/Icon.stories.mdx', + ], + invalidPaths: [ + './stories.js', + './src/stories/Icon.stories.js', + './Icon.stories.js', + '../src/Icon.stories.mdx', + '../src/stories/components/Icon/Icon.stories.ts', + '../src/stories/components/Icon/Icon.mdx', + ], + }, + { + glob: 'dirname/../stories/*.stories.*', + validPaths: [ + './dirname/../stories/App.stories.js', + './dirname/../stories/addon-centered.stories.js', + ], + invalidPaths: ['./dirname/../stories.js', './dirname/../App.stories.js'], + }, +]; + +describe('toRequireContext', () => { + it('matches only suitable paths', () => { + testCases.forEach(({ glob, validPaths, invalidPaths }) => { + const { path: base, match: regex } = toRequireContext(glob); + + function isMatched(filePath) { + const relativePath = `./${path.relative(base, filePath)}`; + return filePath.includes(base) && regex.test(relativePath); + } + + const isMatchedForValidPaths = validPaths.every(isMatched); + const isMatchedForInvalidPaths = invalidPaths.every(filePath => !isMatched(filePath)); + + expect(isMatchedForValidPaths).toBe(true); + expect(isMatchedForInvalidPaths).toBe(true); + }); + }); +}); diff --git a/yarn.lock b/yarn.lock index 1e5550e83ec..d70fb474543 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20880,7 +20880,7 @@ nan@^2.12.1, nan@^2.13.2: resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== -nanomatch@^1.2.9: +nanomatch@^1.2.13, nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==