@storybook/csf 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.babelrc.js +6 -0
- package/README.md +33 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +105 -0
- package/dist/index.test.d.ts +1 -0
- package/dist/index.test.js +137 -0
- package/package.json +65 -0
package/.babelrc.js
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Storybook Component Story Format (CSF)
|
|
2
|
+
|
|
3
|
+
A minimal set of utility functions for dealing with Storybook [Component Story Format (CSF)](https://storybook.js.org/docs/formats/component-story-format/).
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
yarn add @storybook/csf
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## API
|
|
12
|
+
|
|
13
|
+
See package source for function definitions and types:
|
|
14
|
+
|
|
15
|
+
- `isExportStory(key, { includeStories, excludeStories })` - Does a named export match CSF inclusion/exclusion options?
|
|
16
|
+
|
|
17
|
+
- `parseKind(kind, { rootSeparator, groupSeparator })` - Parse out the component/kind name from a path, using the given separator config.
|
|
18
|
+
|
|
19
|
+
- `sanitize(string)` - Remove punctuation and illegal characters from a story ID.
|
|
20
|
+
|
|
21
|
+
- `toId(kind, name)` - Generate a storybook ID from a component/kind and story name.
|
|
22
|
+
|
|
23
|
+
## Contributing
|
|
24
|
+
|
|
25
|
+
If you have any suggestions, please open an issue or a PR.
|
|
26
|
+
|
|
27
|
+
All contributions are welcome!
|
|
28
|
+
|
|
29
|
+
### run tests:
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
yarn test
|
|
33
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare const sanitize: (string: string) => string;
|
|
2
|
+
export declare const toId: (kind: string, name: string) => string;
|
|
3
|
+
export declare const storyNameFromExport: (key: string) => string;
|
|
4
|
+
declare type StoryDescriptor = string[] | RegExp;
|
|
5
|
+
export interface IncludeExcludeOptions {
|
|
6
|
+
includeStories?: StoryDescriptor;
|
|
7
|
+
excludeStories?: StoryDescriptor;
|
|
8
|
+
}
|
|
9
|
+
export declare function isExportStory(key: string, { includeStories, excludeStories }: IncludeExcludeOptions): boolean | null;
|
|
10
|
+
export interface SeparatorOptions {
|
|
11
|
+
rootSeparator: string | RegExp;
|
|
12
|
+
groupSeparator: string | RegExp;
|
|
13
|
+
}
|
|
14
|
+
export declare const parseKind: (kind: string, { rootSeparator, groupSeparator }: SeparatorOptions) => {
|
|
15
|
+
root: string | null;
|
|
16
|
+
groups: string[];
|
|
17
|
+
};
|
|
18
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.isExportStory = isExportStory;
|
|
7
|
+
exports.parseKind = exports.storyNameFromExport = exports.toId = exports.sanitize = void 0;
|
|
8
|
+
|
|
9
|
+
var _startCase = _interopRequireDefault(require("lodash/startCase"));
|
|
10
|
+
|
|
11
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
|
|
12
|
+
|
|
13
|
+
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); }
|
|
14
|
+
|
|
15
|
+
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); }
|
|
16
|
+
|
|
17
|
+
function _iterableToArrayLimit(arr, i) { if (!(Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === "[object Arguments]")) { return; } var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
|
|
18
|
+
|
|
19
|
+
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Remove punctuation and illegal characters from a story ID.
|
|
23
|
+
*
|
|
24
|
+
* See https://gist.github.com/davidjrice/9d2af51100e41c6c4b4a
|
|
25
|
+
*/
|
|
26
|
+
var sanitize = function sanitize(string) {
|
|
27
|
+
return string.toLowerCase() // eslint-disable-next-line no-useless-escape
|
|
28
|
+
.replace(/[ ’–—―′¿'`~!@#$%^&*()_|+\-=?;:'",.<>\{\}\[\]\\\/]/gi, '-').replace(/-+/g, '-').replace(/^-+/, '').replace(/-+$/, '');
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
exports.sanitize = sanitize;
|
|
32
|
+
|
|
33
|
+
var sanitizeSafe = function sanitizeSafe(string, part) {
|
|
34
|
+
var sanitized = sanitize(string);
|
|
35
|
+
|
|
36
|
+
if (sanitized === '') {
|
|
37
|
+
throw new Error("Invalid ".concat(part, " '").concat(string, "', must include alphanumeric characters"));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return sanitized;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Generate a storybook ID from a component/kind and story name.
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
var toId = function toId(kind, name) {
|
|
48
|
+
return "".concat(sanitizeSafe(kind, 'kind'), "--").concat(sanitizeSafe(name, 'name'));
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Transform a CSF named export into a readable story name
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
exports.toId = toId;
|
|
56
|
+
|
|
57
|
+
var storyNameFromExport = function storyNameFromExport(key) {
|
|
58
|
+
return (0, _startCase["default"])(key);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
exports.storyNameFromExport = storyNameFromExport;
|
|
62
|
+
|
|
63
|
+
function matches(storyKey, arrayOrRegex) {
|
|
64
|
+
if (Array.isArray(arrayOrRegex)) {
|
|
65
|
+
return arrayOrRegex.includes(storyKey);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return storyKey.match(arrayOrRegex);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Does a named export match CSF inclusion/exclusion options?
|
|
72
|
+
*/
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
function isExportStory(key, _ref) {
|
|
76
|
+
var includeStories = _ref.includeStories,
|
|
77
|
+
excludeStories = _ref.excludeStories;
|
|
78
|
+
return (// https://babeljs.io/docs/en/babel-plugin-transform-modules-commonjs
|
|
79
|
+
key !== '__esModule' && (!includeStories || matches(key, includeStories)) && (!excludeStories || !matches(key, excludeStories))
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Parse out the component/kind name from a path, using the given separator config.
|
|
85
|
+
*/
|
|
86
|
+
var parseKind = function parseKind(kind, _ref2) {
|
|
87
|
+
var rootSeparator = _ref2.rootSeparator,
|
|
88
|
+
groupSeparator = _ref2.groupSeparator;
|
|
89
|
+
|
|
90
|
+
var _kind$split = kind.split(rootSeparator, 2),
|
|
91
|
+
_kind$split2 = _slicedToArray(_kind$split, 2),
|
|
92
|
+
root = _kind$split2[0],
|
|
93
|
+
remainder = _kind$split2[1];
|
|
94
|
+
|
|
95
|
+
var groups = (remainder || kind).split(groupSeparator).filter(function (i) {
|
|
96
|
+
return !!i;
|
|
97
|
+
}); // when there's no remainder, it means the root wasn't found/split
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
root: remainder ? root : null,
|
|
101
|
+
groups: groups
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
exports.parseKind = parseKind;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _ = require(".");
|
|
4
|
+
|
|
5
|
+
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); }
|
|
6
|
+
|
|
7
|
+
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); }
|
|
8
|
+
|
|
9
|
+
function _iterableToArrayLimit(arr, i) { if (!(Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === "[object Arguments]")) { return; } var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
|
|
10
|
+
|
|
11
|
+
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
12
|
+
|
|
13
|
+
describe('toId', function () {
|
|
14
|
+
[// name, kind, story, output
|
|
15
|
+
['handles simple cases', 'kind', 'story', 'kind--story'], ['handles basic substitution', 'a b$c?d😀e', '1-2:3', 'a-b-c-d😀e--1-2-3'], ['handles runs of non-url chars', 'a?&*b', 'story', 'a-b--story'], ['removes non-url chars from start and end', '?ab-', 'story', 'ab--story'], ['downcases', 'KIND', 'STORY', 'kind--story'], ['non-latin', 'Кнопки', 'нормальный', 'кнопки--нормальный'], ['korean', 'kind', '바보 (babo)', 'kind--바보-babo'], ['all punctuation', 'kind', 'unicorns,’–—―′¿`"<>()!.!!!{}[]%^&$*#&', 'kind--unicorns']].forEach(function (_ref) {
|
|
16
|
+
var _ref2 = _slicedToArray(_ref, 4),
|
|
17
|
+
name = _ref2[0],
|
|
18
|
+
kind = _ref2[1],
|
|
19
|
+
story = _ref2[2],
|
|
20
|
+
output = _ref2[3];
|
|
21
|
+
|
|
22
|
+
it(name, function () {
|
|
23
|
+
expect((0, _.toId)(kind, story)).toBe(output);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
it('does not allow kind with *no* url chars', function () {
|
|
27
|
+
expect(function () {
|
|
28
|
+
return (0, _.toId)('?', 'asdf');
|
|
29
|
+
}).toThrow("Invalid kind '?', must include alphanumeric characters");
|
|
30
|
+
});
|
|
31
|
+
it('does not allow empty kind', function () {
|
|
32
|
+
expect(function () {
|
|
33
|
+
return (0, _.toId)('', 'asdf');
|
|
34
|
+
}).toThrow("Invalid kind '', must include alphanumeric characters");
|
|
35
|
+
});
|
|
36
|
+
it('does not allow story with *no* url chars', function () {
|
|
37
|
+
expect(function () {
|
|
38
|
+
return (0, _.toId)('kind', '?');
|
|
39
|
+
}).toThrow("Invalid name '?', must include alphanumeric characters");
|
|
40
|
+
});
|
|
41
|
+
it('does not allow empty story', function () {
|
|
42
|
+
expect(function () {
|
|
43
|
+
return (0, _.toId)('kind', '');
|
|
44
|
+
}).toThrow("Invalid name '', must include alphanumeric characters");
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
describe('storyNameFromExport', function () {
|
|
48
|
+
it('should format CSF exports with sensible defaults', function () {
|
|
49
|
+
var testCases = {
|
|
50
|
+
name: 'Name',
|
|
51
|
+
someName: 'Some Name',
|
|
52
|
+
someNAME: 'Some NAME',
|
|
53
|
+
some_custom_NAME: 'Some Custom NAME',
|
|
54
|
+
someName1234: 'Some Name 1234',
|
|
55
|
+
someName1_2_3_4: 'Some Name 1 2 3 4'
|
|
56
|
+
};
|
|
57
|
+
Object.entries(testCases).forEach(function (_ref3) {
|
|
58
|
+
var _ref4 = _slicedToArray(_ref3, 2),
|
|
59
|
+
key = _ref4[0],
|
|
60
|
+
val = _ref4[1];
|
|
61
|
+
|
|
62
|
+
return expect((0, _.storyNameFromExport)(key)).toBe(val);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
describe('isExportStory', function () {
|
|
67
|
+
it('should exclude __esModule', function () {
|
|
68
|
+
expect((0, _.isExportStory)('__esModule', {})).toBeFalsy();
|
|
69
|
+
});
|
|
70
|
+
it('should include all stories when there are no filters', function () {
|
|
71
|
+
expect((0, _.isExportStory)('a', {})).toBeTruthy();
|
|
72
|
+
});
|
|
73
|
+
it('should filter stories by arrays', function () {
|
|
74
|
+
expect((0, _.isExportStory)('a', {
|
|
75
|
+
includeStories: ['a']
|
|
76
|
+
})).toBeTruthy();
|
|
77
|
+
expect((0, _.isExportStory)('a', {
|
|
78
|
+
includeStories: []
|
|
79
|
+
})).toBeFalsy();
|
|
80
|
+
expect((0, _.isExportStory)('a', {
|
|
81
|
+
includeStories: ['b']
|
|
82
|
+
})).toBeFalsy();
|
|
83
|
+
expect((0, _.isExportStory)('a', {
|
|
84
|
+
excludeStories: ['a']
|
|
85
|
+
})).toBeFalsy();
|
|
86
|
+
expect((0, _.isExportStory)('a', {
|
|
87
|
+
excludeStories: []
|
|
88
|
+
})).toBeTruthy();
|
|
89
|
+
expect((0, _.isExportStory)('a', {
|
|
90
|
+
excludeStories: ['b']
|
|
91
|
+
})).toBeTruthy();
|
|
92
|
+
expect((0, _.isExportStory)('a', {
|
|
93
|
+
includeStories: ['a'],
|
|
94
|
+
excludeStories: ['a']
|
|
95
|
+
})).toBeFalsy();
|
|
96
|
+
expect((0, _.isExportStory)('a', {
|
|
97
|
+
includeStories: [],
|
|
98
|
+
excludeStories: []
|
|
99
|
+
})).toBeFalsy();
|
|
100
|
+
expect((0, _.isExportStory)('a', {
|
|
101
|
+
includeStories: ['a'],
|
|
102
|
+
excludeStories: ['b']
|
|
103
|
+
})).toBeTruthy();
|
|
104
|
+
});
|
|
105
|
+
it('should filter stories by regex', function () {
|
|
106
|
+
expect((0, _.isExportStory)('a', {
|
|
107
|
+
includeStories: /a/
|
|
108
|
+
})).toBeTruthy();
|
|
109
|
+
expect((0, _.isExportStory)('a', {
|
|
110
|
+
includeStories: /.*/
|
|
111
|
+
})).toBeTruthy();
|
|
112
|
+
expect((0, _.isExportStory)('a', {
|
|
113
|
+
includeStories: /b/
|
|
114
|
+
})).toBeFalsy();
|
|
115
|
+
expect((0, _.isExportStory)('a', {
|
|
116
|
+
excludeStories: /a/
|
|
117
|
+
})).toBeFalsy();
|
|
118
|
+
expect((0, _.isExportStory)('a', {
|
|
119
|
+
excludeStories: /.*/
|
|
120
|
+
})).toBeFalsy();
|
|
121
|
+
expect((0, _.isExportStory)('a', {
|
|
122
|
+
excludeStories: /b/
|
|
123
|
+
})).toBeTruthy();
|
|
124
|
+
expect((0, _.isExportStory)('a', {
|
|
125
|
+
includeStories: /a/,
|
|
126
|
+
excludeStories: ['a']
|
|
127
|
+
})).toBeFalsy();
|
|
128
|
+
expect((0, _.isExportStory)('a', {
|
|
129
|
+
includeStories: /.*/,
|
|
130
|
+
excludeStories: /.*/
|
|
131
|
+
})).toBeFalsy();
|
|
132
|
+
expect((0, _.isExportStory)('a', {
|
|
133
|
+
includeStories: /a/,
|
|
134
|
+
excludeStories: /b/
|
|
135
|
+
})).toBeTruthy();
|
|
136
|
+
});
|
|
137
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@storybook/csf",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Storybook Component Story Format (CSF) utilities",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"storybook",
|
|
7
|
+
"component story format",
|
|
8
|
+
"csf",
|
|
9
|
+
"stories"
|
|
10
|
+
],
|
|
11
|
+
"homepage": "https://github.com/storybookjs/csf",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/storybookjs/csf/issues"
|
|
14
|
+
},
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "https://github.com/storybookjs/csf.git"
|
|
18
|
+
},
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"files": [
|
|
21
|
+
"dist/**/*",
|
|
22
|
+
"README.md",
|
|
23
|
+
"*.js",
|
|
24
|
+
"*.d.ts"
|
|
25
|
+
],
|
|
26
|
+
"main": "dist/index.js",
|
|
27
|
+
"types": "dist/index.d.ts",
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "babel src --out-dir dist --extensions \".ts\" && tsc --emitDeclarationOnly",
|
|
30
|
+
"lint": "eslint src --ext .js,.ts",
|
|
31
|
+
"prepublish": "yarn build",
|
|
32
|
+
"test": "jest"
|
|
33
|
+
},
|
|
34
|
+
"eslintConfig": {
|
|
35
|
+
"extends": [
|
|
36
|
+
"@storybook/eslint-config-storybook"
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
"prettier": "@storybook/linter-config/prettier.config",
|
|
40
|
+
"jest": {
|
|
41
|
+
"testEnvironment": "node"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"lodash": "^4.17.15"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@babel/cli": "^7.7.4",
|
|
48
|
+
"@babel/core": "^7.7.4",
|
|
49
|
+
"@babel/preset-env": "^7.7.4",
|
|
50
|
+
"@babel/preset-typescript": "^7.7.4",
|
|
51
|
+
"@storybook/eslint-config-storybook": "^2.1.0",
|
|
52
|
+
"@types/jest": "^24.0.23",
|
|
53
|
+
"@types/lodash": "^4.14.149",
|
|
54
|
+
"babel-core": "7.0.0-bridge.0",
|
|
55
|
+
"babel-jest": "^24.9.0",
|
|
56
|
+
"common-tags": "^1.8.0",
|
|
57
|
+
"eslint": "^6.7.1",
|
|
58
|
+
"jest": "^24.9.0",
|
|
59
|
+
"prettier": "^1.19.1",
|
|
60
|
+
"typescript": "^3.7.2"
|
|
61
|
+
},
|
|
62
|
+
"publishConfig": {
|
|
63
|
+
"access": "public"
|
|
64
|
+
}
|
|
65
|
+
}
|