@eightshift/frontend-libs-tailwind 2.0.0 → 2.0.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eightshift/frontend-libs-tailwind",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
4
  "description": "A framework for creating modern Gutenberg themes with styling provided by Tailwind CSS.",
5
5
  "author": {
6
6
  "name": "Eightshift team",
@@ -27,28 +27,24 @@
27
27
  },
28
28
  "scripts": {
29
29
  "lintStyle": "stylelint **/*.css",
30
- "lintJs": "npx eslint",
30
+ "lintJs": "bunx eslint",
31
31
  "lint": "npm run lintJs && npm run lintStyle",
32
32
  "prepare": "husky"
33
33
  },
34
34
  "homepage": "https://github.com/infinum/eightshift-frontend-libs-tailwind#readme",
35
35
  "license": "MIT",
36
36
  "dependencies": {
37
- "@eightshift/ui-components": "^3.0.0",
37
+ "@eightshift/ui-components": "^3.0.1",
38
38
  "@stylistic/eslint-plugin-js": "^4.2.0",
39
39
  "@stylistic/stylelint-plugin": "^3.1.2",
40
- "@swc/core": "^1.11.11",
41
- "@wordpress/api-fetch": "^7.20.0",
42
- "@wordpress/block-editor": "^14.15.0",
43
- "@wordpress/dependency-extraction-webpack-plugin": "^6.20.0",
44
- "@wordpress/dom-ready": "^4.20.0",
45
- "@wordpress/server-side-render": "^5.20.0",
40
+ "@swc/core": "^1.11.22",
41
+ "@wordpress/dependency-extraction-webpack-plugin": "^6.22.0",
46
42
  "browserslist": "^4.24.4",
47
43
  "css-loader": "^7.1.2",
48
44
  "css-minimizer-webpack-plugin": "^7.0.2",
49
- "eslint": "^9.22.0",
50
- "eslint-config-prettier": "^10.1.1",
51
- "eslint-plugin-prettier": "^5.2.3",
45
+ "eslint": "^9.25.1",
46
+ "eslint-config-prettier": "^10.1.2",
47
+ "eslint-plugin-prettier": "^5.2.6",
52
48
  "globals": "^16.0.0",
53
49
  "husky": "^9.1.7",
54
50
  "lightningcss": "^1.29.3",
@@ -57,18 +53,19 @@
57
53
  "postcss-loader": "^8.1.1",
58
54
  "prettier": "^3.5.3",
59
55
  "prettier-plugin-tailwindcss": "^0.6.11",
60
- "sonner": "^2.0.1",
61
- "stylelint": "^16.16.0",
62
- "stylelint-config-standard": "^37.0.0",
56
+ "sonner": "^2.0.3",
57
+ "stylelint": "^16.19.0",
58
+ "stylelint-config-standard": "^38.0.0",
63
59
  "swc-loader": "^0.2.6",
64
60
  "terser-webpack-plugin": "^5.3.14",
65
- "webpack": "^5.98.0",
61
+ "webpack": "^5.99.6",
66
62
  "webpack-cli": "^6.0.1",
67
63
  "webpack-manifest-plugin": "^5.0.1",
68
64
  "webpack-merge": "^6.0.1"
69
65
  },
70
66
  "devDependencies": {
71
- "lint-staged": "^15.5.0"
67
+ "lint-staged": "^15.5.1",
68
+ "@wordpress/api-fetch": "^7.22.0"
72
69
  },
73
70
  "sideEffects": false,
74
71
  "lint-staged": {
@@ -29,6 +29,11 @@
29
29
  "type": "boolean",
30
30
  "description": "Set to 'false' to disable wrapper.",
31
31
  "default": true
32
+ },
33
+ "useLegacyComponents": {
34
+ "type": "boolean",
35
+ "description": "Set to 'true' to use legacy components.",
36
+ "default": false
32
37
  }
33
38
  }
34
39
  },
