diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000000..490161b965 --- /dev/null +++ b/.babelrc @@ -0,0 +1,17 @@ +{ + "presets": [ + [ + "@babel/preset-env", + { + "useBuiltIns": "entry", + "targets": { + "esmodules": true + } + } + ], + ["@babel/preset-react", { + "runtime": "automatic" + }], + "@babel/preset-typescript" + ] +} diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000000..b6ffc9085b --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,7 @@ +{ + "extends": [ + "react-app", + "plugin:storybook/recommended" + ], + "ignorePatterns": "stories" +} diff --git a/.flowconfig b/.flowconfig new file mode 100644 index 0000000000..d8078f59d2 --- /dev/null +++ b/.flowconfig @@ -0,0 +1,21 @@ +[include] +src/.* +stories/.* + +[lints] +all=error +untyped-import=off +unclear-type=off +sketchy-null=off +unsafe-getters-setters=warn +dynamic-export=off +inexact-spread =off + +[options] +include_warnings=false +module.ignore_non_literal_requires=true + +[strict] + +[untyped] +.*/node_modules/**/.* diff --git a/.github/ISSUE_TEMPLATE/bug.md b/.github/ISSUE_TEMPLATE/bug.md new file mode 100644 index 0000000000..b2d15ce83a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.md @@ -0,0 +1,18 @@ +--- +name: Bug Report +about: Report a bug encountered while operating core-ui +labels: bug,core-ui + +--- + +**Component**: + + + +**What happened**: + +**What was expected**: + +**Steps to reproduce** + +**Resolution proposal** (optional): diff --git a/.github/ISSUE_TEMPLATE/improvement.md b/.github/ISSUE_TEMPLATE/improvement.md new file mode 100644 index 0000000000..a63730b7cf --- /dev/null +++ b/.github/ISSUE_TEMPLATE/improvement.md @@ -0,0 +1,18 @@ +--- +name: Improvement +about: Suggest an improvement to the core-ui project +labels: core-ui + +--- + +**Component**: + + + +**Why this is needed**: + +**What should be done**: + +**Implementation proposal** (strongly recommended): + +**Test plan**: diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000000..5661d45597 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,24 @@ +**Component**: + + + +**Description**: + +**Design**: + +**Breaking Changes**: + +- [] Breaking Changes + + +--- + + + +Closes: #ISSUE_NUMBER + + diff --git a/.github/workflows/codeql.yaml b/.github/workflows/codeql.yaml new file mode 100644 index 0000000000..a6e5754af5 --- /dev/null +++ b/.github/workflows/codeql.yaml @@ -0,0 +1,25 @@ +--- +name: codeQL + +on: + push: + branches: [development/*, stabilization/*, hotfix/*] + pull_request: + branches: [development/*, stabilization/*, hotfix/*] + workflow_dispatch: + +jobs: + analyze: + name: Static analysis with CodeQL + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: typescript, javascript + + - name: Build and analyze + uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/dependency-review.yaml b/.github/workflows/dependency-review.yaml new file mode 100644 index 0000000000..315bd64d00 --- /dev/null +++ b/.github/workflows/dependency-review.yaml @@ -0,0 +1,18 @@ +--- +name: dependency review + +on: + pull_request: + branches: [development/*, stabilization/*, hotfix/*] + +jobs: + dependency-review: + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + uses: actions/checkout@v3 + + - name: Dependency Review + uses: actions/dependency-review-action@v3 + with: + fail-on-severity: high diff --git a/.github/workflows/github-pages.yml b/.github/workflows/github-pages.yml new file mode 100644 index 0000000000..d4faa2e528 --- /dev/null +++ b/.github/workflows/github-pages.yml @@ -0,0 +1,17 @@ +name: Deploy Storybook + +on: + push: + branches: [development/1.0] +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20 + - run: npm ci + - run: npm run storybook:deploy -- --ci + env: + GH_TOKEN: ${{ github.actor }}:${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/post-release.yml b/.github/workflows/post-release.yml new file mode 100644 index 0000000000..e206914e40 --- /dev/null +++ b/.github/workflows/post-release.yml @@ -0,0 +1,52 @@ +on: + release: + types: + - published + workflow_dispatch: + +jobs: + bump-version: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install node + uses: actions/setup-node@v2 + with: + node-version: '16' + - run: echo "nextVersion=$(npm version --no-git-tag minor)" >> $GITHUB_ENV + - name: Create Pull Request + uses: peter-evans/create-pull-request@v3 + id: cpr + with: + token: ${{ secrets.GIT_ACCESS_TOKEN }} + branch: feature/bump-core-ui-version-to-${{ env.nextVersion }} + commit-message: Bump core-ui version to ${{ env.nextVersion }} + title: Bump core-ui version to ${{ env.nextVersion }} + body: '' + base: ${{ github.event.repository.default_branch }} + - uses: actions/github-script@v5 + if: ${{ steps.cpr.outputs.pull-request-number }} + with: + github-token: ${{secrets.GIT_ACCESS_TOKEN}} + script: | + github.rest.issues.createComment({ + issue_number: '${{ steps.cpr.outputs.pull-request-number }}', + owner: context.repo.owner, + repo: context.repo.repo, + body: '/approve' + }) + publish-npm: + runs-on: ubuntu-latest + environment: npmjs + steps: + - uses: actions/checkout@v3 + # Setup .npmrc file to publish to npmjs.org + - uses: actions/setup-node@v3 + with: + node-version: '16' + registry-url: 'https://registry.npmjs.org' + - run: npm install + - run: npm run build + - run: npm publish + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml new file mode 100644 index 0000000000..a27e5fc7cb --- /dev/null +++ b/.github/workflows/tests.yaml @@ -0,0 +1,32 @@ +name: basic tests + +on: + push: + branches: + - 'user/**' + - 'feature/**' + - 'improvement/**' + - 'bugfix/**' + - 'w/**' + - 'q/**' + - 'hotfix/**' + - 'dependabot/**' + pull_request: + types: + - opened + branches: + - 'feature/bump-core-ui-version-to-**' + +jobs: + tests: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Install node + uses: actions/setup-node@v4 + with: + node-version: 20 + - run: npm ci + - run: npm run test + - run: npm run lint diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..6ab372b4b8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +node_modules/ +dist/ +coverage/ + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +.vscode \ No newline at end of file diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000000..521a9f7c07 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +legacy-peer-deps=true diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000000..946c64ff68 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,5 @@ +{ + "tabWidth": 2, + "trailingComma": "all", + "singleQuote": true +} diff --git a/.storybook/main.ts b/.storybook/main.ts new file mode 100644 index 0000000000..a502830342 --- /dev/null +++ b/.storybook/main.ts @@ -0,0 +1,55 @@ +import type { StorybookConfig } from '@storybook/react-webpack5'; +const config: StorybookConfig = { + stories: ['../stories/**/*.@(mdx|stories.@(ts|tsx))'], + staticDirs: ['./public'], + + addons: [ + '@storybook/addon-essentials', + '@storybook/addon-storysource', + '@storybook/addon-mdx-gfm', + '@storybook/addon-webpack5-compiler-swc', + '@chromatic-com/storybook', + ], + swc: (config, options) => ({ + jsc: { + transform: { + react: { + runtime: 'automatic', + }, + }, + }, + }), + + webpackFinal: async (config, { configType }) => { + // Resolve error when webpack-ing storybook: + // Can't import the named export 'Children' from non EcmaScript module (only + // default export is available) + config.module?.rules?.push({ + test: /\.mjs$/, + include: /node_modules/, + type: 'javascript/auto', + }); + + return config; + }, + + framework: { + name: '@storybook/react-webpack5', + options: {}, + }, + + docs: { + defaultName: 'Stories', + }, + + managerHead: (head) => ` + ${head} + +`, + + typescript: { + reactDocgen: 'react-docgen-typescript', + }, +}; + +export default config; diff --git a/.storybook/manager.ts b/.storybook/manager.ts new file mode 100644 index 0000000000..ea07ae0f47 --- /dev/null +++ b/.storybook/manager.ts @@ -0,0 +1,6 @@ +import { addons } from '@storybook/manager-api'; +import Theme from './theme.storybook'; + +addons.setConfig({ + theme: Theme, +}); diff --git a/.storybook/preview-head.html b/.storybook/preview-head.html new file mode 100644 index 0000000000..03b497519e --- /dev/null +++ b/.storybook/preview-head.html @@ -0,0 +1,99 @@ + + + diff --git a/.storybook/preview.js b/.storybook/preview.js new file mode 100644 index 0000000000..e96c83d601 --- /dev/null +++ b/.storybook/preview.js @@ -0,0 +1,104 @@ +import React from 'react'; +import { QueryClient, QueryClientProvider } from 'react-query'; +import { CoreUiThemeProvider } from '../src/lib/next'; +import { brand, coreUIAvailableThemes } from '../src/lib/style/theme'; +import { Wrapper } from '../stories/common'; +import { ToastProvider } from '../src/lib'; + +export const globalTypes = { + theme: { + name: 'Theme', + description: 'Global theme for components', + defaultValue: 'darkRebrand', + toolbar: { + title: 'Preview Theme', + dynamicTitle: true, + // array of plain string values or MenuItem shape (see below) + items: [ + { value: 'darkRebrand', title: ' A-Dark', icon: 'moon' }, + { value: 'artescaLight', title: 'A-Light', icon: 'sun' }, + { value: 'ring9dark', title: 'R-Dark', icon: 'moon' }, + ], + }, + }, + background: { + name: 'Background Level', + description: 'Background for the wrapper', + toolbar: { + title: 'Background Level', + items: [ + { value: 'backgroundLevel1', title: 'backgroundLevel 1' }, + { value: 'backgroundLevel2', title: 'backgroundLevel 2' }, + { value: 'backgroundLevel3', title: 'backgroundLevel 3' }, + { value: 'backgroundLevel4', title: 'backgroundLevel 4' }, + ], + dynamicTitle: true, + }, + }, +}; +const withThemeProvider = (Story, context) => { + const theme = coreUIAvailableThemes[context.globals.theme]; + const { background } = context.globals; + const { viewMode } = context; + return ( + + + {/* Wrapper to make the stories take the full screen but not in docs */} +
+ + + + + +
+
+
+ ); +}; + +export const decorators = [withThemeProvider]; + +export const parameters = { + layout: 'fullscreen', + docs: { + toc: { headingSelector: 'h2,h3', title: 'Table of Contents' }, + }, + controls: { + //All props with color in name will automatically have a control 'color' + //with colors presets to theme colors, possible to have the color name from theme in control + presetColors: Object.entries(brand).map((color) => { + return { color: color[1], title: color[0] }; + }), + matchers: { + color: /color/i, + }, + exclude: ['data-cy'], + }, + options: { + storySort: { + order: [ + 'Introduction', + 'Style', + 'Guidelines', + 'Templates', + 'Components', + [ + 'Navigation', + 'Data Display', + 'Inputs', + 'Feedback', + 'Progress & loading', + 'Styling', + 'Deprecated', + ], + ], + }, + }, +}; +export const tags = ['autodocs']; diff --git a/.storybook/public/favicon.ico b/.storybook/public/favicon.ico new file mode 100644 index 0000000000..a0b19199bf Binary files /dev/null and b/.storybook/public/favicon.ico differ diff --git a/.storybook/public/logo-core-ui.png b/.storybook/public/logo-core-ui.png new file mode 100644 index 0000000000..0fb5f9f494 Binary files /dev/null and b/.storybook/public/logo-core-ui.png differ diff --git a/.storybook/theme.storybook.ts b/.storybook/theme.storybook.ts new file mode 100644 index 0000000000..b9baba0c46 --- /dev/null +++ b/.storybook/theme.storybook.ts @@ -0,0 +1,13 @@ +import { create } from '@storybook/theming'; + +export default create({ + base: 'dark', + // Typography + fontBase: '"Open Sans", sans-serif', + fontCode: 'monospace', + + brandTitle: 'Core UI', + brandUrl: './', + brandImage: './logo-core-ui.png', + brandTarget: '_self', +}); diff --git a/LICENSE b/LICENSE new file mode 100755 index 0000000000..261eeb9e9f --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md new file mode 100755 index 0000000000..46334a2685 --- /dev/null +++ b/README.md @@ -0,0 +1,296 @@ +# Welcome to Scality Core-UI + +Core-UI is a component library containing all components, layouts, icons and themes used for all Scality UI projects. + +## Getting started + +### Installation + +#### Manual installation + +- Add ```@scality/core-ui``` in the ```package.json```'s dependencies of your project. + +```json + "@scality/core-ui": "0.115.0", +``` + +- ```@scality/core-ui``` requires the peerDependencies below. Make sure that you have them in the ```package.json```'s dependencies. + +```json + "@fortawesome/fontawesome-free": "^5.10.2", + "@fortawesome/fontawesome-svg-core": "^1.2.35", + "@fortawesome/free-regular-svg-icons": "^5.15.3", + "@fortawesome/free-solid-svg-icons": "^5.15.3", + "@fortawesome/react-fontawesome": "^0.1.14", + "@js-temporal/polyfill": "^0.4.4", + "polished": "3.4.1", + "pretty-bytes": "^5.6.0", + "react": "^17.0.2", + "react-debounce-input": "3.2.2", + "react-dom": "^17.0.2", + "react-dropzone": "^14.2.3", + "react-query": "^3.34.0", + "react-router": "^5.2.0", + "react-router-dom": "^5.2.0", + "react-select": "4.3.1", + "react-table": "^7.7.0", + "react-virtualized": "9.22.3", + "react-virtualized-auto-sizer": "^1.0.5", + "react-window": "^1.8.6", + "styled-components": "^4.1.2", + "styled-system": "^5.1.5", + "vega": "^5.17.3", + "vega-embed": "^6.0.0", + "vega-lite": "^5.0.0", + "vega-tooltip": "^0.27.0" +``` + +- Install the dependencies : + +```sh +npm install +``` + +### Usage + +- Import a component from ```@scality/core-ui/dist/next'``` or ```@scality/core-ui``` + +- Use props to change its appearance and behaviour + +```jsx +import { Button } from '@scality/core-ui/dist/next'; +import { Icon } from '@scality/core-ui'; + + + + + ); +}; + +describe('SelectV2', () => { + const selectors = { + option: (name: string | RegExp) => screen.getByRole('option', { name }), + options: () => screen.queryAllByRole('option'), + select: (withSearch?: boolean, name?: string) => { + if (withSearch) { + return screen.getByRole('combobox', { name }); + } + return screen.getByRole('listbox', { name }); + }, + input: () => screen.getByRole('textbox'), + noOptions: () => screen.getByText(/No options/i), + highlightedText: () => screen.getByRole('mark'), + }; + + it('should throw error if