Skip to content
This repository was archived by the owner on Dec 23, 2024. It is now read-only.

Commit 47c7a1b

Browse files
committedDec 17, 2020
Sync DSM Storybook Integration codebases
1 parent 679bccd commit 47c7a1b

14 files changed

+182
-59
lines changed
 

‎bin/dsm-storybook.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ program
1010
.option('--dsm-host [host]', 'The DSM server')
1111
.option('--auth-token [token]', 'Key provided to you by DSM to authenticate uploads')
1212
.option('--story-path [glob]', 'Path to folder containing storybook stories')
13-
.option('-o, --output-dir [path]', 'Path to folder to store the DSM build artifacts');
13+
.option('-o, --output-dir [path]', 'Path to folder to store the DSM build artifacts')
14+
.option('--default-docs-tab', 'Load the docs tab by default on web')
15+
.option('--hide-dsm-generated-table', 'Hide the DSM Props Table and Sample Code on your DSM website');
1416

1517
program
1618
.command('publish')
@@ -77,6 +79,7 @@ function getConfigurations() {
7779
* */
7880
function optionsFromArgs() {
7981
const configurationKeys = buildConfiguration.configurationKeys;
82+
8083
return keys(configurationKeys).reduce((acc, key) => {
8184
if (program[key]) {
8285
acc[configurationKeys[key]] = program[key];

‎config/webpack-node.config.dev.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const webpackConfigBase = require('./webpack-node.config.base');
77
const devWebpackConfig = {
88
devtool: false,
99
plugins: [
10-
new WebpackShellPlugin({ dev: false, onBuildEnd: ['yalc publish --no-sig --force --push'] }),
10+
new WebpackShellPlugin({ dev: false, onBuildEnd: ['yalc publish --yarn --no-sig --force --push'] }),
1111
new webpack.SourceMapDevToolPlugin({
1212
filename: '[file].map'
1313
})

‎config/webpack.config.dev.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const webpackConfigBase = require('./webpack.config.base');
66

77
const devWebpackConfig = {
88
devtool: 'eval-source-map',
9-
plugins: [new WebpackShellPlugin({ dev: false, onBuildEnd: ['yalc publish --no-sig --force --push'] })]
9+
plugins: [new WebpackShellPlugin({ dev: false, onBuildEnd: ['yalc publish --yarn --no-sig --force --push'] })]
1010
};
1111

1212
module.exports = _.merge({}, webpackConfigBase, devWebpackConfig);

‎package.json

+5-5
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"test": "NODE_ENV=test jest --config jest.config.json",
1717
"test-local": "docker-compose -f docker-compose.yml -f docker-compose-test.yml up",
1818
"lint": "eslint src",
19-
"local-publish": "yalc publish --no-sig --force",
19+
"local-publish": "yalc publish --yarn --no-sig --force",
2020
"clean-yalc": "yalc installations clean @invisionapp/dsm-storybook",
2121
"postupdate": "chmod +x ./node_modules/.bin/dsm-storybook"
2222
},
@@ -46,8 +46,8 @@
4646
"pkg-dir": "4.1.0",
4747
"prettier": "1.19.1",
4848
"prism-react-renderer": "1.0.2",
49-
"prismjs": "1.15.0",
50-
"react-docgen": "5.3.0",
49+
"prismjs": "1.21.0",
50+
"react-docgen": "5.3.1",
5151
"read-pkg": "4.0.1",
5252
"read-pkg-up": "4.0.0",
5353
"request-promise": "4.2.4",
@@ -93,6 +93,6 @@
9393
"webpack-cli": "3.1.2",
9494
"webpack-node-externals": "1.7.2",
9595
"webpack-shell-plugin": "0.5.0",
96-
"yalc": "1.0.0-pre.27"
96+
"yalc": "1.0.0-pre.45"
9797
}
98-
}
98+
}

‎src/addons/constants.js

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
const DSM_ADDON_NAME = 'invision-dsm';
22
const DSM_INFO_OBJECT_KEY = 'in-dsm';
33
const STORY_SELECTED_EVENT = `${DSM_ADDON_NAME}/on_story_selected`;
4+
const STORY_CONTENT_LOADED = `${DSM_ADDON_NAME}/on_story_content_loaded`;
45
const HTML_SAMPLE_CODE_CHANGED_EVENT = `${DSM_ADDON_NAME}/on_html_sample_code_changed`;
56
const INIT_DSM_REGISTERED_EVENT = `${DSM_ADDON_NAME}/on_init_dsm_registered`;
67
const INIT_DSM_EVENT = `${DSM_ADDON_NAME}/on_init_dsm`;
@@ -19,6 +20,7 @@ module.exports = {
1920
DSM_INFO_OBJECT_KEY,
2021
DSM_STORYBOOK_START_EVENT,
2122
STORY_SELECTED_EVENT,
23+
STORY_CONTENT_LOADED,
2224
HTML_SAMPLE_CODE_CHANGED_EVENT,
2325
INIT_DSM_REGISTERED_EVENT,
2426
INIT_DSM_EVENT,

‎src/addons/register.js

+9-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ import {
1616
KNOBS_SET_EVENT as DSM_KNOBS_SET_EVENT,
1717
INIT_DSM_REGISTERED_EVENT,
1818
INIT_DSM_EVENT,
19-
DSM_STORYBOOK_START_EVENT
19+
DSM_STORYBOOK_START_EVENT,
20+
STORY_CONTENT_LOADED,
21+
FRAMEWORKS
2022
} from './constants';
2123
import { optionsSettings, isLocalPreview } from './dsm-options';
2224

@@ -55,6 +57,12 @@ export function registerDsm(envVariable) {
5557
});
5658

5759
channel.on(HTML_SAMPLE_CODE_CHANGED_EVENT, (data) => {
60+
if (getByEnvKey(environmentKeys.storybookFramework) === FRAMEWORKS.html) {
61+
notifyDsm(data);
62+
}
63+
});
64+
65+
channel.on(STORY_CONTENT_LOADED, (data) => {
5866
notifyDsm(data);
5967
});
6068

‎src/addons/services/client-logger.js

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { isString } from 'lodash';
44
const getErrorMessage = (message) => (message && isString(message) ? message : message.stack);
55

66
export default {
7+
info: (message) => console.log(message),
78
warning: (message) => console.log(message),
89
error: (message, exception) => console.log(`${getErrorMessage(message)} ${exception ? `\n${exception.stack}` : ''}`)
910
};

‎src/addons/with-dsm.js

+75-36
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ import addons, { makeDecorator } from '@storybook/addons';
22
import logger from './services/client-logger';
33
import userMessages from '../user-messages';
44

5-
import { DSM_INFO_OBJECT_KEY, STORY_SELECTED_EVENT, HTML_SAMPLE_CODE_CHANGED_EVENT, FRAMEWORKS } from './constants';
6-
import { environmentKeys, getByEnvKey } from '../services/configuration';
5+
import { STORY_SELECTED_EVENT, HTML_SAMPLE_CODE_CHANGED_EVENT, DSM_INFO_OBJECT_KEY, STORY_CONTENT_LOADED } from './constants';
76
import { getStorySampleCode } from './html-sample-code';
87

98
function isDifferentStories(current, next) {
@@ -12,47 +11,87 @@ function isDifferentStories(current, next) {
1211

1312
let currentStory = null;
1413

15-
export default makeDecorator({
16-
name: 'withDsm',
17-
parameterName: DSM_INFO_OBJECT_KEY,
18-
skipIfNoParametersOrOptions: true,
19-
wrapper: (getStory, context) => {
20-
const story = getStory(context);
21-
const framework = getByEnvKey(environmentKeys.storybookFramework);
22-
23-
// In HTML framework the sample code is the story itself on runtime, return the story only if it's a string and not a JS document
24-
if (framework === FRAMEWORKS.html) {
25-
const { containerClass } = context.parameters['in-dsm'];
26-
let sampleCode = '';
27-
28-
try {
29-
sampleCode = getStorySampleCode(story, containerClass);
30-
} catch (e) {
31-
sampleCode = userMessages.failedToExtractSampleCode();
32-
logger.error(e);
33-
}
14+
const emitHtmlSampleCode = (story, context) => {
15+
// In HTML framework the sample code is the story itself on runtime, the story only
16+
// if it's a string and not a JS document
17+
const { containerClass } = context.parameters['in-dsm'];
18+
let sampleCode = '';
3419

35-
addons.getChannel().emit(HTML_SAMPLE_CODE_CHANGED_EVENT, {
36-
eventName: HTML_SAMPLE_CODE_CHANGED_EVENT,
37-
sampleCode: sampleCode
20+
try {
21+
sampleCode = getStorySampleCode(story, containerClass);
22+
} catch (e) {
23+
sampleCode = userMessages.failedToExtractSampleCode();
24+
logger.error(e);
25+
}
26+
27+
addons.getChannel().emit(HTML_SAMPLE_CODE_CHANGED_EVENT, {
28+
eventName: HTML_SAMPLE_CODE_CHANGED_EVENT,
29+
sampleCode: sampleCode
30+
});
31+
};
32+
33+
const emitStoryData = (story, context) => {
34+
if (!currentStory || isDifferentStories(currentStory, context)) {
35+
// story.type won't exist for Vue, we ignore it for now (__docgenInfo is only relevant for react)
36+
const docgenInfo = story.type && story.type.__docgenInfo;
37+
38+
const storyData = {
39+
eventName: STORY_SELECTED_EVENT,
40+
docgenInfo: docgenInfo
41+
};
42+
43+
addons.getChannel().emit(STORY_SELECTED_EVENT, storyData);
44+
currentStory = context;
45+
}
46+
};
47+
48+
const emitStoryLayoutDimensions = () => {
49+
const CONTENT_REFRESH_TIME = 200; // ms
50+
const RETRY_MAX_ATTEMPTS = 20;
51+
let retryCount = 0;
52+
53+
const messageContentHeightInterval = setInterval(() => {
54+
const container = document.getElementById('docs-root');
55+
56+
// If the container does not exist we want to continue retrying in the "else" block.
57+
// Additionally if the container DOES exist but is hidden, the offsetHeight will be 0.
58+
// In this case we also continue retrying.
59+
if (container && container.offsetHeight) {
60+
addons.getChannel().emit(STORY_CONTENT_LOADED, {
61+
eventName: STORY_CONTENT_LOADED,
62+
height: container.offsetHeight
3863
});
39-
}
4064

41-
// Will emit the story selected event only one time per story selected instead of each time the story is updated
42-
// (for example story is updated each time knobs are changed)
43-
if (!currentStory || isDifferentStories(currentStory, context)) {
44-
// story.type won't exist for Vue, we ignore it for now (__docgenInfo is only relevant for react)
45-
const docgenInfo = story.type && story.type.__docgenInfo;
65+
clearInterval(messageContentHeightInterval);
66+
} else {
67+
addons.getChannel().emit(STORY_CONTENT_LOADED, {
68+
eventName: STORY_CONTENT_LOADED
69+
// We do not send a height so that web will use a default height. dsm-storybook
70+
// doesn't really care what that height is.
71+
});
4672

47-
const storyData = {
48-
eventName: STORY_SELECTED_EVENT,
49-
docgenInfo: docgenInfo
50-
};
73+
// We may not ever find the container, or the container may not have an `offsetHeight`.
74+
// This may occur if the user is viewing "Canvas" instead of "Docs." In this situation
75+
// we want to just stop the interval to prevent a "memory leak."
76+
retryCount += 1;
5177

52-
addons.getChannel().emit(STORY_SELECTED_EVENT, storyData);
53-
currentStory = context;
78+
if (retryCount > RETRY_MAX_ATTEMPTS) {
79+
clearInterval(messageContentHeightInterval);
80+
}
5481
}
82+
}, CONTENT_REFRESH_TIME);
83+
};
5584

85+
export default makeDecorator({
86+
name: 'withDsm',
87+
parameterName: DSM_INFO_OBJECT_KEY,
88+
skipIfNoParametersOrOptions: true,
89+
wrapper: (getStory, context) => {
90+
const story = getStory(context);
91+
emitHtmlSampleCode(story, context);
92+
emitStoryData(story, context);
93+
emitStoryLayoutDimensions();
94+
logger.info(userMessages.decoratorStarted());
5695
return story;
5796
}
5897
});

‎src/cli/build-configuration.js

+24-5
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ const configurationKeys = {
1818
dsmHost: 'dsmHost',
1919
authToken: 'authToken',
2020
storyPath: 'storyPath',
21-
outputDir: 'outputDir'
21+
outputDir: 'outputDir',
22+
hideDsmGeneratedTable: 'hideDsmGeneratedTable',
23+
defaultDocsTab: 'defaultDocsTab'
2224
};
2325

2426
let loadedConfiguration = null;
@@ -56,7 +58,8 @@ function create(commandlineOptions, customArgs) {
5658
prettierConfiguration,
5759
storybookConfigPath,
5860
storybookConfigFolderPath,
59-
isUsingDeclarativeConfiguration: isUsingDeclarativeConfiguration(storybookConfigPath)
61+
isUsingDeclarativeConfiguration: isUsingDeclarativeConfiguration(storybookConfigPath),
62+
isDocsPage: customArgs.includes('--docs')
6063
};
6164

6265
const environmentConfiguration = loadEnvironmentVariables();
@@ -81,8 +84,22 @@ function loadEnvironmentVariables() {
8184
}
8285

8386
function mergeConfigurationValues(appConfiguration, cmdConfiguration, fileConfiguration = {}, environmentConfiguration) {
84-
// command line args overrides config file and environment variables
85-
return Object.assign(appConfiguration, fileConfiguration, environmentConfiguration, cmdConfiguration);
87+
// The below options are optional and we automatically set them to false. If the
88+
// user specifies them via the command-line or dsmrc the user options will be
89+
// overwrite the defaults.
90+
const optionalCommandLineOptions = {
91+
[configurationKeys.defaultDocsTab]: false,
92+
[configurationKeys.hideDsmGeneratedTable]: false
93+
};
94+
95+
// command line args overrides config file, default DSM options, and environment variables
96+
return Object.assign(
97+
appConfiguration,
98+
optionalCommandLineOptions,
99+
fileConfiguration,
100+
environmentConfiguration,
101+
cmdConfiguration
102+
);
86103
}
87104

88105
function get() {
@@ -99,8 +116,10 @@ function validateConfiguration() {
99116
process.exit(1);
100117
}
101118
let hasMissingConfiguration = false;
119+
102120
keys(configurationKeys).map((key) => {
103-
if (!loadedConfiguration[key]) {
121+
const configurationKeyExists = key in loadedConfiguration;
122+
if (!configurationKeyExists) {
104123
logger.error(userMessages.missingConfigurationKey(key));
105124
hasMissingConfiguration = true;
106125
}

‎src/cli/runner.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,11 @@ async function publish(options, customArgs) {
5757
storybookDependencies: options.storybookDependencies,
5858
storiesMetadata: storiesMetadata,
5959
buildId: buildId,
60-
prettierConfiguration: options.prettierConfiguration
60+
prettierConfiguration: options.prettierConfiguration,
61+
62+
isDocsPage: options.isDocsPage,
63+
hideDsmGeneratedTable: options.hideDsmGeneratedTable,
64+
defaultDocsTab: options.defaultDocsTab
6165
};
6266

6367
logger.progress(userMessages.updatingDsm());
+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
const { getByVersion, resolvers } = require('../versions');
22

3-
module.exports = function(kind, name, storybookVersion) {
3+
module.exports = function(kind, name, storybookVersion, { defaultDocsTab = false }) {
44
const { getStoryUrlParams } = getByVersion(resolvers.getStoryUrlParams, storybookVersion);
5-
return getStoryUrlParams(kind, name);
5+
return getStoryUrlParams(kind, name, defaultDocsTab);
66
};

‎src/user-messages.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,8 @@ const messages = {
102102
`More than 1 Storybook UI framework dependencies detected. You can set the UI framework you want to use explicitly inside the '.dsmrc' file or make sure only 1 '@storybook/<framework>' is inside the 'package.json' file.`,
103103
explicitFrameworkNotDetectedInInstalledPackages: (framework) =>
104104
`Failed to detect the framework '${framework}' that was provided in '.dsmrc' file. Please ensure you installed Storybook for your selected UI library correctly. See https://storybook.js.org/basics/slow-start-guide/ for details.`,
105-
inDsmInitLocationError: (inDsmLevel) => `${inDsmLevel} 'in-dsm' must be initialized inside the 'parameters' property`
105+
inDsmInitLocationError: (inDsmLevel) => `${inDsmLevel} 'in-dsm' must be initialized inside the 'parameters' property`,
106+
decoratorStarted: () => 'The DSM Storybook integration is now running.'
106107
};
107108

108109
module.exports = messages;

‎src/versions/v5.1.1/get-story-url-params.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
const { sanitize } = require('./url-utils');
22

3-
const getStoryUrlParams = (rawKind, rawName) => {
3+
const getStoryUrlParams = (rawKind, rawName, defaultDocsTab) => {
44
const { kind, name } = sanitizeParams(rawKind, rawName);
5+
const initialTab = defaultDocsTab ? 'docs' : 'story';
56

6-
return { path: `/story/${kind}--${name}` };
7+
return { path: `/${initialTab}/${kind}--${name}` };
78
};
89

910
const sanitizeParams = (rawKind, rawName) => {

0 commit comments

Comments
 (0)