@storybook/csf 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
package/.babelrc.js ADDED
@@ -0,0 +1,6 @@
1
+ module.exports = {
2
+ presets: [
3
+ '@babel/preset-env',
4
+ '@babel/preset-typescript',
5
+ ],
6
+ };
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
+ ```
@@ -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
+ }