소스 검색

[frontend] Add and configure cuix library (#3282)

* [frontend] Add and configure cuix library

* [frontent] improved contributing-frontend based on feedback

* [frontend] updated licence checker to include cuix libs
Bjorn Alm 2 년 전
부모
커밋
e390a6128d

+ 111 - 42
contributing-frontend.md

@@ -1,91 +1,98 @@
-![alt text](https://raw.githubusercontent.com/cloudera/hue/master/docs/images/hue_logo.png "Hue Logo")
+![alt text](https://raw.githubusercontent.com/cloudera/hue/master/docs/images/hue_logo.png 'Hue Logo')
 
 Thank you for considering contributing to Hue!
 
 # Contributing to Hue frontend
+
 The Frontend in Hue is currently made up of a combination of frameworks and libraries that have been added over time. It uses python templates (.mako files), Knockout.js, jQuery, Bootstrap, plain javascript, Vue and React.js. The Hue team is planning on migrating all existing frontend code to React.js. This section explains how to contribute to Hue using React.js.
 
 ## Adding new components to Hue
+
 If you want to add a new component as a descendant to an already existing react component it can be imported and used as normal. Components that are globally reused throughout Hue should be created in the folder [reactComponents](https://github.com/cloudera/hue/tree/master/desktop/core/src/desktop/js/reactComponents) and application specific components should be created in the component folder (or subfolder) of that app, e.g. a component specific to the Editor's result section should be created in [apps/editor/components/result](https://github.com/cloudera/hue/tree/master/desktop/core/src/desktop/js/apps/editor/components/result)
 
-If you want to add a react component where there currently is no react ancestor in the DOM you will have to integrate it as a react root node in the page itself, either directly in an HTML page or by using the Knockout-to-React bridge. There is currently no Vue-to-React bridge in Hue. 
+If you want to add a react component where there currently is no react ancestor in the DOM you will have to integrate it as a react root node in the page itself, either directly in an HTML page or by using the Knockout-to-React bridge. There is currently no Vue-to-React bridge in Hue.
+
+A react root element (or the first container element within it) must include the HTML classes `cuix` and `antd` in order for the styling of the child components to work. 
 
 ### HTML/Mako page integration
+
 If your react component isn't dependent on any Knockout observables or Vue components you can integrate it by adding a small script and a component reference directly in the HTML code. The following example integrates MyComponent as a react root in an HTML/mako file.
 
- ```HTML
- <script type="text/javascript">
-   (function () {
-     window.createReactComponents('#some-container');
-   })();
- </script>
- 
- <div id="some-container">
-   <MyComponent data-reactcomponent='MyComponent' data-props='{"myObj": {"id": 1}, "children": "mako template only", "version" : "${sys.version_info[0]}"}'></MyComponent>
- </div>
- ```
+```HTML
+<script type="text/javascript">
+  (function () {
+    window.createReactComponents('#some-container');
+  })();
+</script>
+
+<div id="some-container">
+  <MyComponent data-reactcomponent='MyComponent' data-props='{"myObj": {"id": 1}, "children": "mako template only", "version" : "${sys.version_info[0]}"}'></MyComponent>
+</div>
+```
 
 The MyComponent tag must be present as a descendant of the DOM element specified by the selector (#some-container) when this script runs. The attribute `data-reactcomponent` is mapped to the component name and `data-props` contains the props in JSON format.
 
- The component must also be imported and added to the file [js/reactComponents/imports.js](https://github.com/cloudera/hue/blob/master/desktop/core/src/desktop/js/reactComponents/imports.js) 
+The component must also be imported and added to the file [js/reactComponents/imports.js](https://github.com/cloudera/hue/blob/master/desktop/core/src/desktop/js/reactComponents/imports.js)
+
+```js
+ case 'MyComponent':
+   return (await import('./MyComponent/MyComponent')).default;
+```
+
+### Knockout.js integration
+
+If the new component is dependent on one or more Knockout observables you should use the Knockout-to-React bridge provided by the KO-binding `reactWrapper`. The binding expects the name of the react component and the react props (i.e. the KO observables of interest) as shown in the example below.
 
- ```js
-  case 'MyComponent':
-    return (await import('./MyComponent/MyComponent')).default;  
- ```
- 
- ### Knockout.js integration
- If the new component is dependent on one or more Knockout observables you should use the Knockout-to-React bridge provided by the KO-binding `reactWrapper`. The binding expects the name of the react component and the react props (i.e. the KO observables of interest) as shown in the example below. 
+```HTML
+<MyComponent data-bind="reactWrapper: 'MyComponent', props: { executable: activeExecutable }"></MyComponent>
+```
 
- ```HTML
- <MyComponent data-bind="reactWrapper: 'MyComponent', props: { executable: activeExecutable }"></MyComponent> 
- ```
+## Writing React components
 
- ## Writing React components
- Hue react components are written as functional components in [Typescript](https://www.typescriptlang.org/) with styles written in external [SCSS](https://sass-lang.com/) files using [BEM notation](https://getbem.com/). The component tsx file needs to import the scss file. Each component should normally be placed in a folder with the same name as the component itself. Test files, mocks and styles are placed in the same folder. Mock files are optional.
+Hue react components are written as functional components in [Typescript](https://www.typescriptlang.org/) with styles written in external [SCSS](https://sass-lang.com/) files using [BEM notation](https://getbem.com/). The component tsx file needs to import the scss file. Each component should normally be placed in a folder with the same name as the component itself. Test files, mocks and styles are placed in the same folder. Mock files are optional.
 
 ```
 components/MyComponent
 │   MyComponent.tsx
-│   MyComponent.scss    
+│   MyComponent.scss
 │   MyComponent.test.tsx
 └── Mocks
 │   │   MyComponent.tsx
-``` 
- 
+```
+
 Hue does not use the React PropTypes or defaulProps. Instead type checking for the component props is done using an interface and default values are set using ES6 default parameters.
 
 ### i18n
+
 All texts exposed to the end user (paragraphs, labels, alt texts, aria text etc) should be internationalized. To help with this Hue has a hook called `useTranslation` in module [i18nReact](https://github.com/cloudera/hue/blob/master/desktop/core/src/desktop/js/utils/i18nReact.ts). This is a wrapper around the hook provided by the external package `react-i18next`.
 
-```js
+```ts
 const { t } = i18nReact.useTranslation();
 const myGreeting = t('Hello');
 ```
 
 The useTranslation hook will automatically suspend your component from rendering until the language files have been loaded from the backend. If you want to provide your own loading indicator you can disable the suspense with the config object as shown below. The `ready` property will be false until the translate function is ready to use. Unless you are writing a React root component this is normally not something you have to think about.
 
-```js
+```ts
 const { t, ready } = i18nReact.useTranslation(undefined, { useSuspense: false });`
 ```
 
 The useTranslation hook is automatically mocked in the unit tests. The json based language files used by i18nReact are located in folder [src/desktop/static/desktop/locales](https://github.com/cloudera/hue/blob/master/desktop/core/src/desktop/static/desktop/locales/) and the default namespace is `translation`. See [i18next](https://github.com/i18next/i18next) for more info.
 
-
-
 ### Application wide communication
+
 For communication with non react based parts of Hue there is a publish–subscribe messaging system called [huePubsub](https://github.com/cloudera/hue/blob/master/desktop/core/src/desktop/js/utils/huePubSub.ts). To publish a message, call the `publish` function with a topic. You can subscribe to a topic using the hook [usePubSub](https://github.com/cloudera/hue/blob/master/desktop/core/src/desktop/js/reactComponents/useHuePubSub.ts) which will force your functional component to rerender once a message matching the topic is published. In the example below the editCursor state is updated by useHuePubSub each time a CURSOR_POSITION_CHANGED_EVENT is published.
 
-```js
+```ts
 const editorCursor = useHuePubSub<EditorCursor>({ topic: CURSOR_POSITION_CHANGED_EVENT });
 ```
 
+## Unit testing
 
- ## Unit testing
- Tests are written using [Jest](https://jestjs.io/) and [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/). New test files will be automatically picked up by the test runner which is started with the command `npm run test`. Try to use the `userEvent` instead of the low level `fireEvent` when simulating user events. Below is a simple test file example.
+Tests are written using [Jest](https://jestjs.io/) and [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/). New test files will be automatically picked up by the test runner which is started with the command `npm run test`. Try to use the `userEvent` instead of the low level `fireEvent` when simulating user events. Below is a simple test file example.
 
- ```js
+```ts
 import React from 'react';
 import { render, screen } from '@testing-library/react';
 import userEvent from '@testing-library/user-event';
@@ -93,18 +100,80 @@ import '@testing-library/jest-dom';
 
 import MyComponent from './MyComponent';
 
-describe('MyComponent', () => {  
-
+describe('MyComponent', () => {
   test('disables after click', async () => {
     // It is recommended to call userEvent.setup per test
     const user = userEvent.setup();
     render(<MyComponent />);
-    // Insctruct the "user" to click the button with the text "Click me"
+    // Instruct the "user" to click the button with the text "Click me"
     const btn = screen.getByRole('button', { name: 'Click me' });
     expect(btn).not.toBeDisabled();
     await user.click(btn);
-    
+
     expect(btn).toBeDisabled();
   });
 });
- ```
+```
+
+## Component libraries
+
+Hue uses a component library from Cloudera called cuix and the open source library Ant Design. The cuix components all come pre styled for usage in Hue and many of the Ant Design components have been restyled to fit the new look and feel of Hue. Always use the cuix components when possible, and if something else is needed try one of the Ant Design components before writing your own.
+
+Example of how to import and use cuix and antd components in Hue:
+
+```ts
+// The Pagination styles from antd are automatically overwritten by cuix to fit Hue
+import { Pagination } from 'antd';
+// Cuix exports its own primary button based on the antd button
+import PrimaryButton from 'cuix/dist/components/Button/PrimaryButton';
+```
+
+If you are importing a component from Ant Design that is new to Hue you also need to import its styles into the
+file `desktop/core/src/desktop/static/desktop/less/root-wrapped-antd.less`. If you would import the pagination component for the first time you would write
+
+```less
+import 'node_modules/antd/es/pagination/style/index.less';
+```
+
+## Styling
+
+Hue uses `Sass` while both cuix and Ant Design use `Less` for writing CSS. The core cuix variables are however stil available in sass.
+
+Any component specific styling should be written in Sass and saved in a .scss file in the same folder as the component. Use [BEM](https://getbem.com/) when naming classes and prepend the `hue-` to all names.
+
+Predefined variables are available in [components/styles/variables.scss](https://github.com/cloudera/hue/blob/master/desktop/core/src/desktop/js/components/styles/variables.scss) and mixins are available in [components/styles/mixins.scss](https://github.com/cloudera/hue/blob/master/desktop/core/src/desktop/js/components/styles/mixins.scss). There are also shared classes available in [components/styles/classes.scss](https://github.com/cloudera/hue/blob/master/desktop/core/src/desktop/js/components/styles/classes.scss). Make sure to import the files with "@use" to guarantee that they are available for this component but never added twice to the transpiled CSS. See example Sass file below.
+
+```scss
+// In MyComponent.scss
+@use 'variables' as vars;
+@use 'mixins';
+@use 'classes';
+
+.hue-my-component__header--special-state {
+  // Import and use a cuix color variable when a color is needed.
+  // All cuix variables are prefixed with "fluidx-".
+  color: vars.$fluidx-purple-800;
+  @include mixins.fade-in();
+}
+```
+
+The scss file must be imported in the source code of your component.
+
+```ts
+// In MyComponent.tsx
+import React from 'react';
+import classNames from 'classnames';
+import './MyComponent.scss';
+
+const MyComponent = ({special}) => {
+  return (
+    // In this example MyComponent is a react root so we also add the cuix antd classes 
+    <div class="cuix antd">
+      <!-- The hue-1 class is defined in the shared styling file classes.scss -->
+      <h1 class={classNames('hue-h1', {['hue-my-component__header--special-state']: special})}>
+        Hello, this is MyComponent!
+      </h1>
+    </div>
+  );
+};
+```

+ 15 - 0
desktop/core/src/desktop/js/apps/editor/components/result/reactExample/ReactExample.test.tsx

@@ -4,6 +4,21 @@ import '@testing-library/jest-dom';
 
 import ReactExample from './ReactExample';
 
+// For now, this is needed to mock cuix components exported as ESM (ECMAScript module)
+jest.mock('cuix/dist/components/Button/PrimaryButton', () => () => undefined);
+jest.mock('@cloudera/cuix-core/icons/react/PlusCircleIcon', () => () => undefined);
+
+// Required by the antd Pagination component
+window.matchMedia = jest.fn().mockImplementation(query => {
+  return {
+    matches: false,
+    media: query,
+    onchange: null,
+    addListener: jest.fn(), // for older versions of Safari
+    removeListener: jest.fn() // for older versions of Safari
+  };
+});
+
 describe('ReactExample', () => {
   test('shows a title', () => {
     render(<ReactExample title="test title" />);

+ 14 - 5
desktop/core/src/desktop/js/apps/editor/components/result/reactExample/ReactExample.tsx

@@ -1,7 +1,10 @@
 'use strict';
 
 import React, { FunctionComponent, useState } from 'react';
-import { Button, Modal, Skeleton } from 'antd';
+import { Button, Modal, Skeleton, Pagination } from 'antd';
+import PrimaryButton from 'cuix/dist/components/Button/PrimaryButton';
+
+import PlusCircleIcon from '@cloudera/cuix-core/icons/react/PlusCircleIcon';
 
 // Provides a i18n translation hook
 import { i18nReact } from '../../../../../utils/i18nReact';
@@ -76,11 +79,17 @@ const ReactExample: FunctionComponent<ReactExampleProps> = ({ activeExecutable,
     // Also make sure that the component specific Antd style is imported in the file
     // 'root-wrapped-antd.less'.
     <React.StrictMode>
-      <div className="react-example antd">
+      <div className="react-example cuix antd">
         <h1 className="react-example__title">{title}</h1>
-        <Button type="primary" onClick={showModal}>
-          {t('Open')}
-        </Button>
+        <Pagination
+          showTotal={total => `Total ${total} items`}
+          showSizeChanger
+          defaultCurrent={1}
+          total={500}
+        />
+        <PrimaryButton icon={<PlusCircleIcon />} onClick={showModal}>
+          {t('Open (cuix button)')}
+        </PrimaryButton>
 
         <p className="react-example__description">
           I'm an Editor specific react component containing subcomponents. The dynamic id that I'm

+ 12 - 28
desktop/core/src/desktop/js/components/styles/variables.scss

@@ -16,8 +16,20 @@
  limitations under the License.
 */
 
+// THESE VARIABLES ARE FOR REACT.JS COMPONENTS
+// (and possibly some Vue components until they have been translated to react)
+
+// Many, but not all, cuix can be imported from the cuix-core library.
+// These variable are prefixed with fluidx which is the legacy name of cuix.
+// Includes both spacing and color variables.
+@import '@cloudera/cuix-core/variables.scss';
+
+// TODO: These variables are named diferently. We should remove the usage.
 @import './colors';
 
+// Some handy variables used by cuix are only available as less variables where the
+// origin of the value can be either cuix or antd. Here we manually recreate the once we
+// want in Hue as sass variables.
 $animation-duration-base: 0.2s;
 $animation-duration-slow: 0.3s;
 $text-color: $fluid-gray-900;
@@ -38,31 +50,3 @@ $fluid-spacing-xxl: 48px;
 $fluid-spacing-giant: 64px;
 
 $fluid-border-radius: 3px;
-
-$fluidx-heading-h1-size: 28px;
-$fluidx-heading-h1-line-height: 36px;
-$fluidx-heading-h1-weight: 300;
-$fluidx-heading-h2-size: 24px;
-$fluidx-heading-h2-line-height: 32px;
-$fluidx-heading-h2-weight: 300;
-$fluidx-heading-h3-size: 20px;
-$fluidx-heading-h3-line-height: 28px;
-$fluidx-heading-h3-weight: 300;
-$fluidx-heading-h4-size: 16px;
-$fluidx-heading-h4-line-height: 24px;
-$fluidx-heading-h4-weight: 500;
-$fluidx-heading-h5-size: 14px;
-$fluidx-heading-h5-line-height: 16px;
-$fluidx-heading-h5-weight: 500;
-$fluidx-heading-h6-size: 12px;
-$fluidx-heading-h6-line-height: 20px;
-$fluidx-heading-h6-weight: 500;
-
-/** For Modal Dialogs and Title Bars */
-$fluidx-title-h1-small-size: 20px;
-
-/** For Modal Dialogs and Title Bars */
-$fluidx-title-h1-small-line-height: 28px;
-
-/** For Modal Dialogs and Title Bars */
-$fluidx-title-h1-small-weight: 300;

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 0
desktop/core/src/desktop/static/desktop/css/hue.css


BIN
desktop/core/src/desktop/static/desktop/ext/cuix/cloudera-cuix-core-1.0.6.tgz


BIN
desktop/core/src/desktop/static/desktop/ext/cuix/cuix-13.0.4.tgz


+ 18 - 10
desktop/core/src/desktop/static/desktop/less/hue.less

@@ -15,16 +15,24 @@
  See the License for the specific language governing permissions and
  limitations under the License.
 */
-@import (reference) "cui/colors.less";
-@import (reference) "hue-mixins.less";
-@import "hue-assist";
-@import "hue-autocomplete";
-@import "hue-cui-overrides.less";
-@import "hue-cross-version.less";
-@import "hue-multi-cluster-icons.less";
-@import "components/hue-clearable.less";
-@import "hue4.less";
-@import "root-wrapped-antd.less";
+
+// Legacy styles for Mako, Knockout.js and Vue components
+@import (reference) 'cui/colors.less';
+@import (reference) 'hue-mixins.less';
+@import 'hue-assist';
+@import 'hue-autocomplete';
+@import 'hue-cui-overrides.less';
+@import 'hue-cross-version.less';
+@import 'hue-multi-cluster-icons.less';
+@import 'components/hue-clearable.less';
+@import 'hue4.less';
+
+// Styles for react.js components based on the Ant design library
+// and the Cuix library. Will only be applied for components wrapped in
+// the classes "cuix" and "antd" in order not to intefere with the legacy styles.
+@import 'root-wrapped-antd.less';
+@import 'cuix/dist/styles/variables.less';
+@import 'cuix/dist/styles/cuix-ant-overrides.less';
 
 html {
   height: 100%;

+ 2 - 1
desktop/core/src/desktop/static/desktop/less/root-wrapped-antd.less

@@ -13,7 +13,7 @@
   // Import global antd styles
   @root-entry-name: variable;
   @import 'node_modules/antd/es/style/core/index.less';
-  @import 'node_modules/antd/es/style/themes/default.less';
+  @import (reference) 'node_modules/antd/es/style/themes/default.less';
 
   // Import the styles for the actual components used
   @import 'node_modules/antd/es/button/style/index.less';
@@ -29,6 +29,7 @@
   @import 'node_modules/antd/es/dropdown/style/index.less';
   @import 'node_modules/antd/es/tooltip/style/index.less';
   @import 'node_modules/antd/es/grid/style/index.less';
+  @import 'node_modules/antd/es/pagination/style/index.less';
 
   /* stylelint-enable no-invalid-position-at-import-rule */
 

+ 105 - 0
package-lock.json

@@ -10,6 +10,7 @@
       "license": "Apache-2.0",
       "dependencies": {
         "@ant-design/icons": "5.0.1",
+        "@cloudera/cuix-core": "file:desktop/core/src/desktop/static/desktop/ext/cuix/cloudera-cuix-core-1.0.6.tgz",
         "@gethue/sql-formatter": "4.0.3",
         "@selectize/selectize": "0.14.0",
         "antd": "4.24.5",
@@ -18,6 +19,7 @@
         "classnames": "2.3.2",
         "clipboard": "1.7.1",
         "core-js": "3.19.1",
+        "cuix": "file:desktop/core/src/desktop/static/desktop/ext/cuix/cuix-13.0.4.tgz",
         "d3v3": "1.0.3",
         "dropzone": "5.5.1",
         "file-saver": "2.0.5",
@@ -2058,6 +2060,23 @@
       "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
       "dev": true
     },
+    "node_modules/@cloudera/cuix-core": {
+      "version": "1.0.6",
+      "resolved": "file:desktop/core/src/desktop/static/desktop/ext/cuix/cloudera-cuix-core-1.0.6.tgz",
+      "integrity": "sha512-E3/vHlm7TsYmsXf439TK772ee94UpKz273Q1rjYy7lbOTA7OX+Jgdu6KdFs2+Wz0Fr/t8TvHaJ56Etvuol6p7A==",
+      "peerDependencies": {
+        "preact": "*",
+        "react": "*"
+      },
+      "peerDependenciesMeta": {
+        "preact": {
+          "optional": true
+        },
+        "react": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/@ctrl/tinycolor": {
       "version": "3.4.1",
       "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.4.1.tgz",
@@ -2122,6 +2141,11 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/@fullstory/browser": {
+      "version": "1.6.2",
+      "resolved": "https://registry.npmjs.org/@fullstory/browser/-/browser-1.6.2.tgz",
+      "integrity": "sha512-r3kPrTKijI/LkdSW7t1GlXPn4jPuZL4AFGDJBWMj4E4HLeF2lCvZtSOZ1YTwZtJFciLp/+KOIW3qBDkqqXC2qw=="
+    },
     "node_modules/@gethue/sql-formatter": {
       "version": "4.0.3",
       "resolved": "https://registry.npmjs.org/@gethue/sql-formatter/-/sql-formatter-4.0.3.tgz",
@@ -6562,6 +6586,21 @@
       "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.17.tgz",
       "integrity": "sha512-u1wmTI1jJGzCJzWndZo8mk4wnPTZd1eOIYTYvuEyOQGfmDl3TrabCCfKnOC86FZwW/9djqTl933UF/cS425i9A=="
     },
+    "node_modules/cuix": {
+      "version": "13.0.4",
+      "resolved": "file:desktop/core/src/desktop/static/desktop/ext/cuix/cuix-13.0.4.tgz",
+      "integrity": "sha512-q2cJTt0DjDl0TEjxKqY0BmiK6fEwWNMcJ7J8CFa6kYw6DQKCItSuZHr7Mxivb05NAKOLkr/OE77f1GTC8eCTDg==",
+      "dependencies": {
+        "@fullstory/browser": "1.6.2",
+        "javascript-autocomplete": "1.0.5",
+        "js-cookie": "2.2.1",
+        "popper.js": "1.16.0",
+        "preact": "10.11.0"
+      },
+      "engines": {
+        "node": ">=5.5"
+      }
+    },
     "node_modules/d3v3": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/d3v3/-/d3v3-1.0.3.tgz",
@@ -9769,6 +9808,11 @@
         "node": ">=8"
       }
     },
+    "node_modules/javascript-autocomplete": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/javascript-autocomplete/-/javascript-autocomplete-1.0.5.tgz",
+      "integrity": "sha512-AwR8g10tPIjhrxc5n9mNHCvtspQ0bBZCNia4tXu2gk6HrhD3auI2hQLzgm79yKMihLRkmfXZ8kEXTGcfOmx6OQ=="
+    },
     "node_modules/jest": {
       "version": "27.3.1",
       "resolved": "https://registry.npmjs.org/jest/-/jest-27.3.1.tgz",
@@ -11498,6 +11542,11 @@
       "resolved": "https://registry.npmjs.org/jquery.cookie/-/jquery.cookie-1.4.1.tgz",
       "integrity": "sha1-1j3OIJ6raR/mMxbbCMqeR+D5OFs="
     },
+    "node_modules/js-cookie": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz",
+      "integrity": "sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ=="
+    },
     "node_modules/js-tokens": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -13380,6 +13429,12 @@
       "resolved": "https://registry.npmjs.org/plotly.js-dist/-/plotly.js-dist-1.45.3.tgz",
       "integrity": "sha512-suJi344/mTIQn3OKVjDtnDwiv9dhnluGqaH+poqgdJABp21rjDMa07LUQV5nZJ68d6+ZYTjkpC/DPfkiP+8ezA=="
     },
+    "node_modules/popper.js": {
+      "version": "1.16.0",
+      "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.0.tgz",
+      "integrity": "sha512-+G+EkOPoE5S/zChTpmBSSDYmhXJ5PsW8eMhH8cP/CQHMFPBG/kC9Y5IIw6qNYgdJ+/COf0ddY2li28iHaZRSjw==",
+      "deprecated": "You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1"
+    },
     "node_modules/postcss": {
       "version": "8.4.6",
       "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.6.tgz",
@@ -13558,6 +13613,15 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/preact": {
+      "version": "10.11.0",
+      "resolved": "https://registry.npmjs.org/preact/-/preact-10.11.0.tgz",
+      "integrity": "sha512-Fk6+vB2kb6mSJfDgODq0YDhMfl0HNtK5+Uc9QqECO4nlyPAQwCI+BKyWO//idA7ikV7o+0Fm6LQmNuQi1wXI1w==",
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/preact"
+      }
+    },
     "node_modules/prelude-ls": {
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@@ -18936,6 +19000,11 @@
       "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
       "dev": true
     },
+    "@cloudera/cuix-core": {
+      "version": "file:desktop/core/src/desktop/static/desktop/ext/cuix/cloudera-cuix-core-1.0.6.tgz",
+      "integrity": "sha512-E3/vHlm7TsYmsXf439TK772ee94UpKz273Q1rjYy7lbOTA7OX+Jgdu6KdFs2+Wz0Fr/t8TvHaJ56Etvuol6p7A==",
+      "requires": {}
+    },
     "@ctrl/tinycolor": {
       "version": "3.4.1",
       "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.4.1.tgz",
@@ -18981,6 +19050,11 @@
         }
       }
     },
+    "@fullstory/browser": {
+      "version": "1.6.2",
+      "resolved": "https://registry.npmjs.org/@fullstory/browser/-/browser-1.6.2.tgz",
+      "integrity": "sha512-r3kPrTKijI/LkdSW7t1GlXPn4jPuZL4AFGDJBWMj4E4HLeF2lCvZtSOZ1YTwZtJFciLp/+KOIW3qBDkqqXC2qw=="
+    },
     "@gethue/sql-formatter": {
       "version": "4.0.3",
       "resolved": "https://registry.npmjs.org/@gethue/sql-formatter/-/sql-formatter-4.0.3.tgz",
@@ -22639,6 +22713,17 @@
       "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.17.tgz",
       "integrity": "sha512-u1wmTI1jJGzCJzWndZo8mk4wnPTZd1eOIYTYvuEyOQGfmDl3TrabCCfKnOC86FZwW/9djqTl933UF/cS425i9A=="
     },
+    "cuix": {
+      "version": "file:desktop/core/src/desktop/static/desktop/ext/cuix/cuix-13.0.4.tgz",
+      "integrity": "sha512-q2cJTt0DjDl0TEjxKqY0BmiK6fEwWNMcJ7J8CFa6kYw6DQKCItSuZHr7Mxivb05NAKOLkr/OE77f1GTC8eCTDg==",
+      "requires": {
+        "@fullstory/browser": "1.6.2",
+        "javascript-autocomplete": "1.0.5",
+        "js-cookie": "2.2.1",
+        "popper.js": "1.16.0",
+        "preact": "10.11.0"
+      }
+    },
     "d3v3": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/d3v3/-/d3v3-1.0.3.tgz",
@@ -24996,6 +25081,11 @@
         "istanbul-lib-report": "^3.0.0"
       }
     },
+    "javascript-autocomplete": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/javascript-autocomplete/-/javascript-autocomplete-1.0.5.tgz",
+      "integrity": "sha512-AwR8g10tPIjhrxc5n9mNHCvtspQ0bBZCNia4tXu2gk6HrhD3auI2hQLzgm79yKMihLRkmfXZ8kEXTGcfOmx6OQ=="
+    },
     "jest": {
       "version": "27.3.1",
       "resolved": "https://registry.npmjs.org/jest/-/jest-27.3.1.tgz",
@@ -26299,6 +26389,11 @@
       "resolved": "https://registry.npmjs.org/jquery.cookie/-/jquery.cookie-1.4.1.tgz",
       "integrity": "sha1-1j3OIJ6raR/mMxbbCMqeR+D5OFs="
     },
+    "js-cookie": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz",
+      "integrity": "sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ=="
+    },
     "js-tokens": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -27791,6 +27886,11 @@
       "resolved": "https://registry.npmjs.org/plotly.js-dist/-/plotly.js-dist-1.45.3.tgz",
       "integrity": "sha512-suJi344/mTIQn3OKVjDtnDwiv9dhnluGqaH+poqgdJABp21rjDMa07LUQV5nZJ68d6+ZYTjkpC/DPfkiP+8ezA=="
     },
+    "popper.js": {
+      "version": "1.16.0",
+      "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.0.tgz",
+      "integrity": "sha512-+G+EkOPoE5S/zChTpmBSSDYmhXJ5PsW8eMhH8cP/CQHMFPBG/kC9Y5IIw6qNYgdJ+/COf0ddY2li28iHaZRSjw=="
+    },
     "postcss": {
       "version": "8.4.6",
       "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.6.tgz",
@@ -27909,6 +28009,11 @@
       "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
       "dev": true
     },
+    "preact": {
+      "version": "10.11.0",
+      "resolved": "https://registry.npmjs.org/preact/-/preact-10.11.0.tgz",
+      "integrity": "sha512-Fk6+vB2kb6mSJfDgODq0YDhMfl0HNtK5+Uc9QqECO4nlyPAQwCI+BKyWO//idA7ikV7o+0Fm6LQmNuQi1wXI1w=="
+    },
     "prelude-ls": {
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",

+ 2 - 0
package.json

@@ -31,6 +31,7 @@
   },
   "dependencies": {
     "@ant-design/icons": "5.0.1",
+    "@cloudera/cuix-core": "file:desktop/core/src/desktop/static/desktop/ext/cuix/cloudera-cuix-core-1.0.6.tgz",
     "@gethue/sql-formatter": "4.0.3",
     "@selectize/selectize": "0.14.0",
     "antd": "4.24.5",
@@ -39,6 +40,7 @@
     "classnames": "2.3.2",
     "clipboard": "1.7.1",
     "core-js": "3.19.1",
+    "cuix": "file:desktop/core/src/desktop/static/desktop/ext/cuix/cuix-13.0.4.tgz",
     "d3v3": "1.0.3",
     "dropzone": "5.5.1",
     "file-saver": "2.0.5",

+ 5 - 2
tools/ci/check_for_js_licenses.js

@@ -1,4 +1,4 @@
-// Licensed to Cloudera, Inc. under one
+ // Licensed to Cloudera, Inc. under one
 // or more contributor license agreements.  See the NOTICE file
 // distributed with this work for additional information
 // regarding copyright ownership.  Cloudera, Inc. licenses this file
@@ -45,7 +45,10 @@ checker.init(
           lowerLicenses.indexOf('python-2.0') === -1 &&
           // lz-string is marked as WTFPL license on NPM but the valid license is MIT from the github repo
           // https://github.com/pieroxy/lz-string/issues/147
-          !packageName.startsWith('lz-string@')
+          !packageName.startsWith('lz-string@') &&
+          // cuix and cuix-core are from Cloudera, no need to check licence here.
+          !packageName.startsWith('@cloudera/cuix-core@') && 
+          !packageName.startsWith('cuix@')
         ) {
           console.warn(`Found invalid license in "${packageName}", license: "${licenses}".`);
           process.exitCode = 1;

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.