@@ -64,7 +64,7 @@ export const PickerPlaceholder = (props) => {
64
64
  <RichLabel
65
65
  icon={icon}
66
66
  label={title}
67
- className='col-span-2 mb-2 font-medium !text-gray-400 select-none'
67
+ className='col-span-2 mb-2 select-none font-medium !text-gray-400'
68
68
  />
69
69
 
70
70
  <span className='es:col-span-2 es:select-none es:justify-self-center'>{presetsHeading}</span>
@@ -16,9 +16,9 @@ import { upperFirst } from '@eightshift/ui-components/utilities';
16
16
  */
17
17
  export const getPaletteColors = () =>
18
18
  useSelect((select) => {
19
- const colors = select(STORE_NAME).getSettings().globalVariables.colors;
19
+ const colors = select(STORE_NAME).getSettings()?.globalVariables?.colors;
20
20
 
21
- return colors.reduce(
21
+ return colors?.reduce(
22
22
  (obj, item) => ({
23
23
  ...obj,
24
24
  [item.slug]: item,
@@ -116,7 +116,7 @@ export const getOptionColors = (colors) => {
116
116
  return Object.values(coreColors);
117
117
  }
118
118
 
119
- return colors.map((colorName) => coreColors[colorName]);
119
+ return colors.map((colorName) => coreColors?.[colorName]);
120
120
  };
121
121
 
122
122
  /**
@@ -31,7 +31,7 @@ import { camelCase, kebabCase, lowerFirst, upperFirst } from '@eightshift/ui-com
31
31
  * Usage:
32
32
  * ```js
33
33
  * registerBlocks(
34
- * globalSettings,
34
+ * globalManifest,
35
35
  * Wrapper,
36
36
  * WrapperManifest,
37
37
  * require.context('./../../components', true, /manifest.json$/),
@@ -49,9 +49,9 @@ export const registerBlocks = (
49
49
  globalManifest = {},
50
50
  wrapperComponent = null,
51
51
  wrapperManifest = {},
52
- componentsManifestPath,
53
- blocksManifestPath,
54
- blocksEditComponentPath,
52
+ componentsManifestPath = null,
53
+ blocksManifestPath = null,
54
+ blocksEditComponentPath = null,
55
55
  hooksComponentPath = null,
56
56
  transformsComponentPath = null,
57
57
  deprecationsComponentPath = null,
@@ -226,9 +226,6 @@ export const registerVariations = (
226
226
  ) => {
227
227
  const variationsManifests = variationsManifestPath.keys().map(variationsManifestPath);
228
228
 
229
- // Set all store values.
230
- dispatch(STORE_NAME).setVariations(variationsManifests);
231
-
232
229
  // Iterate blocks to register.
233
230
  variationsManifests.map((variationManifest) => {
234
231
  const { active = true } = variationManifest;
@@ -10,6 +10,7 @@ const DEFAULT_STATE = {
10
10
  components: {},
11
11
  config: {
12
12
  useWrapper: true,
13
+ useLegacyComponents: true,
13
14
  },
14
15
  wrapper: {},
15
16
  variations: {},
@@ -44,6 +45,9 @@ const selectors = {
44
45
  getConfigUseWrapper(state) {
45
46
  return state.config.useWrapper;
46
47
  },
48
+ getConfigUseLegacyComponents(state) {
49
+ return state.config.useLegacyComponents;
50
+ },
47
51
  getWrapper(state) {
48
52
  return state.wrapper;
49
53
  },
@@ -90,6 +94,12 @@ const actions = {
90
94
  config,
91
95
  };
92
96
  },
97
+ setConfigUseLegacyComponents(config) {
98
+ return {
99
+ type: 'SET_CONFIG_USE_LEGACY_COMPONENTS',
100
+ config,
101
+ };
102
+ },
93
103
  setWrapper(wrapper) {
94
104
  return {
95
105
  type: 'SET_WRAPPER',
@@ -140,6 +150,15 @@ const reducer = (state = DEFAULT_STATE, action) => {
140
150
  },
141
151
  };
142
152
  }
153
+ case 'SET_CONFIG_USE_LEGACY_COMPONENTS': {
154
+ return {
155
+ ...state,
156
+ config: {
157
+ ...state.config,
158
+ useLegacyComponents: action.config,
159
+ },
160
+ };
161
+ }
143
162
  case 'SET_WRAPPER': {
144
163
  return {
145
164
  ...state,
@@ -2,7 +2,6 @@
2
2
  * Helper to set and unset cookies.
3
3
  */
4
4
  export const cookies = {
5
-
6
5
  /**
7
6
  * Set a cookie value
8
7
  *
@@ -10,32 +9,41 @@ export const cookies = {
10
9
  * @param {string} value - Cookie value.
11
10
  * @param {number} time - Number denoting the expiration of the cookie.
12
11
  * @param {string} path - URL path that must exist in the requested URL in order to send the Cookie header.
13
- * @param {string?} domain - Cookie domain. Optional.
12
+ * @param {string} domain - Domain name of the server that set the cookie.
13
+ * @param {boolean} secure - A secure cookie is only sent to the server with an encrypted request over the HTTPS protocol.
14
+ * @param {string} sameSite - A SameSite cookie prevents the browser from sending this cookie along with cross-site requests
15
+ *
14
16
  * @access public
15
17
  *
16
- * @returns {void}
18
+ * @returns {boolean}
17
19
  *
18
20
  * Usage:
19
21
  * ```js
20
- * cookies.setCookie('gdpr', '2', cookies.setOneDay(), '/');
22
+ * cookies.setCookie('gdpr', '2', cookies.setOneDay(), '/', '.example.com', true, 'Strict');
21
23
  * ```
22
24
  */
23
- setCookie(key, value, time, path, domain) {
25
+ setCookie(key, value, time, path, domain, secure = true, sameSite = 'Lax') {
24
26
  const expires = new Date();
25
- expires.setTime(expires.getTime() + (time));
26
-
27
- let pathValue = '';
28
- let domainValue = '';
27
+ expires.setTime(expires.getTime() + time);
29
28
 
30
- if (typeof path !== 'undefined') {
31
- pathValue = `;path=${path}`;
32
- }
29
+ const cookieParts = {
30
+ value: `${key}=${value}`,
31
+ expires: `expires=${expires.toUTCString()}`,
32
+ sameSite: `SameSite=${sameSite}`,
33
+ path: path ? `path=${path}` : '',
34
+ domain: domain ? `domain=${domain}` : '',
35
+ secure: secure ? 'Secure' : '',
36
+ };
33
37
 
34
- if (typeof domain !== 'undefined') {
35
- domainValue = `;domain=${domain}`;
36
- }
38
+ try {
39
+ document.cookie = Object.values(cookieParts).filter(Boolean).join('; ');
40
+
41
+ return true;
42
+ } catch (e) {
43
+ console.error('Failed to set cookie:', e);
37
44
 
38
- document.cookie = `${key}=${value}${pathValue}${domainValue};expires=${expires.toUTCString()}`;
45
+ return false;
46
+ }
39
47
  },
40
48
 
41
49
  /**
@@ -1,7 +1,7 @@
1
1
  import domReady from '@wordpress/dom-ready';
2
2
  import apiFetch from '@wordpress/api-fetch';
3
- import { subscribe, select } from '@wordpress/data';
4
- import { debounce, isEmpty } from '@eightshift/ui-components/utilities';
3
+ import { addAction } from '@wordpress/hooks';
4
+ import { select } from '@wordpress/data';
5
5
 
6
6
  /* global YoastSEO */
7
7
 
@@ -9,61 +9,87 @@ import { debounce, isEmpty } from '@eightshift/ui-components/utilities';
9
9
  * Attributes with this key will be passed as custom data to YoastSEO's analysis.
10
10
  * See https://developer.yoast.com/customization/yoast-seo/adding-custom-data-analysis for more info.
11
11
  */
12
- export const yoastSeo = () => {
12
+ export const yoastSeo = (options) => {
13
+ const filterPriority = options?.filterPriority || 20;
14
+ const filterName = options?.filterName || 'EightshiftCustomSeo';
15
+
13
16
  domReady(() => {
14
- // Bailout if plugin is missing.
15
- if (typeof YoastSEO === 'undefined' && typeof YoastSEO?.app === 'undefined') {
17
+ // Ensure YoastSEO.js is present and can access the necessary features.
18
+ if (
19
+ typeof YoastSEO === 'undefined' ||
20
+ typeof YoastSEO.analysis === 'undefined' ||
21
+ typeof YoastSEO.analysis.worker === 'undefined'
22
+ ) {
16
23
  return;
17
24
  }
18
25
 
19
- // Local variable content, used to update Yoast modifications.
20
- let content = '';
21
- let isDataAvailable = false;
26
+ // Initialize the state and dirty flag.
27
+ let state = '';
28
+ let isDirty = true;
29
+
30
+ /**
31
+ * This function fetches the content of the post from the API.
32
+ * It will only fetch the content data and will not refresh the content.
33
+ *
34
+ * @param {boolean} shouldReload Should the content be refreshed after fetching.
35
+ */
36
+ const fetchContent = async (shouldReload = true) => {
37
+ // Set the dirty flag to false.
38
+ isDirty = false;
39
+
40
+ // Find API url for single item.
41
+ const apiUrl = select('core/editor')?.getCurrentPost()?.['_links']?.['wp:action-publish']?.[0]?.href;
22
42
 
23
- YoastSEO.app.registerPlugin('EightshiftCustomSeo', { status: 'ready' });
24
- YoastSEO.app.registerModification('content', () => content, 'EightshiftCustomSeo', 5);
43
+ if (typeof apiUrl === 'undefined') {
44
+ return;
45
+ }
25
46
 
26
- // Subscribe to changes.
27
- subscribe(
28
- // Small debounce for more optimisations in loading.
29
- debounce(() => {
30
- // Filter only when saved or autosaved.
31
- const isSavingPost = wp.data.select('core/editor').isSavingPost();
32
- const isAutosavingPost = wp.data.select('core/editor').isAutosavingPost();
47
+ // Fetch content from the api with only content data in it.
48
+ const response = await apiFetch({
49
+ url: `${apiUrl}?_fields=content`,
50
+ method: 'GET',
51
+ });
33
52
 
34
- // Get the new current post when ready.
35
- const currentPost = select('core/editor').getCurrentPost();
53
+ // Set the content to the state.
54
+ state = response?.content?.rendered || '';
36
55
 
37
- // Filter subscribes. Check only if post is saving, autosaving or initial load.
38
- if (isEmpty(currentPost) || (isDataAvailable && !isSavingPost && !isAutosavingPost)) {
39
- return;
40
- }
56
+ // Refresh the content if needed.
57
+ if (shouldReload) {
58
+ YoastSEO.app.refresh();
59
+ }
60
+ };
41
61
 
42
- isDataAvailable = true;
62
+ /**
63
+ * This function is called by the editor to save the content of the post.
64
+ * It will refresh the content after the save is done.
65
+ *
66
+ * @param {string} edits The content of the post.
67
+ *
68
+ * @return {string} The content of the post.
69
+ */
70
+ const onSaveCallback = (edits) => {
71
+ fetchContent();
43
72
 
44
- // Find API url for single item.
45
- const apiUrl = currentPost['_links']['wp:action-publish'][0].href;
73
+ return edits;
74
+ };
46
75
 
47
- if (typeof apiUrl === 'undefined') {
48
- return;
49
- }
76
+ /**
77
+ * This function is called by YoastSEO to get the content of the post.
78
+ *
79
+ * @return {string} The content of the post.
80
+ */
81
+ const registerYoastSeoPlugin = () => {
82
+ // Set the dirty flag to true.
83
+ if (isDirty) {
84
+ fetchContent(false);
85
+ }
50
86
 
51
- // Fetch content from the api with only content data in it.
52
- apiFetch({
53
- url: `${apiUrl}?_fields=content`,
54
- method: 'GET',
55
- }).then((response) => {
56
- const fetchedContent = response?.content?.rendered;
87
+ return state;
88
+ };
57
89
 
58
- if (typeof content === 'undefined' && fetchedContent === content) {
59
- return;
60
- }
61
- // Updating global variable content.
62
- content = fetchedContent;
63
- // Refreshing Yoast input.
64
- YoastSEO.app.pluggable.refresh();
65
- });
66
- }, 50),
67
- );
90
+ // Register the filter and the plugin.
91
+ addAction('editor.savePost', filterName, onSaveCallback);
92
+ YoastSEO.app.registerPlugin(filterName, { status: 'ready' });
93
+ YoastSEO.app.registerModification('content', registerYoastSeoPlugin, filterName, filterPriority);
68
94
  });
69
95
  };
package/webpack/base.mjs CHANGED
@@ -53,11 +53,6 @@ export default (options) => {
53
53
  plugins.push(
54
54
  new DependencyExtractionWebpackPlugin({
55
55
  outputFormat: 'json',
56
- requestToExternal: function (request) {
57
- if (request === '@wordpress/dom-ready') {
58
- return '';
59
- }
60
- },
61
56
  }),
62
57
  );
63
58
  }
@@ -78,6 +73,16 @@ export default (options) => {
78
73
  });
79
74
  }
80
75
 
76
+ // Module for MJS.
77
+ if (!options.overrides.includes('mjs')) {
78
+ module.rules.push({
79
+ test: /\.m?js/,
80
+ resolve: {
81
+ fullySpecified: false,
82
+ },
83
+ });
84
+ }
85
+
81
86
  // Module for CSS.
82
87
  if (!options.overrides.includes('css')) {
83
88
  module.rules.push({
@@ -10,7 +10,6 @@ import path from 'path';
10
10
  *
11
11
  * @param {string} projectDir Current project directory absolute path.
12
12
  * @param {string} projectPathConfig Project path relative to project root.
13
- * @param {string} assetsPathConfig Assets path after projectPath location.
14
13
  * @param {string} blocksAssetsPathConfig Path of the block assets.
15
14
  * @param {string} outputPathConfig Public output path after projectPath location.
16
15
  * @param {string} blocksManifestSettingsPath Main global settings manifest.json path after projectPath location.
@@ -19,7 +18,6 @@ import path from 'path';
19
18
  function getConfig(
20
19
  projectDir,
21
20
  projectPathConfig,
22
- assetsPathConfig = 'assets',
23
21
  blocksAssetsPathConfig = 'src/Blocks/assets',
24
22
  outputPathConfig = 'public',
25
23
  blocksManifestSettingsPath = 'src/Blocks/manifest.json',
@@ -38,7 +36,6 @@ function getConfig(
38
36
 
39
37
  // Clear all slashes from user config.
40
38
  const projectPathConfigClean = projectPathConfig.replace(/^\/|\/$/g, '');
41
- const assetsPathConfigClean = assetsPathConfig.replace(/^\/|\/$/g, '');
42
39
  const blocksAssetsPathConfigClean = blocksAssetsPathConfig.replace(/^\/|\/$/g, '');
43
40
  const outputPathConfigClean = outputPathConfig.replace(/^\/|\/$/g, '');
44
41
  const blocksManifestSettingsPathClean = blocksManifestSettingsPath.replace(/^\/|\/$/g, '');
@@ -56,8 +53,7 @@ function getConfig(
56
53
  publicPath: path.join('/', projectPathConfigClean, outputPathConfigClean, '/'),
57
54
 
58
55
  // Source files entries absolute locations.
59
- applicationEntry: path.resolve(absolutePath, assetsPathConfigClean, 'application.js'),
60
- applicationAdminEntry: path.resolve(absolutePath, assetsPathConfigClean, 'application-admin.js'),
56
+ applicationAdminEntry: path.resolve(absolutePath, blocksAssetsPathConfigClean, 'application-admin.js'),
61
57
  applicationBlocksEntry: path.resolve(absolutePath, blocksAssetsPathConfigClean, 'application-blocks.js'),
62
58
  applicationBlocksEditorEntry: path.resolve(
63
59
  absolutePath,
package/webpack/index.mjs CHANGED
@@ -21,7 +21,6 @@ const eightshiftConfig = (mode, optionsData = {}) => {
21
21
  options.config = getConfig(
22
22
  optionsData.config.projectDir,
23
23
  optionsData.config.projectPath,
24
- optionsData.config.assetsPath,
25
24
  optionsData.config.blocksAssetsPath,
26
25
  optionsData.config.outputPath,
27
26
  optionsData.config.blocksManifestSettingsPath,
@@ -18,11 +18,6 @@ export default (options) => {
18
18
  library: '[name]',
19
19
  };
20
20
 
21
- // Load Application Entrypoint.
22
- if (!options.overrides.includes('application') && fs.existsSync(options.config.applicationEntry)) {
23
- entry.application = options.config.applicationEntry;
24
- }
25
-
26
21
  // Load ApplicationAdmin Entrypoint.
27
22
  if (!options.overrides.includes('applicationAdmin') && fs.existsSync(options.config.applicationAdminEntry)) {
28
23
  entry.applicationAdmin = options.config.applicationAdminEntry;
package/bun.lockb DELETED
Binary file
@@ -1,74 +0,0 @@
1
- # Packages readme
2
-
3
- This is a document explaining what each `package.json` package is used for.
4
-
5
- ## Dependencies
6
-
7
- * @babel/cli - Adds terminal commands for babel.
8
- * @babel/core - Main Babel core package.
9
- * @babel/eslint-parser - JS Parser for babel.
10
- * @babel/eslint-plugin - JS Parser for babel plugin.
11
- * @dnd-kit/core - Modern helper for creating re-orderable react components
12
- * @dnd-kit/modifiers - Modern helper for creating re-orderable react components
13
- * @dnd-kit/sortable - Modern helper for creating re-orderable react components
14
- * @dnd-kit/utilities - Modern helper for creating re-orderable react components
15
- * @infinumjs/eslint-config-react-js - Adds JS linter config.
16
- * @swc/core - loader swc for faster build times.
17
- * @wordpress/api-fetch - WP package for api fetch.
18
- * @wordpress/dependency-extraction-webpack-plugin - Allows Webpack to read all @wordpress packages as external dependency.
19
- * @wordpress/dom-ready - WP package to load JS when dom is ready.
20
- * @wordpress/icons - WP icons package.
21
- * autoprefixer - Package for adding vendor prefix depending on your browserlist.
22
- * babel-loader - Loader used for Webpack.
23
- * browser-sync - Browser sync.
24
- * browser-sync-webpack-plugin - Browser sync plugin for Webpack.
25
- * classnames - Classnames package used in view components.
26
- * clean-webpack-plugin - Webpack plugin used to delete build folder.
27
- * core-js - Latest core Js to use in babel.
28
- * css-loader - Webpack loader user for css.
29
- * css-minimizer-webpack-plugin - Webpack plugin used to minimize output.
30
- * eslint - Package used for linting JS.
31
- * eslint-plugin-jsx-a11y - Package used for linting JS.
32
- * eslint-plugin-react - Package used for linting JS.
33
- * eslint-plugin-react-hooks - Package used for linting JS.
34
- * file-loader - Webpack loaded used for images and files.
35
- * husky - Package used for linting code on git commands.
36
- * import-glob - Webpack loader used for putting regex in css import.
37
- * media-blender - Package used for providing media queries.
38
- * mini-css-extract-plugin - Webpack plugin extracts CSS into separate files.
39
- * postcss - package for getting postcss options.
40
- * postcss-loader - Webpack loader used for postcss.
41
- * postcss-scss - package for parsing scss used for style linter.
42
- * promisify-child-process - Async child process library used in project setup.
43
- * raw-loader - Webpack loader used for files.
44
- * rc-slider - React slider component used in our components.
45
- * rc-tooltip - React tooltip component used in our components.
46
- * react-select - Select component for React.
47
- * regenerator-runtime - Used for Core-js polyfill.
48
- * sass - A pure JavaScript implementation of Sass
49
- * sass-loader - Webpack loader used for sass.
50
- * storybook - Storybook library used in projects.
51
- * style-loader - Webpack loader used for styles.
52
- * stylelint - Css linter.
53
- * stylelint-config-standard-scss - Css linter for scss type.
54
- * terser-webpack-plugin - Webpack plugin used for minimization.
55
- * webpack - Webpack library.
56
- * webpack-cli - Webpack cli library.
57
- * webpack-manifest-plugin - Webpack plugin used for outputting manifest.json.
58
- * webpack-merge - Webpack utility for merging multiple webpack files.
59
-
60
- ## Dev dependencies
61
-
62
- * @babel/preset-env - Is a smart preset that allows you to use the latest JavaScript without needing to micromanage.
63
- * @babel/preset-react - Babel preset for React.
64
- * @eightshift/storybook - Our storybook used for frontend libs.
65
- * chalk - Terminal coloring library. Used in project setup.
66
- * del - Terminal delete library. Used in project setup.
67
- * gh-pages - Terminal GH pages lib for deployment to GH pages.
68
- * micromodal - Modal component used in the project here only for storybook.
69
- * ol - Map component used in the project here only for storybook.
70
- * ol-mapbox-style - Map component used in the project here only for storybook.
71
- * react-test-renderer - This package provides an experimental React renderer that can be used to render React components to pure JavaScript objects.
72
- * replace-in-file - A simple utility to quickly replace text in one or more files or globs.
73
- * sassdoc - Sass docs library.
74
- * swiper - Swiper component used in the project here only for storybook.