diff --git a/lib/storybook/csf.js b/lib/storybook/csf.js index a277b0d..04d465c 100644 --- a/lib/storybook/csf.js +++ b/lib/storybook/csf.js @@ -54,7 +54,7 @@ function readParams(obj, acc = {}) { }); } - if (acc.config.rules) { + if (acc.config && acc.config.rules) { acc.config.rules = acc.config.rules.reduce((prev, value) => { prev[value.id] = value; delete prev[value.id].id; @@ -66,6 +66,41 @@ function readParams(obj, acc = {}) { return acc; } +function getConfig(parameters) { + let a11yConfig = null; + + if (parameters.properties) { + parameters.properties.some(param => { + if (param.key && param.key.name === 'a11y') { + a11yConfig = readParams(param.value); + + return true; + } + + return false; + }); + } + + return a11yConfig; +} + +function mergeObjects(firstObject, secondObject) { + if (!firstObject && !secondObject) { + return null; + } + + const result = {...(firstObject || {})}; + + for (const [key, value] of Object.entries(secondObject || {})) { + result[key] = + value && typeof value === 'object' && !Array.isArray(value) + ? mergeObjects(result[key] || {}, value) + : value; + } + + return result; +} + module.exports = { async parse(storyPath, returnMetaOnly = false, pluginSettings = {}) { let csf; @@ -80,8 +115,8 @@ module.exports = { throw error; } - const parameters = csf._metaAnnotations.parameters || {}; const title = csf._meta.title; + const a11yParameters = getConfig(csf._metaAnnotations.parameters || {}); if (returnMetaOnly) { return { @@ -90,23 +125,10 @@ module.exports = { }; } - let a11yConfig = null; - - if (parameters.properties) { - parameters.properties.some(param => { - if (param.key && param.key.name === 'a11y') { - a11yConfig = readParams(param.value); - - return true; - } - - return false; - }); - } - return Object.entries(csf._stories) .map(([key, story]) => { const {id, name, parameters} = story; + const storyParameters = getConfig(csf._storyAnnotations[key].parameters || {}); if (key === '__page') { // If key is default, current story is in docs mode, which @@ -121,9 +143,9 @@ module.exports = { id, name, viewMode, - a11yConfig, + a11yConfig: mergeObjects(a11yParameters, storyParameters), storyPath: path.resolve(storyPath) }; }); } -}; +}; \ No newline at end of file diff --git a/nightwatch/globals.js b/nightwatch/globals.js index a5270e1..9c86136 100644 --- a/nightwatch/globals.js +++ b/nightwatch/globals.js @@ -10,12 +10,32 @@ const STORYBOOK_PORT_RE = /(?:localhost|127.0.0.1):(\d+)\/?$/; let storybookPid = null; +const isWinPlatform = /^win/.test(process.platform); + const getStorybookUrl = function() { const pluginSettings = Object.assign(defaultSettings, this.settings['@nightwatch/storybook'] || {}); return pluginSettings.storybook_url; }; +const createProcess = function (storybookPort) { + return spawn(path.resolve(`node_modules/.bin/start-storybook${isWinPlatform ? '.cmd' : ''}`), + ['--no-open', '-p', String(storybookPort)], { + cwd: process.cwd(), + stdio: 'inherit' + }).pid; +}; + +const endProcess = function () { + if (isWinPlatform) { + spawn('taskkill', ['/pid', storybookPid, '/f', '/t']); + + return; + } + + process.kill(storybookPid); +}; + module.exports = { beforeEach() { // child processes don't have access to the context from the before() hook @@ -55,9 +75,7 @@ module.exports = { // eslint-disable-next-line no-console console.info(chalk.dim(` Starting storybook at: ${storybookUrl}...`)); - storybookPid = spawn(path.resolve('node_modules/.bin/start-storybook'), ['--no-open', '-p', String(storybookPort)], { - cwd: process.cwd() - }).pid; + storybookPid = createProcess(storybookPort); await waitOn({ resources: [storybookUrl] @@ -89,7 +107,7 @@ module.exports = { async after() { if (storybookPid) { - process.kill(storybookPid); + endProcess(); } } }; diff --git a/stories/Button.stories.jsx b/stories/Button.stories.jsx index 44c6e5f..4e26b5c 100644 --- a/stories/Button.stories.jsx +++ b/stories/Button.stories.jsx @@ -69,3 +69,21 @@ Small.args = { size: 'small', label: 'Button', }; + +// https://storybook.js.org/docs/react/writing-tests/accessibility-testing#story-level-a11y-configuration +export const EmptyButton = Button.bind({}); +EmptyButton.args = { + size: 'small', +}; +EmptyButton.parameters = { + a11y: { + config: { + rules: [ + { + id: 'button-name', + enabled: false, + }, + ] + } + }, +}; \ No newline at end of file