├── .eslintrc.json
├── .github
├── dependabot.yml
└── workflows
│ ├── build-docs.yml
│ ├── build-v1.yml
│ ├── dependabot-reviewer.yml
│ ├── npm-publish-v1.yml
│ ├── npm-publish.yml
│ └── stale.yml
├── .gitignore
├── .npmignore
├── .nvmrc
├── .storybook
├── main.js
└── preview.js
├── LICENSE
├── README.md
├── VERSION
├── jest.config.js
├── package.json
├── scripts
└── icons
│ ├── generator.js
│ └── scss.hbs
├── src
├── assets
│ ├── favicon
│ │ ├── favicon-16x16.png
│ │ ├── favicon-32x32.png
│ │ └── favicon.ico
│ ├── fonts
│ │ ├── argo-icon.eot
│ │ ├── argo-icon.svg
│ │ ├── argo-icon.ttf
│ │ └── argo-icon.woff
│ └── images
│ │ ├── Download-200.png
│ │ ├── Message-200.png
│ │ ├── User-Manual-200.png
│ │ └── logo.png
├── components
│ ├── autocomplete
│ │ ├── autocomplete-field.tsx
│ │ ├── autocomplete.scss
│ │ └── autocomplete.tsx
│ ├── checkbox.spec.tsx
│ ├── checkbox.tsx
│ ├── data-loader.spec.tsx
│ ├── data-loader.tsx
│ ├── dropdown-menu.tsx
│ ├── dropdown
│ │ ├── dropdown.scss
│ │ └── dropdown.tsx
│ ├── duration.tsx
│ ├── error-notification.tsx
│ ├── form-field
│ │ ├── form-field.scss
│ │ ├── form-field.tsx
│ │ └── index.ts
│ ├── help-icon
│ │ └── help-icon.tsx
│ ├── index.ts
│ ├── layout
│ │ ├── layout.scss
│ │ └── layout.tsx
│ ├── logs-viewer
│ │ ├── logs-viewer.scss
│ │ └── logs-viewer.tsx
│ ├── mockup-list
│ │ ├── mockup-list.scss
│ │ └── mockup-list.tsx
│ ├── nav-bar
│ │ ├── nav-bar.scss
│ │ └── nav-bar.tsx
│ ├── navigation.ts
│ ├── notifications
│ │ ├── notification-manager.ts
│ │ └── notifications.tsx
│ ├── page
│ │ ├── page.scss
│ │ └── page.tsx
│ ├── popup
│ │ ├── popup-manager.spec.tsx
│ │ ├── popup-manager.tsx
│ │ ├── popup.scss
│ │ └── popup.tsx
│ ├── select
│ │ ├── select.scss
│ │ └── select.tsx
│ ├── slide-contents
│ │ ├── slide-contents.scss
│ │ └── slide-contents.tsx
│ ├── sliding-panel
│ │ ├── sliding-panel.scss
│ │ └── sliding-panel.tsx
│ ├── tabs
│ │ ├── tabs.scss
│ │ └── tabs.tsx
│ ├── ticker.tsx
│ ├── tooltip
│ │ └── tooltip.tsx
│ ├── top-bar
│ │ ├── top-bar.scss
│ │ └── top-bar.tsx
│ └── utils.ts
├── context.tsx
├── index.ts
├── models
│ ├── index.ts
│ └── kubernetes.ts
├── setupTests.ts
└── styles
│ ├── argo-icon.scss
│ ├── config.scss
│ ├── elements
│ ├── buttons.scss
│ ├── containers.scss
│ ├── form-controls.scss
│ ├── table-list.scss
│ └── utils.scss
│ ├── icons
│ ├── add-attribute.svg
│ ├── addstorage.svg
│ ├── admin-access.svg
│ ├── application.svg
│ ├── applications.svg
│ ├── approval.svg
│ ├── appstore.svg
│ ├── artifact.svg
│ ├── aws-logo.svg
│ ├── axcluster.svg
│ ├── axlogo.svg
│ ├── back.svg
│ ├── bitbucket.svg
│ ├── branch.svg
│ ├── build.svg
│ ├── calendar-menu.svg
│ ├── calendar.svg
│ ├── cancel-2-1.svg
│ ├── cancel-2.svg
│ ├── cancel.svg
│ ├── cashboard.svg
│ ├── catalog.svg
│ ├── checked.svg
│ ├── checkmark.svg
│ ├── checkout.svg
│ ├── clock.svg
│ ├── close.svg
│ ├── code-file.svg
│ ├── collapse-arrow.svg
│ ├── commit.svg
│ ├── concurrent-usage.svg
│ ├── configs.svg
│ ├── configurations.svg
│ ├── connect.svg
│ ├── console.svg
│ ├── dashboards.svg
│ ├── delete-attribute.svg
│ ├── delete.svg
│ ├── deploy.svg
│ ├── deployment.svg
│ ├── docker.svg
│ ├── docs.svg
│ ├── download.svg
│ ├── edit-property.svg
│ ├── edit-user.svg
│ ├── edit.svg
│ ├── expand-arrow.svg
│ ├── expand.svg
│ ├── external-link.svg
│ ├── fav-selected.svg
│ ├── fav.svg
│ ├── filter.svg
│ ├── fixture.svg
│ ├── fixturecat.svg
│ ├── fixturenew.svg
│ ├── folder-lock.svg
│ ├── gcp-logo.svg
│ ├── git.svg
│ ├── github.svg
│ ├── helm.svg
│ ├── help.svg
│ ├── hosts.svg
│ ├── import.svg
│ ├── info.svg
│ ├── integrations.svg
│ ├── intlogo.svg
│ ├── jira.svg
│ ├── job.svg
│ ├── label.svg
│ ├── launcher.svg
│ ├── lock.svg
│ ├── logout.svg
│ ├── manage.svg
│ ├── menu-2.svg
│ ├── metrics.svg
│ ├── more.svg
│ ├── new.svg
│ ├── nonmarkingreturn.svg
│ ├── notification.svg
│ ├── oci.svg
│ ├── pencil.svg
│ ├── play-2.svg
│ ├── play.svg
│ ├── pod.svg
│ ├── policies.svg
│ ├── policy.svg
│ ├── profile.svg
│ ├── push.svg
│ ├── report-card.svg
│ ├── resubmit-failed.svg
│ ├── retry.svg
│ ├── right-navigation-toolbar.svg
│ ├── safe.svg
│ ├── sales-channels.svg
│ ├── sample.svg
│ ├── scale.svg
│ ├── search.svg
│ ├── settings.svg
│ ├── slack-02.svg
│ ├── stop-property.svg
│ ├── stop.svg
│ ├── storageclass.svg
│ ├── storageprovider.svg
│ ├── tag.svg
│ ├── template.svg
│ ├── terminate.svg
│ ├── test.svg
│ ├── timeline.svg
│ ├── tools.svg
│ ├── user-groups.svg
│ ├── user-profile.svg
│ ├── user.svg
│ ├── users.svg
│ ├── volume.svg
│ ├── warning.svg
│ ├── workflow.svg
│ └── yaml.svg
│ ├── main.scss
│ └── theme.scss
├── stories
├── data-loader.stories.tsx
├── dropdown.stories.tsx
├── forms.stories.tsx
├── logs-viewer.stories.tsx
├── notifications.stories.tsx
├── page.stories.tsx
├── popup.stories.tsx
├── select.stories.tsx
├── table.stories.tsx
├── tabs.stories.tsx
└── utils.tsx
├── tsconfig.json
├── v2
├── .babelrc
├── .gitignore
├── .prettierrc
├── .storybook
│ ├── Argo.js
│ ├── images
│ │ ├── argo-favicon.png
│ │ └── argo-icon-color-square.png
│ ├── main.js
│ ├── manager.js
│ └── preview.js
├── README.md
├── components
│ ├── action-button
│ │ ├── action-button.scss
│ │ ├── action-button.stories.tsx
│ │ └── action-button.tsx
│ ├── alert
│ │ ├── alert.scss
│ │ ├── alert.stories.tsx
│ │ └── alert.tsx
│ ├── autocomplete
│ │ ├── autocomplete.scss
│ │ ├── autocomplete.stories.tsx
│ │ └── autocomplete.tsx
│ ├── box
│ │ ├── box.scss
│ │ ├── box.stories.tsx
│ │ └── box.tsx
│ ├── checkbox
│ │ ├── checkbox.scss
│ │ └── checkbox.tsx
│ ├── effect-div
│ │ ├── effect-div.scss
│ │ ├── effect-div.stories.tsx
│ │ └── effect-div.tsx
│ ├── filler
│ │ ├── filler.scss
│ │ └── filler.tsx
│ ├── flexy
│ │ └── flexy.tsx
│ ├── header
│ │ ├── header.scss
│ │ ├── header.stories.tsx
│ │ └── header.tsx
│ ├── index.ts
│ ├── info-item
│ │ ├── info-item-row.stories.tsx
│ │ ├── info-item.scss
│ │ ├── info-item.stories.tsx
│ │ └── info-item.tsx
│ ├── input
│ │ ├── input.scss
│ │ ├── input.stories.tsx
│ │ └── input.tsx
│ ├── menu
│ │ ├── menu.scss
│ │ ├── menu.stories.tsx
│ │ └── menu.tsx
│ ├── row
│ │ ├── row.scss
│ │ └── row.tsx
│ ├── text
│ │ ├── text.scss
│ │ ├── text.stories.tsx
│ │ └── text.tsx
│ ├── theme-div
│ │ ├── theme-div.stories.tsx
│ │ └── theme-div.tsx
│ ├── theme-toggle
│ │ └── theme-toggle.tsx
│ ├── ticker
│ │ ├── ticker.scss
│ │ ├── ticker.stories.tsx
│ │ └── ticker.tsx
│ ├── tooltip
│ │ ├── tooltip.scss
│ │ ├── tooltip.stories.tsx
│ │ └── tooltip.tsx
│ └── wait-for
│ │ ├── loading-bar.scss
│ │ ├── spinner.scss
│ │ └── wait-for.tsx
├── index.ts
├── shared
│ ├── context
│ │ └── theme.tsx
│ ├── index.ts
│ └── keypress.tsx
├── styles
│ └── colors.scss
└── utils
│ ├── index.ts
│ ├── utils.tsx
│ └── watch.ts
└── yarn.lock
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "eslint:recommended",
4 | "plugin:@typescript-eslint/recommended",
5 | "plugin:react/recommended"
6 | ],
7 | "parser": "@typescript-eslint/parser",
8 | "parserOptions": {
9 | "ecmaVersion": "latest",
10 | "sourceType": "module"
11 | },
12 | "plugins": ["@typescript-eslint", "react"],
13 | "rules": {
14 | "@typescript-eslint/no-explicit-any": "off",
15 | "@typescript-eslint/no-non-null-assertion": "off"
16 | },
17 | "overrides": [
18 | {
19 | "files": "./stories/*.tsx",
20 | "rules": {
21 | "react/display-name": "off",
22 | "@typescript-eslint/no-unused-vars": "off"
23 | }
24 | }
25 | ],
26 | "settings": {
27 | "react": {
28 | "version": "detect"
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | # prod dependencies
4 | - package-ecosystem: "npm"
5 | directory: "/"
6 | schedule:
7 | interval: "weekly"
8 | day: "saturday"
9 | # ignore all non-security updates: https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#open-pull-requests-limit
10 | open-pull-requests-limit: 0
11 | labels:
12 | - type/dependencies
13 | - javascript
14 | commit-message:
15 | prefix: chore(deps)
16 | prefix-development: chore(deps-dev)
17 |
18 | # build / CI dependencies
19 | - package-ecosystem: "github-actions"
20 | directory: "/"
21 | schedule:
22 | interval: "weekly"
23 | day: "saturday"
24 | # ignore all non-security updates: https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#open-pull-requests-limit
25 | open-pull-requests-limit: 0
26 | labels:
27 | - type/dependencies
28 | - github_actions
29 | commit-message:
30 | prefix: chore(deps)
31 |
--------------------------------------------------------------------------------
/.github/workflows/build-docs.yml:
--------------------------------------------------------------------------------
1 | name: Build Storybook Docs v2
2 |
3 | on:
4 | push:
5 |
6 | jobs:
7 | build-docs:
8 | runs-on: ubuntu-latest
9 | steps:
10 | - uses: actions/checkout@v4
11 | - uses: actions/setup-node@v4
12 | with:
13 | node-version-file: ".nvmrc"
14 | - run: yarn install
15 | - run: yarn build-v2
16 |
--------------------------------------------------------------------------------
/.github/workflows/build-v1.yml:
--------------------------------------------------------------------------------
1 | name: Build v1
2 |
3 | on:
4 | push:
5 | pull_request:
6 | branches:
7 | - "master"
8 |
9 | jobs:
10 | build:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v4
14 | - uses: actions/setup-node@v4
15 | with:
16 | node-version-file: ".nvmrc"
17 | - run: yarn install
18 | - run: yarn deduplicate
19 | - run: yarn build
20 | - run: yarn lint
21 | - run: yarn tsc -p tsconfig.json
22 | - run: yarn test --ci --runInBand
23 |
--------------------------------------------------------------------------------
/.github/workflows/dependabot-reviewer.yml:
--------------------------------------------------------------------------------
1 | # https://docs.github.com/en/code-security/dependabot/working-with-dependabot/automating-dependabot-with-github-actions
2 | name: Approve and enable auto-merge for dependabot
3 | on: pull_request
4 |
5 | permissions:
6 | pull-requests: write
7 | contents: write
8 |
9 | jobs:
10 | review:
11 | runs-on: ubuntu-latest
12 | if: ${{ github.actor == 'dependabot[bot]' }}
13 | steps:
14 | - name: Dependabot metadata
15 | id: metadata
16 | uses: dependabot/fetch-metadata@v1.6.0
17 | with:
18 | github-token: "${{ secrets.GITHUB_TOKEN }}"
19 | - name: Approve PR
20 | run: gh pr review --approve "$PR_URL"
21 | env:
22 | PR_URL: ${{github.event.pull_request.html_url}}
23 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
24 | - name: Enable auto-merge for Dependabot PRs
25 | run: gh pr merge --auto --squash "$PR_URL"
26 | env:
27 | PR_URL: ${{github.event.pull_request.html_url}}
28 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
--------------------------------------------------------------------------------
/.github/workflows/npm-publish-v1.yml:
--------------------------------------------------------------------------------
1 | name: Release NPM package v1
2 |
3 | on:
4 | push:
5 | tags:
6 | - v1.*
7 |
8 | jobs:
9 | publish:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v4
13 | - uses: actions/setup-node@v4
14 | with:
15 | node-version-file: ".nvmrc"
16 | - run: npm publish
17 | env:
18 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
19 |
--------------------------------------------------------------------------------
/.github/workflows/npm-publish.yml:
--------------------------------------------------------------------------------
1 | name: Release NPM package v2
2 |
3 | on:
4 | workflow_dispatch:
5 | inputs:
6 | tag:
7 | description: Git tag to build release from
8 | required: true
9 |
10 | jobs:
11 | publish:
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@v4
15 | with:
16 | ref: ${{ github.event.inputs.tag }}
17 | - uses: actions/setup-node@v4
18 | with:
19 | node-version-file: ".nvmrc"
20 | - run: cd v2 && npm publish
21 | env:
22 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
23 |
--------------------------------------------------------------------------------
/.github/workflows/stale.yml:
--------------------------------------------------------------------------------
1 | # https://github.com/actions/stale
2 | name: Mark stale issues and pull requests
3 |
4 | on:
5 | schedule:
6 | - cron: '0 2 * * *' # once a day at 2am
7 |
8 | permissions:
9 | contents: read
10 |
11 | jobs:
12 | stale:
13 | permissions:
14 | issues: write # for commenting on an issue and editing labels
15 | pull-requests: write # for commenting on a PR and editing labels
16 | runs-on: ubuntu-latest
17 | steps:
18 | - uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9.0.0
19 | with:
20 | repo-token: ${{ secrets.GITHUB_TOKEN }}
21 | # timing
22 | days-before-stale: 14 # 2 weeks of inactivity
23 | days-before-close: 14 # 2 more weeks of inactivity
24 | # labels to watch for, add, and remove
25 | only-labels: 'problem/more information needed' # only mark issues/PRs as stale if they have this label
26 | labels-to-remove-when-unstale: 'problem/more information needed' # remove label when unstale -- should be manually added back if information is insufficient
27 | stale-issue-label: 'problem/stale'
28 | stale-pr-label: 'problem/stale'
29 | # automated messages to issue/PR authors
30 | stale-issue-message: >
31 | This issue has been automatically marked as stale because it has not had recent activity and needs more information.
32 | It will be closed if no further activity occurs.
33 | stale-pr-message: >
34 | This PR has been automatically marked as stale because it has not had recent activity and needs further changes.
35 | It will be closed if no further activity occurs.
36 | close-issue-message: >
37 | This issue has been closed due to inactivity and lack of information.
38 | If you still encounter this issue, please add the requested information and re-open.
39 | close-pr-message:
40 | This PR has been closed due to inactivity and lack of changes.
41 | If you would like to still work on this PR, please address the review comments and re-open.
42 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | bundle
4 | .vscode
5 | .idea
6 | *.log
7 | coverage/
8 | .DS_STORE
9 | docs/
10 | v2/storybook-static/
11 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | v2
2 | node_modules
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | v20
2 |
--------------------------------------------------------------------------------
/.storybook/main.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | module.exports = {
4 | stories: ['../stories/*.stories.tsx'],
5 | addons: ['@storybook/addon-essentials'],
6 | typescript: {
7 | check: false, // typecheck separately
8 | reactDocgen: false, // substantially improves performance: https://github.com/storybookjs/storybook/issues/22164#issuecomment-1603627308
9 | },
10 | webpackFinal: async (config, {configType}) => {
11 | config.devtool = false; // perf per: https://github.com/storybookjs/storybook/issues/19736#issuecomment-1478103817
12 | config.module.rules.push({
13 | test: /\.scss$/,
14 | exclude: /node_modules/,
15 | include: path.resolve(__dirname, '../'),
16 | sideEffects: true, // get side-effect styles to load per: https://github.com/storybookjs/storybook/issues/4690#issuecomment-435909433
17 | loader: 'style-loader!raw-loader!sass-loader'
18 | });
19 | return config;
20 | },
21 | };
22 |
--------------------------------------------------------------------------------
/.storybook/preview.js:
--------------------------------------------------------------------------------
1 | // import '../src/styles/main.scss'; -- this seems to not work and also makes the Storybook freeze for multiple minutes 😕
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Argo UI Components
2 |
3 |
4 |
5 | Set of React components used by [Argo Workflows](https://github.com/argoproj/argo-workflows), [Argo CD](https://github.com/argoproj/argo-cd), and [Argo Rollouts](https://github.com/argoproj/argo-rollouts).
6 |
7 | ## Build & Run
8 |
9 | 1. Install Toolset: [NodeJS](https://nodejs.org/en/download/) and [Yarn v1](https://classic.yarnpkg.com/en/docs)
10 | 1. Install Dependencies: run `yarn install`
11 | 1. Run: `yarn start` - starts the [Storybook v6](https://storybook.js.org/docs/6.5/get-started/install) dev server
12 |
13 | ## Local Development
14 |
15 | To test your changes locally against Argo CD or another Argo project, we recommend using [`yalc`](https://github.com/wclr/yalc).
16 |
17 | First, install `yalc`:
18 |
19 | ```sh
20 | npm i -g yalc
21 | ```
22 |
23 | Next, in your local `argo-ui` directory, run
24 |
25 | ```sh
26 | yalc publish
27 | ```
28 |
29 | Finally, in your local `argo-cd/ui` directory, run
30 |
31 | ```sh
32 | yalc add argo-ui
33 | ```
34 |
35 | Your local changes to the `argo-ui` package will now be seen by your local `argo-cd`.
36 |
--------------------------------------------------------------------------------
/VERSION:
--------------------------------------------------------------------------------
1 | v2.2.1
2 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | preset: 'ts-jest',
3 | collectCoverage: true,
4 | collectCoverageFrom: [
5 | 'src/**/*.{ts,tsx}'
6 | ],
7 | "moduleNameMapper": {
8 | "\\.(css|less|scss|sass)$": "identity-obj-proxy"
9 | },
10 | snapshotSerializers: ['enzyme-to-json/serializer'],
11 | setupFilesAfterEnv: ['src/setupTests.ts'],
12 | globals: {
13 | 'ts-jest': {
14 | tsconfig: '/tsconfig.json'
15 | }
16 | }
17 | };
18 |
--------------------------------------------------------------------------------
/scripts/icons/generator.js:
--------------------------------------------------------------------------------
1 | 'use strict;';
2 |
3 | const webfontsGenerator = require('webfonts-generator');
4 | const path = require('path');
5 | const glob = require('glob');
6 | const fs = require('fs');
7 | const FONT_TYPES = ['svg', 'ttf', 'woff', 'eot'];
8 |
9 | webfontsGenerator({
10 | files: glob.sync('src/styles/icons/*.svg'),
11 | dest: 'src/assets/fonts',
12 | fontName: 'argo-icon',
13 | types: FONT_TYPES,
14 | cssTemplate: path.resolve(__dirname, './scss.hbs'),
15 | templateOptions: {
16 | baseTag: 'i',
17 | classPrefix: 'argo-icon-',
18 | baseSelector: '.argo-icon'
19 | }
20 | }, function (error) {
21 | if (error) {
22 | console.log('Fail!', error);
23 | } else {
24 | const scss = fs.readFileSync('src/assets/fonts/argo-icon.css', 'utf-8').replace(/url\(\"argo-icon/g, 'url\($argo-icon-fonts-root + \"argo-icon');
25 | fs.writeFileSync('src/styles/argo-icon.scss', scss);
26 | fs.unlinkSync('src/assets/fonts/argo-icon.css');
27 | }
28 | });
29 |
--------------------------------------------------------------------------------
/scripts/icons/scss.hbs:
--------------------------------------------------------------------------------
1 | // AUTOGENERATED. Don't modify this file. Use 'yarn utils:icons' to regenerate style after new icons added.
2 |
3 | $argo-icon-fonts-root: '/' !default;
4 |
5 | [class^='{{fontName}}'],
6 | [class*=' {{fontName}}'] {
7 | display: inline-block;
8 | vertical-align: middle;
9 | font: normal normal normal 18px/1 '{{fontName}}';
10 | font-size: inherit;
11 | -webkit-font-smoothing: antialiased;
12 | -moz-osx-font-smoothing: grayscale;
13 | }
14 |
15 | @font-face {
16 | font-family: "{{ fontName }}";
17 | src: {{{ src }}};
18 | }
19 |
20 | {{ baseTag }} {
21 | line-height: 1;
22 | }
23 |
24 | [data-icon]:before {
25 | font-family: "{{ fontName }}" !important;
26 | content: attr(data-icon);
27 | font-style: normal !important;
28 | font-weight: normal !important;
29 | font-variant: normal !important;
30 | text-transform: none !important;
31 | speak: none;
32 | line-height: 1;
33 | -webkit-font-smoothing: antialiased;
34 | -moz-osx-font-smoothing: grayscale;
35 | }
36 |
37 | [class^="{{ fontName }}-"]:before,
38 | [class*=" {{ fontName }}-"]:before {
39 | font-family: "{{ fontName }}" !important;
40 | font-style: normal !important;
41 | font-weight: normal !important;
42 | font-variant: normal !important;
43 | text-transform: none !important;
44 | speak: none;
45 | line-height: 1;
46 | -webkit-font-smoothing: antialiased;
47 | -moz-osx-font-smoothing: grayscale;
48 | }
49 |
50 | [class^="{{ fontName }}-"]:before,
51 | [class*=" {{ fontName }}-"]:before {
52 | font-family: "{{ fontName }}" !important;
53 | font-style: normal !important;
54 | font-weight: normal !important;
55 | font-variant: normal !important;
56 | text-transform: none !important;
57 | speak: none;
58 | line-height: 1;
59 | -webkit-font-smoothing: antialiased;
60 | -moz-osx-font-smoothing: grayscale;
61 | }
62 |
63 | {{# each codepoints }}
64 | .{{ ../classPrefix }}{{ @key }}:before {
65 | content: "\\{{ this }}";
66 | }
67 | {{/ each }}
68 |
--------------------------------------------------------------------------------
/src/assets/favicon/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/argoproj/argo-ui/5cf36101733ce43eed57242a12389f2a7e40bd2b/src/assets/favicon/favicon-16x16.png
--------------------------------------------------------------------------------
/src/assets/favicon/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/argoproj/argo-ui/5cf36101733ce43eed57242a12389f2a7e40bd2b/src/assets/favicon/favicon-32x32.png
--------------------------------------------------------------------------------
/src/assets/favicon/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/argoproj/argo-ui/5cf36101733ce43eed57242a12389f2a7e40bd2b/src/assets/favicon/favicon.ico
--------------------------------------------------------------------------------
/src/assets/fonts/argo-icon.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/argoproj/argo-ui/5cf36101733ce43eed57242a12389f2a7e40bd2b/src/assets/fonts/argo-icon.eot
--------------------------------------------------------------------------------
/src/assets/fonts/argo-icon.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/argoproj/argo-ui/5cf36101733ce43eed57242a12389f2a7e40bd2b/src/assets/fonts/argo-icon.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/argo-icon.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/argoproj/argo-ui/5cf36101733ce43eed57242a12389f2a7e40bd2b/src/assets/fonts/argo-icon.woff
--------------------------------------------------------------------------------
/src/assets/images/Download-200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/argoproj/argo-ui/5cf36101733ce43eed57242a12389f2a7e40bd2b/src/assets/images/Download-200.png
--------------------------------------------------------------------------------
/src/assets/images/Message-200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/argoproj/argo-ui/5cf36101733ce43eed57242a12389f2a7e40bd2b/src/assets/images/Message-200.png
--------------------------------------------------------------------------------
/src/assets/images/User-Manual-200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/argoproj/argo-ui/5cf36101733ce43eed57242a12389f2a7e40bd2b/src/assets/images/User-Manual-200.png
--------------------------------------------------------------------------------
/src/assets/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/argoproj/argo-ui/5cf36101733ce43eed57242a12389f2a7e40bd2b/src/assets/images/logo.png
--------------------------------------------------------------------------------
/src/components/autocomplete/autocomplete-field.tsx:
--------------------------------------------------------------------------------
1 | import {default as classNames} from 'classnames';
2 | import * as React from 'react';
3 | import * as ReactForm from 'react-form';
4 |
5 | import {Autocomplete, AutocompleteOption, AutocompleteProps} from './autocomplete';
6 |
7 | export const AutocompleteField = ReactForm.FormField((props: AutocompleteProps & {fieldApi: ReactForm.FieldApi; className?: string}) => {
8 | const {fieldApi: {getValue, setValue, setTouched}, ...rest} = props;
9 | const value = getValue();
10 |
11 | const [forceHasValue, setForceHasValue] = React.useState(false);
12 |
13 | return (
14 | {
17 | setValue(item.value);
18 | }}
19 | inputProps={{
20 | className: props.className,
21 | style: {borderBottom: 'none', position: 'unset'},
22 | }}
23 | value={value}
24 | renderInput={(inputProps) => (
25 | {
29 | if (inputProps.onFocus) {
30 | inputProps.onFocus(e);
31 | }
32 | setForceHasValue(true);
33 | }}
34 | onBlur={(e) => {
35 | if (inputProps.onBlur) {
36 | inputProps.onBlur(e);
37 | }
38 | setForceHasValue(false);
39 | setTouched(true);
40 | }}
41 | />
42 | )}
43 | onChange={(val) => setValue(val.target.value)}
44 | {...rest}
45 | />
46 | );
47 | }) as React.ComponentType;
48 |
--------------------------------------------------------------------------------
/src/components/autocomplete/autocomplete.scss:
--------------------------------------------------------------------------------
1 | .autocomplete {
2 | &__items__item {
3 | &:last-child {
4 | border-bottom: none;
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/components/checkbox.spec.tsx:
--------------------------------------------------------------------------------
1 | import {shallow} from 'enzyme';
2 | import * as React from 'react';
3 | import {Checkbox} from './checkbox';
4 |
5 | describe('Checkbox', () => {
6 | it('should invoke onChange when clicked', () => {
7 | const onChange = jest.fn();
8 |
9 | const checkbox = shallow();
10 |
11 | checkbox.find('input').simulate('change');
12 |
13 | expect(onChange).toHaveBeenCalledWith(false);
14 | });
15 |
16 | it.each([true, false])('should render the checkbox with the correct value (%s)', (checked) => {
17 | const checkbox = shallow();
18 | expect(checkbox.find('input').filterWhere((item) => item.prop('checked') === checked).length).toBe(1);
19 | });
20 |
21 | it('should set the id of the resulting input', () => {
22 | const checkbox = shallow();
23 |
24 | expect(checkbox.find('input').is('#foo')).toBe(true);
25 | checkbox.setProps({id: 'bar'});
26 | expect(checkbox.find('input').is('#bar')).toBe(true);
27 | });
28 |
29 | it('should set disabled of the resulting input', () => {
30 | const checkbox = shallow();
31 |
32 | expect(checkbox.find('input').filterWhere((item) => item.prop('disabled') === true).length).toBe(1);
33 | checkbox.setProps({disabled: false});
34 | expect(checkbox.find('input').filterWhere((item) => item.prop('disabled') === false).length).toBe(1);
35 | });
36 | });
37 |
--------------------------------------------------------------------------------
/src/components/checkbox.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export const Checkbox = (props: {
4 | disabled?: boolean,
5 | checked?: boolean,
6 | onChange?: (val: boolean) => any,
7 | id?: string,
8 | }) => (
9 |
10 | props.onChange && props.onChange(!props.checked)}/>
11 |
12 |
13 | );
14 |
--------------------------------------------------------------------------------
/src/components/dropdown-menu.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { DropDown } from './dropdown/dropdown';
3 |
4 | export interface MenuItem {
5 | title: string | React.ReactElement;
6 | iconClassName?: string;
7 | action: () => any;
8 | }
9 |
10 | export interface DropDownMenuProps {
11 | items: MenuItem[];
12 | anchor: React.ComponentType;
13 | qeId?: string;
14 | }
15 |
16 | export class DropDownMenu extends React.PureComponent {
17 |
18 | private dropdown: DropDown;
19 |
20 | public render() {
21 | return (
22 | this.dropdown = dropdown} qeId={this.props.qeId}>
23 |
24 | {this.props.items.map((item, i) => - this.onItemClick(item, event)} key={i}>
26 | {item.iconClassName && } {item.title}
27 |
)}
28 |
29 |
30 | );
31 | }
32 |
33 | private onItemClick(item: MenuItem, event: any) {
34 | item.action();
35 | event.stopPropagation();
36 | if (this.dropdown) {
37 | this.dropdown.close();
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/components/dropdown/dropdown.scss:
--------------------------------------------------------------------------------
1 | @import '../../styles/config';
2 |
3 | .argo-dropdown {
4 | display: inline-block;
5 |
6 | &__anchor {
7 | cursor: pointer;
8 | }
9 |
10 | &__content {
11 | position: fixed;
12 | z-index: $notifications-z-index;
13 | padding: 1rem;
14 | background-color: $white-color;
15 | box-shadow: 0 0 4px rgba(#000, .2);
16 | transition: opacity .2s, transform .2s, visibility .2s;
17 |
18 | &:not(.opened) {
19 | transform: translateY(-30%);
20 | opacity: 0;
21 | visibility: hidden;
22 | transition: opacity .2s, transform .2s .2s, visibility .2s;
23 | }
24 |
25 | &.is-menu {
26 | overflow: auto;
27 | max-height: 360px;
28 | padding: 0;
29 | border: 0;
30 |
31 | ul {
32 | margin: 0;
33 | list-style-type: none;
34 | white-space: nowrap;
35 | text-align: left;
36 | cursor: pointer;
37 | min-width: 150px;
38 |
39 | li {
40 | padding: 0.5em 1em;
41 | font-size: 14px;
42 | border-bottom: 1px solid $argo-color-gray-2;
43 | color: $argo-color-gray-6;
44 | cursor: pointer;
45 |
46 | i {
47 | margin-right: 2px;
48 | }
49 |
50 | &:hover {
51 | background-color: $argo-color-gray-1;
52 | }
53 |
54 | &:last-child {
55 | border-bottom: none;
56 | }
57 | }
58 | }
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/components/duration.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { formatDuration } from '../../v2';
4 |
5 | /**
6 | * Output a string duration from a number of seconds
7 | *
8 | * @param {number} props.durationS - The number of seconds.
9 | */
10 | export function Duration(props: {durationS: number}) {
11 | return {formatDuration(props.durationS, 2)};
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/error-notification.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export const ErrorNotification = (props: { title?: string; e: any }) => {
4 | let message;
5 | if (props.e.response && props.e.response.text) {
6 | try {
7 | const apiError = JSON.parse(props.e.response.text);
8 | if (apiError.error) {
9 | message = apiError.error;
10 | }
11 | } catch {
12 | // do nothing
13 | }
14 | }
15 | if (!message) {
16 | if (props.e.message) {
17 | message = props.e.message;
18 | }
19 | }
20 | if (!message) {
21 | message = 'Internal error';
22 | }
23 | if (props.title) {
24 | message = `${props.title}: ${message}`;
25 | }
26 | return ({message});
27 | };
28 |
--------------------------------------------------------------------------------
/src/components/form-field/form-field.scss:
--------------------------------------------------------------------------------
1 | .form-field__select {
2 | border-bottom: none !important;
3 | }
4 |
5 | .form-field__select:not(.argo-has-value) {
6 | &::before {
7 | content: '';
8 | display: inline-block;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/components/form-field/index.ts:
--------------------------------------------------------------------------------
1 | export * from './form-field';
2 |
--------------------------------------------------------------------------------
/src/components/help-icon/help-icon.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import {Tooltip} from '../tooltip/tooltip';
3 |
4 | export const HelpIcon = ({title}: {title: React.ReactChild | React.ReactChild[]}) => (
5 |
6 |
7 | {' '}
8 |
9 |
10 |
11 | );
12 |
--------------------------------------------------------------------------------
/src/components/index.ts:
--------------------------------------------------------------------------------
1 | require('../styles/main.scss');
2 |
3 | export { Utils } from './utils';
4 | export { Layout } from './layout/layout';
5 | export { Page, PageContext, type PageContextProps } from './page/page';
6 | export { MockupList } from './mockup-list/mockup-list';
7 | export { DropDown } from './dropdown/dropdown';
8 | export { DropDownMenu, type DropDownMenuProps, type MenuItem } from './dropdown-menu';
9 | export { Checkbox } from './checkbox';
10 | export { type TopBarProps, type Toolbar, type TopBarFilter } from './top-bar/top-bar';
11 | export { type Tab, Tabs } from './tabs/tabs';
12 | export { Duration } from './duration';
13 | export { SlidingPanel } from './sliding-panel/sliding-panel';
14 | export { LogsViewer } from './logs-viewer/logs-viewer';
15 | export * from './notifications/notifications';
16 | export * from './notifications/notification-manager';
17 | export * from './popup/popup';
18 | export * from './popup/popup-manager';
19 | export { Select, type SelectOption, type SelectProps } from './select/select';
20 | export { HelpIcon } from './help-icon/help-icon';
21 | export { Tooltip } from './tooltip/tooltip';
22 | export * from './ticker';
23 | export * from './data-loader';
24 | export * from './error-notification';
25 | export * from './navigation';
26 | export * from './form-field';
27 | export * from './slide-contents/slide-contents';
28 | export * from './autocomplete/autocomplete';
29 | export * from './autocomplete/autocomplete-field';
30 |
--------------------------------------------------------------------------------
/src/components/layout/layout.scss:
--------------------------------------------------------------------------------
1 | @import '../../styles/config';
2 | @import '../../styles/theme';
3 |
4 | .layout {
5 | overflow: hidden;
6 | @include themify($themes) {
7 | background-color: themed('background-1');
8 | }
9 | &__loader {
10 | @include themify($themes) {
11 | background-color: themed('layout-loader-bg');
12 | }
13 | position: fixed;
14 | left: 0;
15 | top: 0;
16 | right: 0;
17 | bottom: 0;
18 | z-index: 999999;
19 |
20 | .loader-inner {
21 | left: 50%;
22 | position: absolute;
23 | bottom: 50%;
24 | transform: translateX(-50%) translateY(-50%);
25 | & > div {
26 | background: #F07A51;
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/components/layout/layout.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import {NavBar, NavBarStyle} from '../nav-bar/nav-bar';
3 |
4 | require('./layout.scss');
5 |
6 | export interface LayoutProps {
7 | navItems: Array<{ path: string; iconClassName: string; title: string; }>;
8 | version?: () => React.ReactElement;
9 | navBarStyle?: NavBarStyle;
10 | theme?: string;
11 | children?: React.ReactNode;
12 | }
13 |
14 | export const Layout = (props: LayoutProps) => (
15 |
16 |
17 |
18 | {props.children}
19 |
20 |
21 | );
22 |
--------------------------------------------------------------------------------
/src/components/logs-viewer/logs-viewer.scss:
--------------------------------------------------------------------------------
1 | @import '../../styles/config';
2 | @import 'node_modules/xterm/css/xterm';
3 |
4 | .logs-viewer {
5 | font: normal 13px/1.2 'Courier', sans-serif;
6 | line-height: 20px;
7 | height: 100%;
8 | overflow: hidden;
9 |
10 | &__container {
11 | height: 100%;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/components/mockup-list/mockup-list.scss:
--------------------------------------------------------------------------------
1 | @import '../../styles/config';
2 |
3 | @keyframes wavemove {
4 | from {left: -100%;}
5 | to {left: 100%;}
6 | }
7 | $wave-time: 2s;
8 |
9 | .mockup-list {
10 |
11 | &__item {
12 | position: relative;
13 | overflow: hidden;
14 | font-size: .875em;
15 | color: $argo-color-gray-6;
16 | background-color: #fff;
17 | border-radius: 4px;
18 | box-shadow: 1px 2px 3px rgba($argo-color-gray-9, .1);
19 | opacity: 0.3;
20 | }
21 |
22 | &__wave-loader {
23 | position: relative;
24 | overflow: hidden;
25 | background: linear-gradient(to right, #fff, $argo-color-teal-3, #fff);
26 | width: 50%;
27 | height: 100%;
28 | animation: wavemove $wave-time infinite;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/components/mockup-list/mockup-list.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export interface MockupListProps { height?: number; marginTop?: number; }
4 |
5 | require('./mockup-list.scss');
6 |
7 | export class MockupList extends React.Component {
8 |
9 | constructor(props: MockupListProps) {
10 | super(props);
11 | this.state = { count: 0 };
12 | }
13 |
14 | public componentDidMount() {
15 | this.setState({count: Math.round(window.innerHeight / ( this.itemHeight + this.itemMarginTop )) });
16 | }
17 |
18 | public render() {
19 | const items = [];
20 | for (let i = 0; i < this.state.count; i++) {
21 | items.push(i);
22 | }
23 | return (
24 |
25 | { items.map((i: number) => (
26 |
29 | ))}
30 |
);
31 | }
32 |
33 | private get itemHeight() {
34 | return this.props.height || 60;
35 | }
36 |
37 | private get itemMarginTop() {
38 | return this.props.marginTop || 20;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/components/nav-bar/nav-bar.tsx:
--------------------------------------------------------------------------------
1 | import {default as classNames} from 'classnames';
2 | import * as PropTypes from 'prop-types';
3 | import * as React from 'react';
4 |
5 | import { AppContext } from '../../context';
6 | import {Tooltip} from '../tooltip/tooltip';
7 |
8 | require('./nav-bar.scss');
9 |
10 | export interface NavBarProps {
11 | items: Array<{ path: string; iconClassName: string; title: string; }>;
12 | version?: () => React.ReactElement;
13 | style?: NavBarStyle;
14 | }
15 |
16 | export interface NavBarStyle {
17 | backgroundColor?: string;
18 | }
19 |
20 | export function isActiveRoute(locationPath: string, path: string) {
21 | return locationPath === path || locationPath.startsWith(`${path}/`);
22 | }
23 |
24 | export const NavBar: React.FunctionComponent = (props: NavBarProps, context: AppContext) => {
25 | const locationPath = context.router.route.location.pathname;
26 | const navBarStyle = {
27 | ...(props.style?.backgroundColor && {background: `linear-gradient(to bottom, ${props.style?.backgroundColor}, #999`}),
28 | };
29 | return (
30 | = 10,
32 | })} style={navBarStyle}>
33 |
34 |

35 |
{props.version && props.version()}
36 | {(props.items || []).map((item) => (
37 |
38 | context.router.history.push(item.path)}>
40 |
41 |
42 |
43 | ))}
44 |
45 |
46 | );
47 | };
48 |
49 | NavBar.contextTypes = {
50 | router: PropTypes.object,
51 | };
52 |
--------------------------------------------------------------------------------
/src/components/navigation.ts:
--------------------------------------------------------------------------------
1 | import { History } from 'history';
2 | import * as React from 'react';
3 |
4 | export interface NavigationApi {
5 | goto(path: string, query?: {[name: string]: any}, options?: { event?: React.MouseEvent, replace?: boolean }): void;
6 | }
7 |
8 | export class NavigationManager implements NavigationApi {
9 |
10 | private history: History;
11 |
12 | constructor(history: History) {
13 | this.history = history;
14 | }
15 |
16 | public goto(path: string, query: {[name: string]: any} = {}, options?: { event?: React.MouseEvent, replace?: boolean }): void {
17 | if (path.startsWith('.')) {
18 | path = this.history.location.pathname + path.slice(1);
19 | }
20 | const noPathChange = path === this.history.location.pathname;
21 | const params = noPathChange ? new URLSearchParams(this.history.location.search) : new URLSearchParams();
22 | for (const name of Object.keys(query)) {
23 | const val = query[name];
24 | params.delete(name);
25 | if (val !== undefined && val !== null) {
26 | if (val instanceof Array) {
27 | for (const item of val) {
28 | params.append(name, item);
29 | }
30 | } else {
31 | params.set(name, val);
32 | }
33 | }
34 | }
35 | const urlQuery = params.toString();
36 | if (urlQuery !== '') {
37 | path = `${path}?${urlQuery}`;
38 | }
39 | options = options || {};
40 | if (options.event && (options.event.metaKey || options.event.ctrlKey || options.event.button === 1)) {
41 | window.open(path, '_blank');
42 | } else {
43 | if (options.replace) {
44 | this.history.replace(path);
45 | } else {
46 | this.history.push(path);
47 | }
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/components/notifications/notification-manager.ts:
--------------------------------------------------------------------------------
1 | import { ReplaySubject } from 'rxjs';
2 | import { NotificationInfo } from './notifications';
3 |
4 | export interface NotificationsApi {
5 | show(notification: NotificationInfo, autoHideMs?: number): void;
6 | }
7 |
8 | export class NotificationsManager {
9 | private readonly notificationsSubject = new ReplaySubject(1);
10 |
11 | public get notifications() {
12 | return this.notificationsSubject.asObservable();
13 | }
14 |
15 | public show(notification: NotificationInfo) {
16 | this.notificationsSubject.next(notification);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/components/page/page.scss:
--------------------------------------------------------------------------------
1 | @import '../../styles/config';
2 |
3 | .page {
4 | padding-left: $nav-width;
5 | padding-top: $top-bar-height;
6 |
7 | &__content-wrapper {
8 | position: relative;
9 | display: block;
10 | min-height: calc(100vh - #{$top-bar-height});
11 | margin: 0 auto;
12 | overflow: hidden;
13 | }
14 |
15 | &__top-bar {
16 | position: fixed;
17 | top: 0;
18 | right: 0;
19 | left: $nav-width;
20 | height: $top-bar-height;
21 | z-index: $top-bar-z-index;
22 | }
23 |
24 | &--has-toolbar {
25 | padding-top: 2 * $top-bar-height;
26 |
27 | .page__top-bar {
28 | height: 2 * $top-bar-height;
29 | }
30 | .page__content-wrapper {
31 | min-height: calc(100vh - 2 * #{$top-bar-height});
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/components/popup/popup-manager.spec.tsx:
--------------------------------------------------------------------------------
1 | import { mount } from 'enzyme';
2 | import {from} from 'rxjs';
3 | import {TestScheduler} from 'rxjs/testing';
4 | import { Popup, PopupProps } from './popup';
5 | import {PopupManager} from './popup-manager';
6 |
7 | describe('PopupManager', () => {
8 | let scheduler: TestScheduler;
9 |
10 | beforeEach(() => {
11 | scheduler = new TestScheduler((a, b) => expect(a).toEqual(b));
12 | });
13 |
14 | describe('confirm', () => {
15 | it.each([
16 | ['OK', true, '[qe-id="argo-popup-ok-button"]'],
17 | ['Cancel', false, '[qe-id="argo-popup-cancel-button"]'],
18 | ])('%s', async (_, promiseResult, btnSelector) => {
19 | const fn = jest.fn();
20 | const manager = new PopupManager();
21 |
22 | from(manager.popupProps, scheduler).subscribe(fn);
23 | scheduler.flush();
24 |
25 | expect(fn).toHaveBeenCalledTimes(1);
26 | expect(fn).toHaveBeenLastCalledWith(null);
27 |
28 | const promise = manager.confirm('foo', 'bar');
29 | scheduler.flush();
30 |
31 | expect(fn).toHaveBeenCalledTimes(2);
32 |
33 | const props = fn.mock.calls[1][0]!;
34 |
35 | expect(typeof props).toBe('object');
36 |
37 | const popup = mount(Popup(props));
38 |
39 | expect(popup.find('.popup-container__header').text().trim()).toBe('foo');
40 | expect(popup.find('.popup-container__body').text()).toBe('bar');
41 |
42 | popup.find(btnSelector).simulate('click');
43 |
44 | await expect(promise).resolves.toBe(promiseResult);
45 | scheduler.flush();
46 |
47 | expect(fn).toHaveBeenCalledTimes(3);
48 | expect(fn).toHaveBeenLastCalledWith(null);
49 | });
50 | });
51 | });
52 |
--------------------------------------------------------------------------------
/src/components/popup/popup.tsx:
--------------------------------------------------------------------------------
1 | import {default as classNames} from 'classnames';
2 | import * as React from 'react';
3 |
4 | export interface BasePopupProps {
5 | icon?: { name: string; color: string; };
6 | titleColor?: string;
7 | title: string | React.ReactNode;
8 | footer?: React.ReactNode;
9 | }
10 |
11 | export type PopupPropsWithContent = BasePopupProps & { content: React.ComponentType };
12 | export type PopupPropsWithChildren = BasePopupProps & { children: React.ReactNode};
13 | export type PopupProps = PopupPropsWithContent | PopupPropsWithChildren;
14 |
15 | function isPopupWithChildren(value: PopupProps): value is PopupPropsWithChildren {
16 | return (value as any).children !== undefined;
17 | }
18 |
19 | require('./popup.scss');
20 |
21 | export const Popup = (props: PopupProps) => (
22 |
23 |
24 |
25 | {props.title}
26 |
27 |
28 | {props.icon &&
29 |
30 |
31 |
32 | }
33 |
34 | {isPopupWithChildren(props) ? props.children :
}
35 |
36 |
37 |
38 |
39 | {props.footer}
40 |
41 |
42 |
43 | );
44 |
--------------------------------------------------------------------------------
/src/components/slide-contents/slide-contents.scss:
--------------------------------------------------------------------------------
1 | .slide-contents {
2 | .slide-contents--title {
3 | cursor: pointer;
4 | }
5 | .slide-contents--contents {
6 | overflow-y: hidden;
7 | max-height: 100%;
8 |
9 | &.slide-contents--contents-hidden {
10 | max-height: 0;
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/components/slide-contents/slide-contents.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | require('./slide-contents.scss');
4 |
5 | export interface SlideContentsProps {
6 | title: string;
7 | contents: JSX.Element;
8 | className: string;
9 | }
10 |
11 | export interface SlideContentsState {
12 | hidden: boolean;
13 | }
14 |
15 | export class SlideContents extends React.Component {
16 | constructor(props: SlideContentsProps) {
17 | super(props);
18 |
19 | this.state = {
20 | hidden: true,
21 | };
22 |
23 | this.showContents = this.showContents.bind(this);
24 | this.hideContents = this.hideContents.bind(this);
25 | }
26 |
27 | public showContents() {
28 | this.setState({ hidden: false });
29 | }
30 |
31 | public hideContents() {
32 | this.setState({ hidden: true });
33 | }
34 |
35 | public render() {
36 | const { title, contents, className } = this.props;
37 | const { hidden } = this.state;
38 | let toggleSwitch: JSX.Element | undefined;
39 | let clickAction: () => void;
40 | if (hidden) {
41 | toggleSwitch = ;
42 | clickAction = this.showContents;
43 | } else {
44 | toggleSwitch = ;
45 | clickAction = this.hideContents;
46 | }
47 | return (
48 |
49 |
50 | {title}
51 | {toggleSwitch}
52 |
53 |
54 |
55 | {contents}
56 |
57 |
58 |
59 | );
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/components/ticker.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import {interval, Subscription} from 'rxjs';
3 |
4 | export class Ticker extends React.Component<{intervalMs?: number, disabled?: boolean, children?: ((time: Date) => React.ReactNode)}, {time: Date}> {
5 |
6 | private subscription: Subscription | null = null;
7 |
8 | constructor(props: {intervalMs?: number, children?: ((time: Date) => React.ReactNode)}) {
9 | super(props);
10 | this.state = { time: new Date() };
11 | this.ensureSubscribed();
12 | }
13 |
14 | public render() {
15 | return this.props.children && this.props.children(this.state.time);
16 | }
17 |
18 | public componentDidUpdate() {
19 | this.ensureSubscribed();
20 | }
21 |
22 | public componentWillUnmount() {
23 | this.ensureUnsubscribed();
24 | }
25 |
26 | private ensureSubscribed() {
27 | if (this.props.disabled) {
28 | this.ensureUnsubscribed();
29 | } else if (!this.subscription) {
30 | this.subscription = interval(this.props.intervalMs || 1000).subscribe(() => this.setState({ time: new Date() }));
31 | }
32 | }
33 |
34 | private ensureUnsubscribed() {
35 | if (this.subscription != null) {
36 | this.subscription.unsubscribe();
37 | this.subscription = null;
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/components/tooltip/tooltip.tsx:
--------------------------------------------------------------------------------
1 | import Tippy, { TippyProps } from '@tippy.js/react';
2 | import * as React from 'react';
3 | import 'tippy.js/dist/tippy.css';
4 | import 'tippy.js/themes/light.css';
5 |
6 | export const Tooltip = ( props: TippyProps ) => (
7 |
8 | );
9 |
--------------------------------------------------------------------------------
/src/components/utils.ts:
--------------------------------------------------------------------------------
1 | import { from, Observable } from 'rxjs';
2 |
3 | export function isPromise(obj: any): obj is PromiseLike {
4 | return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function';
5 | }
6 |
7 | export const Utils = {
8 | toObservable(val: T | Observable | Promise): Observable {
9 | const observable = val as Observable;
10 | if (observable && observable.subscribe && observable.forEach) {
11 | return observable as Observable;
12 | }
13 | return from([val as T]);
14 | },
15 | };
16 |
--------------------------------------------------------------------------------
/src/context.tsx:
--------------------------------------------------------------------------------
1 | import * as H from 'history';
2 | import { match } from 'react-router';
3 |
4 | import { NotificationsApi } from './components/notifications/notification-manager';
5 | import { PopupApi } from './components/popup/popup-manager';
6 |
7 | export interface AppContext {
8 | router: {
9 | history: H.History;
10 | route: {
11 | location: H.Location;
12 | match: match;
13 | };
14 | };
15 | apis: {
16 | popup: PopupApi;
17 | notifications: NotificationsApi;
18 | };
19 | history: H.History;
20 | }
21 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import * as models from './models';
2 |
3 | export * from './components';
4 | export * from './context';
5 |
6 | export {
7 | models,
8 | };
9 |
--------------------------------------------------------------------------------
/src/models/index.ts:
--------------------------------------------------------------------------------
1 | export * from './kubernetes';
2 |
--------------------------------------------------------------------------------
/src/setupTests.ts:
--------------------------------------------------------------------------------
1 | import {configure } from 'enzyme';
2 | import Adapter from 'enzyme-adapter-react-16';
3 |
4 | configure({ adapter: new Adapter() });
5 |
--------------------------------------------------------------------------------
/src/styles/elements/table-list.scss:
--------------------------------------------------------------------------------
1 | @import '../config';
2 | @import '../theme';
3 |
4 | .argo-table-list {
5 | &__head {
6 | margin: 8px 0;
7 | font-size: 12px;
8 | font-weight: 500;
9 | color: $argo-color-gray-6;
10 | text-transform: uppercase;
11 | }
12 |
13 | &__row {
14 | position: relative;
15 | margin: 8px 0;
16 | line-height: 60px;
17 | font-size: .8125em;
18 | @include themify($themes) {
19 | background: themed('background-2');
20 | color: themed('text-1');
21 | }
22 | border-radius: 4px;
23 | box-shadow: 1px 2px 3px rgba(0, 0, 0, 0.1);
24 |
25 | .columns {
26 | overflow: hidden;
27 | white-space: nowrap;
28 | text-overflow: ellipsis;
29 | }
30 |
31 | .title-text {
32 | font-weight: 500;
33 | color: $argo-color-gray-7;
34 |
35 | i {
36 | position: relative;
37 | top: -1px;
38 | margin-right: 6px;
39 | }
40 | }
41 |
42 | &.selected {
43 | background-color: $argo-color-teal-2;
44 | }
45 |
46 | .argo-table-list--clickable & {
47 | cursor: pointer;
48 |
49 | &:hover {
50 | box-shadow: 1px 2px 3px rgba($argo-color-gray-9, .1), 0 0 0 1px rgba($argo-color-teal-5, .5);
51 | }
52 | }
53 |
54 | &--lg {
55 | margin-top: 20px;
56 | margin-bottom: 20px;
57 | line-height: inherit;
58 | }
59 | }
60 |
61 | &__loader {
62 | padding-top: 7px;
63 | padding-bottom: 7px;
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/styles/elements/utils.scss:
--------------------------------------------------------------------------------
1 | @import '../config';
2 |
3 | .status-icon {
4 | &--failed {
5 | color: $argo-failed-color;
6 | }
7 |
8 | &--success {
9 | color: $argo-success-color;
10 | }
11 |
12 | &--waiting {
13 | color: $argo-waiting-color;
14 | }
15 |
16 | &--cancelled {
17 | color: $argo-cancelled-color;
18 | }
19 |
20 | &--running {
21 | color: $argo-running-color;
22 | }
23 |
24 | &--pending {
25 | color: $argo-status-warning-color;
26 | }
27 |
28 | &--init {
29 | color: $argo-init-color;
30 | }
31 |
32 | &--spin {
33 | animation-name: spin;
34 | animation-duration: 4000ms;
35 | animation-iteration-count: infinite;
36 | animation-timing-function: linear;
37 | }
38 |
39 | &--slow-spin {
40 | animation-name: spin;
41 | animation-duration: 10000ms;
42 | animation-iteration-count: infinite;
43 | animation-timing-function: linear;
44 | }
45 |
46 | @keyframes spin {
47 | from {transform:rotate(0deg);}
48 | to {transform:rotate(360deg);}
49 | }
50 | }
51 |
52 | .icon {
53 | font-size: 2em;
54 | }
55 |
56 | .muted {
57 | color: $argo-color-gray-5;
58 | }
59 |
60 | .title {
61 | color: $argo-color-gray-7;
62 | font-size: 1.1em;
63 | }
64 |
--------------------------------------------------------------------------------
/src/styles/icons/add-attribute.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/src/styles/icons/addstorage.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
15 |
--------------------------------------------------------------------------------
/src/styles/icons/admin-access.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/src/styles/icons/application.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
17 |
--------------------------------------------------------------------------------
/src/styles/icons/applications.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/src/styles/icons/approval.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/src/styles/icons/artifact.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/src/styles/icons/aws-logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/src/styles/icons/axlogo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
--------------------------------------------------------------------------------
/src/styles/icons/back.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/src/styles/icons/bitbucket.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/src/styles/icons/branch.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
--------------------------------------------------------------------------------
/src/styles/icons/build.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/src/styles/icons/calendar-menu.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/src/styles/icons/calendar.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/src/styles/icons/cancel-2-1.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/src/styles/icons/cancel-2.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/src/styles/icons/cancel.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/src/styles/icons/cashboard.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
--------------------------------------------------------------------------------
/src/styles/icons/catalog.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/src/styles/icons/checked.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/src/styles/icons/checkmark.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/src/styles/icons/checkout.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/src/styles/icons/clock.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/src/styles/icons/close.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/src/styles/icons/code-file.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/src/styles/icons/collapse-arrow.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/src/styles/icons/commit.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/src/styles/icons/concurrent-usage.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
--------------------------------------------------------------------------------
/src/styles/icons/configs.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
--------------------------------------------------------------------------------
/src/styles/icons/configurations.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
14 |
--------------------------------------------------------------------------------
/src/styles/icons/connect.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
--------------------------------------------------------------------------------
/src/styles/icons/console.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/src/styles/icons/dashboards.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
--------------------------------------------------------------------------------
/src/styles/icons/delete-attribute.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/src/styles/icons/delete.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/src/styles/icons/deploy.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/src/styles/icons/deployment.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
--------------------------------------------------------------------------------
/src/styles/icons/docker.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
--------------------------------------------------------------------------------
/src/styles/icons/docs.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
21 |
--------------------------------------------------------------------------------
/src/styles/icons/download.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/src/styles/icons/edit-property.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/src/styles/icons/edit-user.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/src/styles/icons/edit.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/src/styles/icons/expand-arrow.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/src/styles/icons/expand.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/src/styles/icons/external-link.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/src/styles/icons/fav-selected.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/src/styles/icons/fav.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/src/styles/icons/filter.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/src/styles/icons/fixture.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
24 |
--------------------------------------------------------------------------------
/src/styles/icons/fixturenew.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/styles/icons/folder-lock.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
--------------------------------------------------------------------------------
/src/styles/icons/git.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/src/styles/icons/github.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/src/styles/icons/help.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/src/styles/icons/hosts.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/src/styles/icons/import.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/src/styles/icons/info.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/src/styles/icons/integrations.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
20 |
--------------------------------------------------------------------------------
/src/styles/icons/intlogo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/src/styles/icons/jira.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
--------------------------------------------------------------------------------
/src/styles/icons/job.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
13 |
--------------------------------------------------------------------------------
/src/styles/icons/label.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/src/styles/icons/launcher.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/src/styles/icons/lock.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/src/styles/icons/logout.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/src/styles/icons/manage.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/src/styles/icons/menu-2.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/src/styles/icons/metrics.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
27 |
--------------------------------------------------------------------------------
/src/styles/icons/more.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/src/styles/icons/new.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/src/styles/icons/nonmarkingreturn.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
--------------------------------------------------------------------------------
/src/styles/icons/notification.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/src/styles/icons/oci.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
23 |
--------------------------------------------------------------------------------
/src/styles/icons/pencil.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/src/styles/icons/play-2.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/src/styles/icons/play.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/src/styles/icons/pod.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/src/styles/icons/policies.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
28 |
--------------------------------------------------------------------------------
/src/styles/icons/policy.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/src/styles/icons/push.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/src/styles/icons/report-card.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/src/styles/icons/resubmit-failed.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/src/styles/icons/retry.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/src/styles/icons/right-navigation-toolbar.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/src/styles/icons/safe.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/src/styles/icons/sales-channels.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
13 |
--------------------------------------------------------------------------------
/src/styles/icons/sample.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/src/styles/icons/scale.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/src/styles/icons/search.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/src/styles/icons/slack-02.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/src/styles/icons/stop-property.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/src/styles/icons/stop.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/src/styles/icons/storageclass.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
--------------------------------------------------------------------------------
/src/styles/icons/storageprovider.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
14 |
--------------------------------------------------------------------------------
/src/styles/icons/tag.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/src/styles/icons/template.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
26 |
--------------------------------------------------------------------------------
/src/styles/icons/terminate.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/src/styles/icons/test.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
--------------------------------------------------------------------------------
/src/styles/icons/timeline.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
24 |
--------------------------------------------------------------------------------
/src/styles/icons/tools.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
15 |
--------------------------------------------------------------------------------
/src/styles/icons/user-groups.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
--------------------------------------------------------------------------------
/src/styles/icons/user-profile.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/src/styles/icons/user.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/src/styles/icons/volume.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
24 |
--------------------------------------------------------------------------------
/src/styles/icons/warning.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/src/styles/icons/workflow.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
--------------------------------------------------------------------------------
/src/styles/icons/yaml.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/src/styles/main.scss:
--------------------------------------------------------------------------------
1 | @import './config';
2 | @import './argo-icon';
3 |
4 | @import 'node_modules/foundation-sites/scss/foundation';
5 | @import 'node_modules/@fortawesome/fontawesome-free/scss/fontawesome';
6 | @import 'node_modules/@fortawesome/fontawesome-free/scss/solid';
7 | @import 'node_modules/@fortawesome/fontawesome-free/scss/brands';
8 | @import 'node_modules/@fortawesome/fontawesome-free/scss/regular';
9 |
10 | @include foundation-global-styles;
11 | @include foundation-flex-classes;
12 | @include foundation-flex-grid;
13 | @include foundation-dropdown;
14 | @include foundation-typography;
15 | @include foundation-visibility-classes;
16 |
17 | body {
18 | margin: 0;
19 | font-family: $body-font-family;
20 | font-weight: normal;
21 | font-size: 16px;
22 | font-style: normal;
23 | color: $argo-color-gray-7;
24 | background: $argo-color-gray-3;
25 |
26 | button, a, span, input {
27 | outline: none !important
28 | }
29 | }
30 |
31 | a, a:hover {
32 | color: $argo-color-teal-6;
33 | }
34 |
35 | h1, h2 {
36 | color: $argo-color-teal-6;
37 | }
38 |
39 | @import './elements/buttons';
40 | @import './elements/containers';
41 | @import './elements/table-list';
42 | @import './elements/form-controls';
43 | @import './elements/utils';
44 |
--------------------------------------------------------------------------------
/src/styles/theme.scss:
--------------------------------------------------------------------------------
1 | @import './config.scss';
2 |
3 | $themes: (
4 | light: (
5 | background-1: $argo-color-gray-3,
6 | text-1: $argo-color-gray-7,
7 | background-2: $white-color,
8 | text-2: $argo-color-gray-8,
9 | light-argo-gray-6: $argo-color-gray-6,
10 | light-argo-gray-2: $argo-color-gray-2,
11 | light-argo-teal-1: $argo-color-teal-1,
12 | light-argo-teal-7: $argo-color-teal-7,
13 | light-argo-teal-5: $argo-color-teal-5,
14 | pod-cyan: lightcyan,
15 | layout-loader-bg: rgba($argo-color-gray-7, 0.4),
16 | overlay: rgba(222, 222, 222, 0.62),
17 | shadow: $argo-color-gray-5,
18 | border: $argo-color-gray-3
19 | ),
20 | dark: (
21 | background-1: $dark-theme-background-1,
22 | text-1: $argo-color-gray-3,
23 | background-2: $dark-theme-background-2,
24 | text-2: $white-color,
25 | light-argo-gray-6: $argo-color-gray-2,
26 | light-argo-gray-2: $dark-theme-sliding-panel,
27 | light-argo-teal-1: $argo-color-gray-6,
28 | light-argo-teal-7: $argo-color-teal-5,
29 | light-argo-teal-5: $argo-color-teal-4,
30 | pod-cyan: $argo-color-teal-8,
31 | layout-loader-bg: rgba($argo-color-gray-3, 0.4),
32 | overlay: rgba(70, 70, 70, 0.62),
33 | shadow: $dark-theme-background-1,
34 | border: $argo-color-gray-7
35 | )
36 | );
37 |
38 |
39 | @mixin themify($themes) {
40 | @each $theme, $map in $themes {
41 | .theme-#{$theme} & {
42 | $theme-map: () !global;
43 | @each $key, $submap in $map {
44 | $value: map-get(map-get($themes, $theme), '#{$key}');
45 | $theme-map: map-merge($theme-map, ($key: $value)) !global;
46 | }
47 | @content;
48 | $theme-map: null !global;
49 | }
50 | }
51 | }
52 |
53 | @function themed($key) {
54 | @return map-get($theme-map, $key);
55 | }
56 |
--------------------------------------------------------------------------------
/stories/data-loader.stories.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { App } from './utils';
4 |
5 | import { DataLoader } from '../src/components/data-loader';
6 |
7 | function loadData(input: string): Promise {
8 | return new Promise((resolve) => window.setTimeout(() => resolve(`hello ${input}`), 50));
9 | }
10 |
11 | export default {
12 | title: 'Data Loader',
13 | };
14 |
15 | export const LoadingDataAsynchronously = () => {
16 | const [input, setInput] = React.useState('world');
17 | return (
18 |
19 | {() => (
20 |
21 | setInput(e.target.value)}/>
22 |
23 | {(data) => (
24 |
25 | {data}
26 |
27 | )}
28 |
29 |
30 | )}
31 |
32 | );
33 | };
34 | LoadingDataAsynchronously.storyName = 'loading data asynchronously';
35 |
--------------------------------------------------------------------------------
/stories/dropdown.stories.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { DropDown } from '../src/components/dropdown/dropdown';
4 | import { DropDownMenu } from '../src/components/dropdown-menu';
5 |
6 | export default {
7 | title: 'Dropdown',
8 | };
9 |
10 | export const Default = () => ( Click me}>Dropdown content here
);
11 | Default.storyName = 'default';
12 |
13 | export const Menu = () => {
14 | return (
15 | Click me}>
16 |
20 |
21 | );
22 | }
23 | Menu.storyName = 'menu';
24 |
25 | export const MenuWrapper = () => {
26 | return (
27 | Click me} items={[{
28 | title: 'menu item 1',
29 | action: () => window.alert('Clicked!'),
30 | }]} />
31 | );
32 | }
33 | MenuWrapper.storyName = 'menu wrapper';
34 |
--------------------------------------------------------------------------------
/stories/forms.stories.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { Form, Text } from 'react-form';
3 |
4 | import { FormField, FormSelect } from '../src/components/form-field/form-field';
5 |
6 | export default {
7 | title: 'Forms',
8 | };
9 |
10 | export const Default = () => {
11 | return (
12 |
25 | )}
26 |
27 | );
28 | }
29 | Default.storyName = 'default';
30 |
--------------------------------------------------------------------------------
/stories/logs-viewer.stories.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { Observable } from 'rxjs';
3 |
4 | import { LogsViewer } from '../src/components/logs-viewer/logs-viewer';
5 |
6 | export default {
7 | title: 'LogsViewer',
8 | };
9 |
10 | export const Default = () => (
11 |
12 | new Observable((observer) => {
15 | const interval = setInterval(() => observer.next('test\n'), 1000);
16 | return () => clearInterval(interval);
17 | }),
18 | shouldRepeat: () => false,
19 | }}/>
20 |
21 | );
22 | Default.storyName = 'default';
23 |
--------------------------------------------------------------------------------
/stories/notifications.stories.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { NotificationType } from '../src/components/notifications/notifications';
4 |
5 | import { App } from './utils';
6 |
7 | const messages = [
8 | 'Mist enveloped the ship three hours out from port. The recorded voice scratched in the speaker. Silver mist suffused the deck of the ship.',
9 | 'Then came the night of the first falling star. A red flare silhouetted the jagged edge of a wing. She stared through the window at the stars.',
10 | 'The spectacle before us was indeed sublime. A shining crescent far beneath the flying vessel. She stared through the window at the stars.',
11 | ];
12 |
13 | function getMessage() {
14 | return messages[Math.floor(Math.random() * messages.length)];
15 | }
16 |
17 | export default {
18 | title: 'Notifications',
19 | };
20 |
21 | export const Default = () => {
22 | return (
23 |
24 | {(apis) => [
25 | {type: NotificationType.Success, title: 'Success'},
26 | {type: NotificationType.Warning, title: 'Warning'},
27 | {type: NotificationType.Error, title: 'Error'},
28 | ].map((item) => (
29 |
34 | ))}
35 |
36 | );
37 | }
38 | Default.storyName = 'default';
39 |
--------------------------------------------------------------------------------
/stories/select.stories.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { Select } from '../src/components/select/select';
4 |
5 | export default {
6 | title: 'Select',
7 | };
8 |
9 | export const Default = () => {
10 | const [selected, setSelected] = React.useState('option1');
11 | return (
12 |
13 |
14 | Selected option value: {selected}
15 |
16 |
17 |
24 | )
25 | };
26 | Default.storyName = 'default';
27 |
28 | export const MultiSelect = () => {
29 | const [selected, setSelected] = React.useState(['option1']);
30 | return (
31 |
32 |
40 | )
41 | };
42 | MultiSelect.storyName = 'multi-select';
43 |
--------------------------------------------------------------------------------
/stories/table.stories.tsx:
--------------------------------------------------------------------------------
1 | import {default as classNames} from 'classnames';
2 | import * as React from 'react';
3 |
4 | export default {
5 | title: 'Table',
6 | };
7 |
8 | class TableExample extends React.Component {
9 |
10 | constructor(props: any) {
11 | super(props);
12 | this.state = { selectedIndex: 0 };
13 | }
14 |
15 | public render() {
16 | const items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
17 | return (
18 |
19 |
20 |
21 |
Header 1
22 |
Header 2
23 |
Header 3
24 |
25 |
26 | {items.map((item, i) => (
27 |
this.setState({ selectedIndex: i })}>
28 |
29 |
Cell 1 {item}
30 |
Cell 2 {item}
31 |
Cell 3 {item}
32 |
33 |
34 | ))}
35 |
36 | );
37 | }
38 | }
39 |
40 | export const Default = () => ;
41 | Default.storyName = 'default';
42 |
--------------------------------------------------------------------------------
/stories/tabs.stories.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import { Tabs } from '../src/components/tabs/tabs';
4 |
5 | export default {
6 | title: 'Tabs',
7 | };
8 |
9 | export const BasicTabs = () => (
10 | Tab 1 content,
14 | key: 'tab1',
15 | }, {
16 | title: 'Tab 2 with badge',
17 | content: Tab 2 content
,
18 | key: 'tab2',
19 | badge: '5',
20 | }]}/>
21 | );
22 | BasicTabs.storyName = 'basic tabs';
23 |
--------------------------------------------------------------------------------
/stories/utils.tsx:
--------------------------------------------------------------------------------
1 | import * as PropTypes from 'prop-types';
2 | import * as React from 'react';
3 |
4 | import { Notifications } from '../src/components/notifications/notifications';
5 | import { NotificationsApi, NotificationsManager } from '../src/components/notifications/notification-manager';
6 | import { Popup, PopupProps } from '../src/components/popup/popup';
7 | import { PopupApi, PopupManager } from '../src/components/popup/popup-manager';
8 |
9 | export class App extends React.Component<{ children: (apis: {
10 | notifications: NotificationsApi,
11 | popup: PopupApi,
12 | }) => React.ReactNode }, { popupProps: PopupProps }> {
13 |
14 | public static childContextTypes = {
15 | history: PropTypes.object,
16 | apis: PropTypes.object,
17 | };
18 |
19 | private popupManager: PopupManager;
20 | private notificationsManager: NotificationsManager;
21 |
22 | constructor(props: { children: (apis: { notifications: NotificationsApi, popup: PopupApi }) => React.ReactNode }) {
23 | super(props);
24 | this.state = { popupProps: null };
25 | this.notificationsManager = new NotificationsManager();
26 | this.popupManager = new PopupManager();
27 | }
28 |
29 | public componentDidMount() {
30 | this.popupManager.popupProps.subscribe((popupProps) => this.setState({ popupProps }));
31 | }
32 |
33 | public render() {
34 | return (
35 |
36 |
37 | {this.state.popupProps && }
38 | {this.props.children({ notifications: this.notificationsManager, popup: this.popupManager })}
39 |
40 | );
41 | }
42 |
43 | public getChildContext() {
44 | return { history, apis: { popup: this.popupManager, notifications: this.notificationsManager } };
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "./../bundle",
4 | "sourceMap": true,
5 | "noImplicitAny": true,
6 | "module": "commonjs",
7 | "target": "es6",
8 | "jsx": "react",
9 | "esModuleInterop": true,
10 | "experimentalDecorators": true,
11 | "noUnusedLocals": true,
12 | "declaration": true,
13 | "skipLibCheck": true,
14 | "lib": [
15 | "es2017",
16 | "dom"
17 | ],
18 | "typeRoots": [
19 | "./node_modules/@types"
20 | ]
21 | },
22 | "include": ["./**/*"],
23 | "exclude": ["node_modules", "./**/*.test.ts", "./**/*.test.tsx", "./**/*.stories.tsx"]
24 | }
25 |
--------------------------------------------------------------------------------
/v2/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [["@babel/plugin-proposal-private-property-in-object", {"loose": true}]]
3 | }
4 |
--------------------------------------------------------------------------------
/v2/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
--------------------------------------------------------------------------------
/v2/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "bracketSpacing": false,
3 | "jsxSingleQuote": true,
4 | "printWidth": 180,
5 | "singleQuote": true,
6 | "tabWidth": 4,
7 | "jsxBracketSameLine": true,
8 | "quoteProps": "consistent"
9 | }
10 |
--------------------------------------------------------------------------------
/v2/.storybook/Argo.js:
--------------------------------------------------------------------------------
1 | import {create} from '@storybook/theming';
2 |
3 | export default create({
4 | base: 'light',
5 | brandTitle: 'Argo UX',
6 | fontBase: '"Heebo", sans-serif',
7 | brandUrl: 'https://github.com/argoproj/argo-ui',
8 | brandImage: 'https://raw.githubusercontent.com/cncf/artwork/master/projects/argo/horizontal/color/argo-horizontal-color.png',
9 | });
10 |
--------------------------------------------------------------------------------
/v2/.storybook/images/argo-favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/argoproj/argo-ui/5cf36101733ce43eed57242a12389f2a7e40bd2b/v2/.storybook/images/argo-favicon.png
--------------------------------------------------------------------------------
/v2/.storybook/images/argo-icon-color-square.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/argoproj/argo-ui/5cf36101733ce43eed57242a12389f2a7e40bd2b/v2/.storybook/images/argo-icon-color-square.png
--------------------------------------------------------------------------------
/v2/.storybook/main.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | module.exports = {
4 | stories: ['../components/**/*.stories.tsx'],
5 | addons: ['@storybook/addon-essentials'],
6 | webpackFinal: async (config, {configType}) => {
7 | config.module.rules.push({
8 | test: /\.scss$/,
9 | use: ['style-loader', 'css-loader', 'sass-loader'],
10 | include: path.resolve(__dirname, '../'),
11 | });
12 | return config;
13 | },
14 | managerHead: (head) => `
15 | ${head}
16 |
17 | `,
18 | };
19 |
--------------------------------------------------------------------------------
/v2/.storybook/manager.js:
--------------------------------------------------------------------------------
1 | import {addons} from '@storybook/addons';
2 | import argo from './Argo';
3 |
4 | addons.setConfig({
5 | theme: argo,
6 | });
7 |
--------------------------------------------------------------------------------
/v2/.storybook/preview.js:
--------------------------------------------------------------------------------
1 | import argo from './Argo';
2 |
3 | export const parameters = {
4 | backgrounds: {
5 | default: 'light',
6 | values: [
7 | {
8 | name: 'light',
9 | value: '#dee6eb',
10 | },
11 | {
12 | name: 'dark',
13 | value: '#0e0e14',
14 | },
15 | ],
16 | },
17 | docs: {
18 | theme: argo,
19 | },
20 | };
21 |
--------------------------------------------------------------------------------
/v2/README.md:
--------------------------------------------------------------------------------
1 | # Argo UI v2 Components
2 |
3 | Version 2 components are all based on React Hooks, and contain inline documentation.
4 |
--------------------------------------------------------------------------------
/v2/components/alert/alert.scss:
--------------------------------------------------------------------------------
1 | @import '../../styles/colors.scss';
2 |
3 | .alert {
4 | font-family: 'Heebo', sans-serif;
5 | height: 2em;
6 | line-height: 2em;
7 | padding: 5px 10px;
8 | border-radius: 5px;
9 | color: white;
10 | &--error {
11 | background-color: $coral;
12 | }
13 |
14 | &--dark#{&}--error {
15 | background-color: $dirt;
16 | }
17 |
18 | &--success {
19 | background-color: $lime;
20 | }
21 |
22 | &--dark#{&--success} {
23 | background-color: $leaf;
24 | }
25 |
26 | &--warning {
27 | background-color: $canary;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/v2/components/alert/alert.stories.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import {Alert} from './alert';
3 |
4 | export default {
5 | title: 'Components/Alert',
6 | component: Alert,
7 | argTypes: {
8 | text: {control: {type: 'text'}},
9 | style: {control: {disable: true}},
10 | },
11 | };
12 |
13 | export const Primary = (args: any) => {
14 | return {args.text};
15 | };
16 |
17 | Primary.args = {
18 | type: 'success',
19 | text: 'Hello there!',
20 | };
21 |
--------------------------------------------------------------------------------
/v2/components/alert/alert.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import ThemeDiv from '../theme-div/theme-div';
3 |
4 | import './alert.scss';
5 |
6 | export enum AlertType {
7 | Error = 'error',
8 | Warning = 'warning',
9 | Success = 'success',
10 | }
11 |
12 | interface AlertProps {
13 | children: string | string[];
14 | type: AlertType;
15 | style?: React.CSSProperties;
16 | }
17 |
18 | /**
19 | * Displays important information in a colored banner
20 | */
21 | export const Alert = (props: AlertProps) => {
22 | return (
23 |
24 | {props.children}
25 |
26 | );
27 | };
28 |
29 | export default Alert;
30 |
--------------------------------------------------------------------------------
/v2/components/autocomplete/autocomplete.scss:
--------------------------------------------------------------------------------
1 | @import '../../styles/colors.scss';
2 |
3 | .autocomplete {
4 | min-width: 0;
5 | position: relative;
6 | box-sizing: border-box;
7 | $autocompleteClass: &;
8 |
9 | &__items {
10 | z-index: 3;
11 | position: fixed;
12 | white-space: nowrap;
13 | max-height: 12em;
14 | overflow-y: auto;
15 | border-radius: 3px;
16 | background-color: white;
17 | border: 1px solid $argo-color-gray-4;
18 | padding: 10px;
19 | font-family: 'Heebo', sans-serif;
20 | box-shadow: 1px 2px 2px rgba(0, 0, 0, 0.05);
21 | line-height: 0.5em;
22 |
23 | &__item {
24 | z-index: 2;
25 | padding: 0.75em 0;
26 | padding-right: 30px;
27 | padding-left: 5px;
28 | cursor: pointer;
29 | border-bottom: 1px solid $argo-color-gray-4;
30 | box-sizing: border-box;
31 |
32 | &:hover,
33 | &--selected {
34 | border-radius: 3px;
35 | }
36 |
37 | &:hover {
38 | background-color: $argo-color-gray-3;
39 | }
40 |
41 | &--selected {
42 | background-color: $argo-color-teal-3;
43 | }
44 | }
45 |
46 | &--dark {
47 | background-color: $space;
48 | border: 1px solid $silver-lining;
49 | box-shadow: 1px 2px 3px 1px $space;
50 | color: $shine;
51 |
52 | #{$autocompleteClass}__items__item {
53 | border-bottom: 1px solid $silver-lining;
54 | }
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/v2/components/autocomplete/autocomplete.stories.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import {Autocomplete} from './autocomplete';
3 |
4 | export default {
5 | title: 'Components/Autocomplete',
6 | component: Autocomplete,
7 | argTypes: {
8 | onItemClick: {action: 'itemClicked'},
9 | items: {control: {type: 'object'}},
10 | inputStyle: {control: {disable: true}},
11 | placeholder: {control: {type: 'text'}},
12 | },
13 | };
14 |
15 | export const Primary = (args: any) => (
16 |
19 | );
20 |
21 | Primary.args = {
22 | placeholder: 'Type something here',
23 | };
24 |
--------------------------------------------------------------------------------
/v2/components/box/box.scss:
--------------------------------------------------------------------------------
1 | @import '../../styles/colors.scss';
2 |
3 | .argo-box {
4 | border: 1px solid $argo-color-gray-4;
5 | border-radius: 5px;
6 | padding: 20px;
7 | background-color: white;
8 | height: max-content;
9 | box-sizing: border-box;
10 |
11 | &__title {
12 | font-size: 18px;
13 | font-weight: 600;
14 | margin-bottom: 0.5em;
15 | }
16 |
17 | &--dark {
18 | background-color: $space;
19 | border-color: $silver-lining;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/v2/components/box/box.stories.tsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/argoproj/argo-ui/5cf36101733ce43eed57242a12389f2a7e40bd2b/v2/components/box/box.stories.tsx
--------------------------------------------------------------------------------
/v2/components/box/box.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import {ThemeDiv} from '../theme-div/theme-div';
4 |
5 | import './box.scss';
6 |
7 | export const Box = (props: {children: React.ReactNode}) => {
8 | return {props.children};
9 | };
10 |
11 | export const BoxTitle = (props: {children: React.ReactNode}) => {
12 | return {props.children};
13 | };
14 |
--------------------------------------------------------------------------------
/v2/components/checkbox/checkbox.scss:
--------------------------------------------------------------------------------
1 | @import '../../styles/colors.scss';
2 |
3 | .checkbox {
4 | $size: 1.25em;
5 | width: $size;
6 | height: $size;
7 | padding: 3px;
8 | border: 2px solid $argo-color-gray-7;
9 | border-radius: 3px;
10 | display: flex;
11 | align-items: center;
12 | justify-content: center;
13 | color: rgba(0, 0, 0, 0);
14 |
15 | &--selected {
16 | border-color: $argo-color-teal-6;
17 | background-color: $argo-color-teal-6;
18 | color: white;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/v2/components/checkbox/checkbox.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import './checkbox.scss';
4 |
5 | export const Checkbox = (props: {value?: boolean; onChange?: (value: boolean) => void; style?: React.CSSProperties | any}) => {
6 | const [value, setValue] = React.useState(props.value);
7 |
8 | const syncValue = (val: boolean) => {
9 | setValue(val);
10 | if (props.onChange) {
11 | props.onChange(val);
12 | }
13 | };
14 |
15 | React.useEffect(() => {
16 | syncValue(props.value);
17 | }, [props.value]);
18 |
19 | return (
20 | {
23 | e.stopPropagation();
24 | syncValue(!value);
25 | }}
26 | style={props.style}>
27 |
28 |
29 | );
30 | };
31 |
--------------------------------------------------------------------------------
/v2/components/effect-div/effect-div.scss:
--------------------------------------------------------------------------------
1 | @import '../../styles/colors.scss';
2 |
3 | .effect-div {
4 | position: relative;
5 | &__background {
6 | width: 100%;
7 | height: 100%;
8 | position: absolute;
9 | box-sizing: border-box;
10 | top: 0;
11 | left: 0;
12 | right: 0;
13 | bottom: 0;
14 | background-size: cover;
15 | will-change: transform;
16 | transition: transform 0.1s ease-out;
17 | pointer-events: none;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/v2/components/effect-div/effect-div.stories.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import Text from '../text/text';
3 | import {EffectDiv} from './effect-div';
4 |
5 | export default {
6 | component: EffectDiv,
7 | title: 'Components/EffectDiv',
8 | };
9 |
10 | export const Primary = (args: any) => {
11 | return (
12 |
13 | Hello there!
14 |
15 | );
16 | };
17 |
--------------------------------------------------------------------------------
/v2/components/effect-div/effect-div.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import {appendSuffixToClasses} from '../../utils/utils';
3 | import ThemeDiv from '../theme-div/theme-div';
4 |
5 | import './effect-div.scss';
6 |
7 | interface EffectDivProps extends React.DetailedHTMLProps, HTMLDivElement> {
8 | children?: React.ReactNode;
9 | innerref?: React.MutableRefObject;
10 | }
11 |
12 | /**
13 | * EffectDiv is a component that attaches a background to a div, that can be animated with CSS transitions or otherwise.
14 | * It was designed to avoid text artifacts when scaling a div; an EffectDiv allows you to easily scale JUST its background, and not its contents.
15 | *
16 | * You can drop in replace a div with an EffectDiv, but to add a background effect, you need to:
17 | *
18 | * - Remove background styles from the main div (including border and border-radius)
19 | * - Add the styles you removed to the `&__background` selector
20 | * - Add transitions to the `&__background` selector
21 | */
22 | export const EffectDiv = (props: EffectDivProps) => {
23 | const backgroundCl = appendSuffixToClasses(props.className, '__background');
24 | return (
25 |
26 |
27 | {props.children}
28 |
29 | );
30 | };
31 |
--------------------------------------------------------------------------------
/v2/components/filler/filler.scss:
--------------------------------------------------------------------------------
1 | @import '../../styles/colors.scss';
2 |
3 | .filler {
4 | box-sizing: border-box;
5 | font-size: 18px;
6 | text-align: center;
7 | color: $argo-color-gray-5;
8 | margin-top: 2em;
9 | i {
10 | margin-right: 5px;
11 | }
12 | &--dark {
13 | color: $dull-shine;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/v2/components/filler/filler.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import {ThemeDiv} from '../theme-div/theme-div';
4 |
5 | import './filler.scss';
6 |
7 | export const Filler = (props: {children: React.ReactNode}) => {props.children};
8 |
--------------------------------------------------------------------------------
/v2/components/flexy/flexy.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export const Flexy = (props: {noAlignment?: boolean; onClick?: (e: any) => void; className?: string; style?: React.CSSProperties; children: React.ReactNode}) => {
4 | return (
5 |
6 | {props.children}
7 |
8 | );
9 | };
10 |
--------------------------------------------------------------------------------
/v2/components/header/header.scss:
--------------------------------------------------------------------------------
1 | @import '../../styles/colors.scss';
2 |
3 | .header {
4 | display: flex;
5 | background: $slate;
6 | color: white;
7 | align-items: center;
8 | padding: 10px 0;
9 | font-family: 'Heebo', sans-serif;
10 |
11 | &__brand {
12 | color: $shine;
13 | display: flex;
14 | align-items: center;
15 | text-decoration: none;
16 | -webkit-user-select: none;
17 | -moz-user-select: none;
18 | -ms-user-select: none;
19 | user-select: none;
20 | }
21 |
22 | &__welcome {
23 | position: absolute;
24 | transform-origin: left;
25 | display: block;
26 | overflow: hidden;
27 | white-space: nowrap;
28 | transition: transform 1s ease 1s, opacity 1s ease;
29 | }
30 |
31 | &__title {
32 | position: absolute;
33 | transition: transform 500ms ease 750ms;
34 | display: flex;
35 | flex-wrap: nowrap;
36 | flex-grow: 1;
37 | align-items: center;
38 | white-space: nowrap;
39 | }
40 |
41 | h1 {
42 | position: relative;
43 | font-size: 22px;
44 | font-weight: 400;
45 | margin: 0;
46 | display: flex;
47 | align-items: center;
48 | }
49 |
50 | h2 {
51 | font-size: 18px;
52 | color: $sherbert;
53 | margin: 0;
54 | margin-left: 10px;
55 | flex-grow: 1;
56 | white-space: nowrap;
57 | }
58 |
59 | &__info {
60 | margin-left: auto;
61 | display: flex;
62 | align-items: center;
63 | }
64 | &__version {
65 | color: $shine;
66 | margin: 0 15px;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/v2/components/header/header.stories.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import {Brand, Header} from './header';
3 |
4 | export default {
5 | component: Header,
6 | title: 'Components/Header',
7 | argTypes: {
8 | text: {control: {type: 'text'}},
9 | style: {control: {disable: true}},
10 | },
11 | };
12 |
13 | export const Primary = (args: any) => {
14 | return (
15 |
18 | );
19 | };
20 |
21 | Primary.args = {
22 | text: 'Hello world!',
23 | };
24 |
25 | export const Secondary = (args: any) => {
26 | return (
27 |
30 | );
31 | };
32 |
33 | Secondary.args = {
34 | brandName: 'Argo UX',
35 | };
36 |
--------------------------------------------------------------------------------
/v2/components/header/header.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import './header.scss';
4 |
5 | export const Logo = () =>
;
6 |
7 | interface BrandProps {
8 | path?: string;
9 | welcomeText?: string;
10 | brandName?: string;
11 | logo?: React.ReactNode;
12 | }
13 | /**
14 | * Brand Hero designed for display in a header. Includes the Argo Logo and a welcome message that fades after page load
15 | */
16 | export const Brand = (props: BrandProps) => {
17 | const [loading, setLoading] = React.useState(true);
18 | React.useEffect(() => {
19 | setLoading(true);
20 | setTimeout(() => setLoading(false), 500);
21 | }, [props.welcomeText]);
22 |
23 | const showWelcome = loading && !props.path;
24 | const welcomeText = props.welcomeText || 'Welcome to';
25 | return (
26 |
27 | {props.logo ? props.logo :
}
28 |
29 |
30 | {welcomeText}
31 |
32 |
33 | {props.brandName || 'Argo'} {props.path &&
/ {props.path}
}
34 |
35 |
36 |
37 | );
38 | };
39 |
40 | interface HeaderProps {
41 | children: React.ReactNode;
42 | style?: React.CSSProperties;
43 | }
44 |
45 | /**
46 | * Header banner in dark indigo color, designed for display at the top of a page. Wraps its children in indigo banner.
47 | */
48 | export const Header = (props: HeaderProps) => {
49 | return (
50 |
51 | {props.children}
52 |
53 | );
54 | };
55 |
--------------------------------------------------------------------------------
/v2/components/index.ts:
--------------------------------------------------------------------------------
1 | export * from './action-button/action-button';
2 | export * from './alert/alert';
3 | export * from './autocomplete/autocomplete';
4 | export * from './box/box';
5 | export * from './checkbox/checkbox';
6 | export * from './effect-div/effect-div';
7 | export * from './filler/filler';
8 | export * from './flexy/flexy';
9 | export * from './header/header';
10 | export * from './info-item/info-item';
11 | export * from './input/input';
12 | export * from './menu/menu';
13 | export * from './row/row';
14 | export * from './text/text';
15 | export {ThemeDiv} from './theme-div/theme-div';
16 | export * from './theme-toggle/theme-toggle';
17 | export * from './tooltip/tooltip';
18 | export * from './wait-for/wait-for';
19 |
--------------------------------------------------------------------------------
/v2/components/info-item/info-item-row.stories.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import {InfoItemRow} from './info-item';
3 |
4 | export default {
5 | component: InfoItemRow,
6 | title: 'Components/InfoItemRow',
7 | argTypes: {
8 | style: {control: {disable: true}},
9 | },
10 | args: {
11 | items: [{content: 'Hello world'}],
12 | label: 'Greeting',
13 | },
14 | };
15 |
16 | export const Primary = (args: any) => {
17 | return (
18 |
19 |
20 |
21 | );
22 | };
23 |
--------------------------------------------------------------------------------
/v2/components/info-item/info-item.scss:
--------------------------------------------------------------------------------
1 | @import '../../styles/colors.scss';
2 |
3 | .info-item {
4 | background-color: $argo-color-gray-4;
5 | border-radius: 3px;
6 | border: 1px solid $argo-color-gray-5;
7 | padding: 5px 7px;
8 | margin-right: 5px;
9 | color: $argo-color-gray-8;
10 | display: flex;
11 | align-items: center;
12 | min-width: 0;
13 |
14 | &--lightweight {
15 | border: none;
16 | background: none;
17 | font-weight: 500;
18 | padding-left: 0;
19 | padding-right: 0;
20 | }
21 |
22 | &--dark {
23 | background-color: $fog;
24 | border: 1px solid $silver-lining;
25 | color: $dull-shine;
26 | }
27 |
28 | &--colored {
29 | background-color: $sherbert;
30 | border: 1px solid $sherbert;
31 | color: white;
32 | }
33 |
34 | &--dark#{&}--colored {
35 | background-color: $spray-tan;
36 | border: 1px solid $sherbert;
37 | color: white;
38 | }
39 |
40 | &--canary {
41 | background-color: $canary;
42 | border: 1px solid $canary;
43 | color: $space;
44 | }
45 |
46 | &--bluegreen {
47 | background-color: $sea;
48 | border: 1px solid $sea;
49 | color: white;
50 | }
51 |
52 | &--monospace {
53 | font-family: ui-monospace, Menlo, Monaco, 'Cascadia Mono', 'Segoe UI Mono', 'Roboto Mono', 'Oxygen Mono', 'Ubuntu Monospace', 'Source Code Pro', 'Fira Mono',
54 | 'Droid Sans Mono', 'Courier New', monospace;
55 | font-size: 14px;
56 | }
57 |
58 | &--row {
59 | display: flex;
60 | align-items: center;
61 | flex-grow: 1;
62 | label {
63 | margin-right: auto;
64 | padding-right: 5px;
65 | }
66 | .info-item {
67 | margin: 0.25em 0;
68 | margin-left: 5px;
69 | }
70 |
71 | &__container {
72 | margin-left: auto;
73 | display: flex;
74 | min-width: 0;
75 | padding-left: 25px;
76 | flex-wrap: wrap;
77 | justify-content: flex-end;
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/v2/components/info-item/info-item.stories.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import {InfoItem} from './info-item';
3 |
4 | export default {
5 | component: InfoItem,
6 | title: 'Components/InfoItem',
7 | argTypes: {
8 | style: {control: {disable: true}},
9 | },
10 | args: {
11 | content: 'Hello world',
12 | },
13 | };
14 |
15 | export const Primary = (args: any) => {
16 | return (
17 |
18 |
19 |
20 | );
21 | };
22 |
--------------------------------------------------------------------------------
/v2/components/input/input.scss:
--------------------------------------------------------------------------------
1 | @import '../../styles/colors.scss';
2 |
3 | .input-container {
4 | min-width: 0;
5 | box-sizing: border-box;
6 | .input {
7 | box-sizing: border-box;
8 | width: 100%;
9 | appearance: none;
10 | border-radius: 5px;
11 | padding: 7px 10px;
12 | background-color: $argo-color-gray-3;
13 | border: 1px solid $argo-color-gray-5;
14 | color: $argo-color-gray-7;
15 | min-width: 0;
16 | font-size: 14px;
17 | line-height: 1.5em;
18 | }
19 |
20 | input::placeholder {
21 | color: $argo-color-gray-6;
22 | }
23 |
24 | &--dark {
25 | .input {
26 | background-color: $space;
27 | border: 1px solid $silver-lining;
28 | color: $shine;
29 | }
30 |
31 | input::placeholder {
32 | color: $dull-shine;
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/v2/components/input/input.stories.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import Text from '../text/text';
3 | import {Input, useInput} from './input';
4 |
5 | export default {
6 | title: 'Components/Input',
7 | component: Input,
8 | argTypes: {
9 | style: {control: {disable: true}},
10 | innerref: {control: {disable: true}},
11 | placeholder: {control: {type: 'text'}},
12 | onChange: {action: 'inputChanged'},
13 | },
14 | };
15 |
16 | export const Primary = (args: any) => {
17 | const [field, , fieldProps] = useInput('');
18 | const origOnChange = fieldProps.onChange;
19 | fieldProps.onChange = (val) => {
20 | origOnChange(val);
21 | args.onChange(val);
22 | };
23 | return (
24 |
25 |
26 |
27 |
28 | {field && field !== '' && Your input: {field}}
29 |
30 | );
31 | };
32 |
33 | Primary.args = {
34 | placeholder: 'Type something here',
35 | };
36 |
--------------------------------------------------------------------------------
/v2/components/input/input.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import {ThemeDiv} from '../theme-div/theme-div';
3 |
4 | import './input.scss';
5 |
6 | export interface InputProps {
7 | value: string;
8 | ref: React.MutableRefObject;
9 | onChange: (e: React.ChangeEvent) => void;
10 | }
11 |
12 | export type SetInputFxn = (val: string) => void;
13 | export const FormResetFactory = (setFxns: SetInputFxn[]) => {
14 | return () => {
15 | setFxns.forEach((reset) => reset(''));
16 | };
17 | };
18 |
19 | export const useInput = (init: string, callback?: (val: string) => void): [string, SetInputFxn, InputProps] => {
20 | const [state, setState] = React.useState(init);
21 | const inputRef = React.useRef(null);
22 |
23 | const InputP: InputProps = {
24 | value: state,
25 | ref: inputRef,
26 | onChange: (e: React.ChangeEvent) => {
27 | setState(e.target.value);
28 | if (callback) {
29 | callback(e.target.value);
30 | }
31 | },
32 | };
33 |
34 | return [state, setState, InputP];
35 | };
36 |
37 | export const useDebounce = (value: string, debouncems: number): string => {
38 | const [val, setVal] = React.useState(value);
39 |
40 | React.useEffect(() => {
41 | const to = setTimeout(() => {
42 | setVal(value);
43 | }, debouncems);
44 | return () => clearInterval(to);
45 | }, [value, debouncems]);
46 |
47 | return val;
48 | };
49 |
50 | /**
51 | * A formatted field, accompanied by the `useInput` hook. `useInput` returns a `[data, setData, props]` thruple, where `data` and `setData` are analagous to `React.useState`,
52 | * and `props` are designed to be included in the Input component with a spread operator: ``
53 | */
54 | export const Input = (props: React.InputHTMLAttributes & {innerref?: React.MutableRefObject, className?: string}) => (
55 |
56 |
57 |
58 | );
59 |
--------------------------------------------------------------------------------
/v2/components/menu/menu.scss:
--------------------------------------------------------------------------------
1 | @import '../../styles/colors.scss';
2 |
3 | .menu {
4 | position: absolute;
5 | padding: 5px 10px;
6 | border-radius: 3px;
7 | top: 100%;
8 | z-index: 3;
9 | background-color: white;
10 | color: $argo-color-gray-7;
11 | border: 1px solid $argo-color-gray-4;
12 | box-shadow: 1px 2px 2px rgba(0, 0, 0, 0.05);
13 | width: 150px;
14 | font-family: 'Heebo', sans-serif;
15 |
16 | &__item {
17 | width: 100%;
18 | padding: 0.5em 0;
19 | cursor: pointer;
20 | z-index: 3;
21 |
22 | &__label {
23 | display: inline-block;
24 | margin-left: 10px;
25 | }
26 | }
27 |
28 | &--dark {
29 | border: 1px solid $silver-lining;
30 | background-color: $slate;
31 | color: white;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/v2/components/menu/menu.stories.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import ActionButton from '../action-button/action-button';
3 | import {Menu} from './menu';
4 |
5 | export default {
6 | component: Menu,
7 | title: 'Components/Menu',
8 | };
9 |
10 | export const Primary = (args: any) => {
11 | return (
12 |
17 | );
18 | };
19 |
20 | Primary.args = {
21 | items: ['A', 'B', 'C'],
22 | };
23 |
--------------------------------------------------------------------------------
/v2/components/row/row.scss:
--------------------------------------------------------------------------------
1 | .argo-row {
2 | display: flex;
3 | width: 100%;
4 | margin-bottom: 1.5em;
5 | justify-content: center;
6 |
7 | .argo-box {
8 | height: auto;
9 | width: 400px;
10 | margin-right: 15px;
11 |
12 | &:last-child {
13 | margin-right: 0;
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/v2/components/row/row.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | import {ThemeDiv} from '../theme-div/theme-div';
4 |
5 | import './row.scss';
6 |
7 | export const CenteredRow = (props: {children: React.ReactNode}) => {props.children};
8 |
--------------------------------------------------------------------------------
/v2/components/text/text.scss:
--------------------------------------------------------------------------------
1 | .text {
2 | font-family: 'Heebo', sans-serif;
3 | white-space: normal;
4 | &--dark {
5 | color: white;
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/v2/components/text/text.stories.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import {Text} from './text';
3 |
4 | export default {
5 | component: Text,
6 | title: 'Components/Text',
7 | argTypes: {
8 | children: {control: {type: 'text'}},
9 | dark: {description: 'Light colored text when true. Corresponds to the theme, not the color of the text.'},
10 | theme: {description: 'Same logic as `dark`, but takes precendence over `dark` when set'},
11 | },
12 | };
13 |
14 | export const Primary = (args: any) => {args.children};
15 | Primary.args = {children: 'Hello world'};
16 |
--------------------------------------------------------------------------------
/v2/components/text/text.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import ThemeDiv, {Theme} from '../theme-div/theme-div';
3 |
4 | import './text.scss';
5 |
6 | /**
7 | * Themes children according to Theme Context, and styles them with appropriate font.
8 | */
9 | export const Text = (props: {children: string | string[] | React.ReactNode; dark?: boolean; theme?: Theme; style?: React.CSSProperties}) => {
10 | return (
11 |
12 | {props.children}
13 |
14 | );
15 | };
16 |
17 | export default Text;
18 |
--------------------------------------------------------------------------------
/v2/components/theme-div/theme-div.stories.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import ActionButton from '../action-button/action-button';
3 | import {Flexy} from '../flexy/flexy';
4 | import Text from '../text/text';
5 | import {Theme, ThemeDiv} from './theme-div';
6 |
7 | export default {
8 | component: ThemeDiv,
9 | title: 'Components/ThemeDiv',
10 | argTypes: {
11 | innerref: {control: {disable: true}},
12 | },
13 | };
14 |
15 | export const Primary = (args: any) => {
16 | const [theme, setTheme] = React.useState(Theme.Light);
17 | React.useEffect(() => {
18 | setTheme(args.theme as Theme);
19 | }, [args.theme]);
20 | const toggleTheme = () => setTheme(theme === Theme.Dark ? Theme.Light : Theme.Dark);
21 | const style = {background: theme === Theme.Dark ? 'black' : 'white', padding: '1em'};
22 | return (
23 |
24 |
25 |
26 |
27 | Classes: {theme === Theme.Dark ? 'test test--dark' : 'test'}
28 |
29 |
30 |
31 | );
32 | };
33 |
--------------------------------------------------------------------------------
/v2/components/theme-div/theme-div.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import {useTheme} from '../../shared/context/theme';
3 |
4 | export enum Theme {
5 | Light = 'light',
6 | Dark = 'dark',
7 | }
8 |
9 | /**
10 | * Automatically appends `--dark` to all classes on the div when `theme` prop is set to `Theme.Dark`
11 | */
12 | export const ThemeDiv = (
13 | props: {
14 | children?: React.ReactNode;
15 | disabled?: boolean;
16 | innerref?: React.LegacyRef;
17 | theme?: Theme;
18 | className?: string;
19 | } & React.DetailedHTMLProps, HTMLDivElement>,
20 | ) => {
21 | const theme = props.theme || useTheme();
22 | let clString = props.className;
23 |
24 | if (theme === Theme.Dark && !props.disabled) {
25 | const cl = (clString || '').split(' ') || [];
26 | const darkCl = [];
27 | for (const c of cl) {
28 | if (!c.endsWith('--dark')) {
29 | darkCl.push(c + '--dark');
30 | }
31 | }
32 | clString = `${cl.join(' ')} ${darkCl.join(' ')}`;
33 | }
34 |
35 | return (
36 |
37 | {props.children}
38 |
39 | );
40 | };
41 |
42 | export default ThemeDiv;
43 |
--------------------------------------------------------------------------------
/v2/components/theme-toggle/theme-toggle.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import {Theme, ThemeContext} from '../../shared/context/theme';
3 | import {ActionButton} from '../action-button/action-button';
4 |
5 | export const ThemeToggle = () => {
6 | const dmCtx = React.useContext(ThemeContext);
7 | const isDark = dmCtx.theme === Theme.Dark;
8 | const icon = isDark ? 'fa-sun' : 'fa-moon';
9 | return dmCtx.set(isDark ? Theme.Light : Theme.Dark)} icon={icon} dark={true} />;
10 | };
11 |
--------------------------------------------------------------------------------
/v2/components/ticker/ticker.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/argoproj/argo-ui/5cf36101733ce43eed57242a12389f2a7e40bd2b/v2/components/ticker/ticker.scss
--------------------------------------------------------------------------------
/v2/components/ticker/ticker.stories.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import {Ticker} from './ticker';
3 |
4 | export default {
5 | component: Ticker,
6 | title: 'Components/Ticker',
7 | argTypes: {
8 | value: {control: {type: 'number'}},
9 | },
10 | };
11 |
12 | export const Primary = () => {
13 | const [randVal, setRandVal] = React.useState(0);
14 |
15 | React.useEffect(() => {
16 | const to = setTimeout(() => {
17 | setRandVal(Math.round((Math.random() * 10) % 10));
18 | return () => clearInterval(to);
19 | }, 2000);
20 | });
21 | return ;
22 | };
23 |
--------------------------------------------------------------------------------
/v2/components/ticker/ticker.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import {Flexy} from '../flexy/flexy';
3 | import Text from '../text/text';
4 | import ThemeDiv from '../theme-div/theme-div';
5 |
6 | interface TickerProps {
7 | value: number;
8 | }
9 |
10 | /**
11 | * Ticker
12 | */
13 | export const Ticker = (props: TickerProps) => {
14 | const [cur, setCur] = React.useState(props.value);
15 | const [delta, setDelta] = React.useState(0);
16 |
17 | React.useEffect(() => {
18 | setDelta(cur - props.value);
19 | setCur(props.value);
20 | }, [props.value]);
21 | return (
22 |
23 |
24 | 0 ? 'fa-arrow-down' : 'fa-arrow-up'}`} style={{marginRight: '5px'}} />
25 | {props.value}
26 |
27 |
28 | );
29 | };
30 |
--------------------------------------------------------------------------------
/v2/components/tooltip/tooltip.scss:
--------------------------------------------------------------------------------
1 | @import '../../styles/colors.scss';
2 |
3 | .tooltip {
4 | position: absolute;
5 | padding: 5px 10px;
6 | border-radius: 3px;
7 | bottom: 100%;
8 | z-index: 2;
9 | background-color: $slate;
10 | white-space: nowrap;
11 | color: white;
12 | border: 1px solid $slate;
13 | &--dark {
14 | border: 1px solid $dull-shine;
15 | }
16 |
17 | &--inverted {
18 | top: 100%;
19 | bottom: auto;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/v2/components/tooltip/tooltip.stories.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import Text from '../text/text';
3 | import {Tooltip} from './tooltip';
4 |
5 | export default {
6 | component: Tooltip,
7 | title: 'Components/Tooltip',
8 | };
9 |
10 | export const Primary = (args: any) => {
11 | return (
12 |
13 | {args.content}}>
14 | Hover over me!
15 |
16 |
17 | );
18 | };
19 |
20 | Primary.args = {
21 | content: 'Hello there!',
22 | };
23 |
--------------------------------------------------------------------------------
/v2/components/tooltip/tooltip.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import {ThemeDiv} from '../theme-div/theme-div';
3 |
4 | import './tooltip.scss';
5 |
6 | export const useHover = (): [React.MutableRefObject, boolean] => {
7 | const [show, setShow] = React.useState(false);
8 | const ref = React.useRef(null);
9 |
10 | const handleMouseOver = () => setShow(true);
11 | const handleMouseOut = () => setShow(false);
12 |
13 | React.useEffect(() => {
14 | const cur = ref.current;
15 |
16 | if (cur) {
17 | cur.addEventListener('mouseover', handleMouseOver);
18 | cur.addEventListener('mouseout', handleMouseOut);
19 |
20 | return () => {
21 | cur.removeEventListener('mouseover', handleMouseOver);
22 | cur.removeEventListener('mouseout', handleMouseOut);
23 | };
24 | }
25 | }, []);
26 |
27 | return [ref, show];
28 | };
29 |
30 | /**
31 | * Displays a Tooltip when its children are hovered over
32 | */
33 | export const Tooltip = (props: {content: React.ReactNode | string; inverted?: boolean} & React.PropsWithRef) => {
34 | const [tooltip, showTooltip] = useHover();
35 | return (
36 |
37 |
38 | {props.content}
39 |
40 |
{props.children}
41 |
42 | );
43 | };
44 |
--------------------------------------------------------------------------------
/v2/components/wait-for/loading-bar.scss:
--------------------------------------------------------------------------------
1 | @import '../../styles/colors.scss';
2 |
3 | .loading-bar {
4 | background-color: $argo-color-gray-4;
5 | border-radius: 3px;
6 | height: 7px;
7 | overflow: hidden;
8 | width: 350px;
9 | will-change: opacity;
10 | &--dark {
11 | background-color: $slate;
12 | }
13 | &__fill {
14 | transform-origin: left;
15 | background-color: $sherbert;
16 | height: 100%;
17 | width: 1px;
18 |
19 | &--loaded {
20 | transform: scaleX(350);
21 | }
22 | }
23 |
24 | &--loaded {
25 | opacity: 0;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/v2/components/wait-for/spinner.scss:
--------------------------------------------------------------------------------
1 | @import '../../styles/colors.scss';
2 |
3 | .spinner {
4 | color: $slate;
5 | font-size: 25px;
6 |
7 | &--dark {
8 | color: white;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/v2/components/wait-for/wait-for.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import {ThemeDiv} from '../theme-div/theme-div';
3 | import './loading-bar.scss';
4 | import './spinner.scss';
5 |
6 | export const Spinner = () => (
7 |
8 |
9 |
10 | );
11 |
12 | export const LoadingBar = (props: {loadms?: string | number}) => {
13 | const [loading, setLoading] = React.useState(true);
14 |
15 | const loadms = props.loadms || 400;
16 |
17 | React.useEffect(() => {
18 | setLoading(false);
19 | }, []);
20 | return (
21 | setLoading(false)}>
22 |
23 |
24 | );
25 | };
26 |
27 | export const WaitFor = (props: {loading: boolean; loader?: React.ReactNode; loadms?: string | number} & React.ComponentProps): JSX.Element => (
28 | {props.loading ? props.loader || : props.children}
29 | );
30 |
--------------------------------------------------------------------------------
/v2/index.ts:
--------------------------------------------------------------------------------
1 | export * from './components';
2 | export * from './shared';
3 | export * from './utils';
4 |
--------------------------------------------------------------------------------
/v2/shared/context/theme.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export const THEME_KEY = 'theme';
4 |
5 | export enum Theme {
6 | Dark = 'dark',
7 | Light = 'light',
8 | }
9 |
10 | const init = (JSON.parse(window.localStorage.getItem(THEME_KEY)) as Theme) || Theme.Light;
11 |
12 | interface ThemeContextProps {
13 | theme: Theme;
14 | set: (th: Theme) => void;
15 | }
16 |
17 | export const ThemeContext = React.createContext({theme: init} as ThemeContextProps);
18 |
19 | export const ThemeProvider = (props: {children: React.ReactNode}) => {
20 | const [theme, setTheme] = React.useState(init);
21 | React.useEffect(() => {
22 | window.localStorage.setItem(THEME_KEY, JSON.stringify(theme));
23 | }, [theme]);
24 |
25 | return setTheme(th)}}>{props.children};
26 | };
27 |
28 | export const useTheme = () => {
29 | try {
30 | const dmCtx = React.useContext(ThemeContext);
31 | return dmCtx.theme;
32 | } catch {
33 | return Theme.Light;
34 | }
35 | };
36 |
--------------------------------------------------------------------------------
/v2/shared/index.ts:
--------------------------------------------------------------------------------
1 | export * from './context/theme';
2 | export * from './keypress';
3 |
--------------------------------------------------------------------------------
/v2/styles/colors.scss:
--------------------------------------------------------------------------------
1 | $argo-color-gray-1: #f8fbfb;
2 | $argo-color-gray-2: #eff3f5;
3 | $argo-color-gray-3: #dee6eb;
4 | $argo-color-gray-4: #ccd6dd;
5 | $argo-color-gray-5: #8fa4b1;
6 | $argo-color-gray-6: #6d7f8b;
7 | $argo-color-gray-7: #495763;
8 | $argo-color-gray-8: #363c4a;
9 | $argo-color-gray-9: #000000;
10 |
11 | $argo-color-teal-1: #f5fbfd;
12 | $argo-color-teal-2: #dff6f9;
13 | $argo-color-teal-3: #bdecf2;
14 | $argo-color-teal-4: #99e1ea;
15 | $argo-color-teal-5: #1fbdd0;
16 | $argo-color-teal-6: #00a2b3;
17 | $argo-color-teal-7: #006f8a;
18 | $argo-color-teal-8: #004c67;
19 |
20 | $white-color: #ffffff;
21 |
22 | // Status colors
23 | $argo-failed-color: #e96d76;
24 | $argo-failed-color-dark: #c04b4f;
25 | $argo-failed-color-light: #ff6262;
26 | $argo-status-failed-color: #ef0b28;
27 | $argo-color-red: #f00052;
28 |
29 | $argo-success-color: #18be94;
30 | $argo-success-color-dark: #3f946d;
31 | $argo-success-color-light: #95d58f;
32 | $argo-status-success-color: #0d8d38;
33 | $argo-color-green: #7ed321;
34 |
35 | $argo-status-warning-color: #f4c030;
36 | $argo-color-yellow: #ffd100;
37 |
38 | $argo-running-color-dark: #378398;
39 | $argo-running-color-light: #02c4d3;
40 | $argo-running-color: #0dadea;
41 |
42 | $argo-waiting-color-dark: $argo-color-gray-6;
43 | $argo-waiting-color-light: $argo-color-gray-5;
44 | $argo-waiting-color: $argo-color-gray-5;
45 | $argo-cancelled-color: $argo-color-gray-5;
46 | $argo-init-color: $argo-color-gray-5;
47 |
48 | // Dark Mode
49 | $space: #09090f;
50 | $midnight-sky: #0e0e14;
51 | $slate: #191826;
52 | $shine: #dad6e7;
53 | $dull-shine: #abb2b9;
54 | $silver-lining: #30303d;
55 | $fog: #22212d;
56 | $glow: #434355;
57 |
58 | // Highlights
59 | $spray-tan: #8b4329;
60 | $sherbert: #f07548;
61 | $peach: #fd8a5f;
62 |
63 | // Statuses
64 | $lime: #3eb74f;
65 | $grass: #339440;
66 | $leaf: #246b2d;
67 | $forest: #12221e;
68 |
69 | $sky: #58a6fe;
70 | $sea: #3a5e88;
71 | $deep-sea: #172636;
72 |
73 | $coral: #f85149;
74 | $clay: #9e3d38;
75 | $dirt: #22141a;
76 |
77 | $canary: #e4aa37;
78 |
--------------------------------------------------------------------------------
/v2/utils/index.ts:
--------------------------------------------------------------------------------
1 | export * from './utils';
2 | export * from './watch';
3 |
--------------------------------------------------------------------------------