mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
Merge branch 'dev' into topic/count-pairs-correctly
This commit is contained in:
@@ -5,4 +5,4 @@ set -e
|
||||
flake8 --exclude $(paste -sd, .ci/flake8_ignorelist.txt) .
|
||||
|
||||
# Apply stricter rules for the directories shared with Pulsar
|
||||
flake8 --ignore=D --max-line-length=150 lib/galaxy/jobs/runners/util/
|
||||
flake8 --ignore=E203,D --max-line-length=150 lib/galaxy/jobs/runners/util/
|
||||
|
||||
@@ -104,7 +104,7 @@ jobs:
|
||||
- run: tox -e test_galaxy_packages
|
||||
js_lint:
|
||||
docker:
|
||||
- image: cimg/node:14.15
|
||||
- image: cimg/node:16.13.2
|
||||
<<: *set_workdir
|
||||
steps:
|
||||
- *restore_yarn_cache
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
# Migrate code style to Black and Apply isort
|
||||
623a5e165ad79e15321b19b2a2afbf2f9224cc90
|
||||
# Application of black styling to /tools/
|
||||
03351d2a9f81b938d650e2a955c65c18526ee7f0
|
||||
# Migrate code style to Prettier
|
||||
5b2928f851bd5ea3b9c2a04abf2cee9ff0bc54cc
|
||||
87873c5e2f4e6b97fe0f2084bfca0295fcd471de
|
||||
@@ -0,0 +1 @@
|
||||
comment: false
|
||||
@@ -10,10 +10,13 @@ on:
|
||||
- 'client/**'
|
||||
- 'doc/**'
|
||||
- 'lib/galaxy_test/selenium/**'
|
||||
schedule:
|
||||
# Run at midnight UTC every Tuesday
|
||||
- cron: '0 0 * * 2'
|
||||
env:
|
||||
GALAXY_DEPENDENCIES_INSTALL_WEASYPRINT: '1'
|
||||
GALAXY_TEST_DBURI: 'postgresql://postgres:postgres@localhost:5432/galaxy?client_encoding=utf8'
|
||||
GALAXY_TEST_RAISE_EXCEPTION_ON_HISTORYLESS_HDA: '1'
|
||||
GALAXY_DEPENDENCIES_INSTALL_WEASYPRINT: '1'
|
||||
GALAXY_CONFIG_SQLALCHEMY_WARN_20: '1'
|
||||
concurrency:
|
||||
group: api-${{ github.ref }}
|
||||
@@ -35,6 +38,10 @@ jobs:
|
||||
ports:
|
||||
- 5432:5432
|
||||
steps:
|
||||
- if: github.event_name == 'schedule'
|
||||
run: |
|
||||
echo "GALAXY_CONFIG_OVERRIDE_METADATA_STRATEGY=extended" >> $GITHUB_ENV
|
||||
echo "GALAXY_CONFIG_OVERRIDE_OUTPUTS_TO_WORKING_DIRECTORY=true" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
path: 'galaxy root'
|
||||
@@ -53,11 +60,15 @@ jobs:
|
||||
- name: Cache galaxy venv
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: .venv
|
||||
path: 'galaxy root/.venv'
|
||||
key: gxy-venv-${{ runner.os }}-${{ steps.full-python-version.outputs.version }}-${{ hashFiles('galaxy root/requirements.txt') }}-api
|
||||
- name: Run tests
|
||||
run: ./run_tests.sh --skip_flakey_fails -api
|
||||
run: ./run_tests.sh --coverage --skip_flakey_fails -api
|
||||
working-directory: 'galaxy root'
|
||||
- uses: codecov/codecov-action@v2
|
||||
with:
|
||||
flags: api
|
||||
working-directory: 'galaxy root'
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: failure()
|
||||
with:
|
||||
|
||||
@@ -8,6 +8,9 @@ on:
|
||||
paths-ignore:
|
||||
- 'client/**'
|
||||
- 'doc/**'
|
||||
schedule:
|
||||
# Run at midnight UTC every Tuesday
|
||||
- cron: '0 0 * * 2'
|
||||
env:
|
||||
GALAXY_TEST_RAISE_EXCEPTION_ON_HISTORYLESS_HDA: '1'
|
||||
concurrency:
|
||||
@@ -21,6 +24,10 @@ jobs:
|
||||
matrix:
|
||||
python-version: ['3.7']
|
||||
steps:
|
||||
- if: github.event_name == 'schedule'
|
||||
run: |
|
||||
echo "GALAXY_CONFIG_OVERRIDE_METADATA_STRATEGY=extended" >> $GITHUB_ENV
|
||||
echo "GALAXY_CONFIG_OVERRIDE_OUTPUTS_TO_WORKING_DIRECTORY=true" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
path: 'galaxy root'
|
||||
|
||||
@@ -53,11 +53,15 @@ jobs:
|
||||
- name: Cache galaxy venv
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: .venv
|
||||
path: 'galaxy root/.venv'
|
||||
key: gxy-venv-${{ runner.os }}-${{ steps.full-python-version.outputs.version }}-${{ hashFiles('galaxy root/requirements.txt') }}
|
||||
- name: Run tests
|
||||
run: ./run_tests.sh --skip_flakey_fails -cwl lib/galaxy_test/api/cwl -- -m "${{ matrix.marker }} and ${{ matrix.conformance-version }}"
|
||||
run: ./run_tests.sh --coverage --skip_flakey_fails -cwl lib/galaxy_test/api/cwl -- -m "${{ matrix.marker }} and ${{ matrix.conformance-version }}"
|
||||
working-directory: 'galaxy root'
|
||||
- uses: codecov/codecov-action@v2
|
||||
with:
|
||||
flags: cwl-conformance
|
||||
working-directory: 'galaxy root'
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: failure()
|
||||
with:
|
||||
|
||||
@@ -22,10 +22,6 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version: ['3.7', '3.10']
|
||||
webserver: ['uwsgi']
|
||||
include:
|
||||
- python-version: '3.10'
|
||||
webserver: 'dev'
|
||||
defaults:
|
||||
run:
|
||||
shell: bash -l {0}
|
||||
@@ -57,11 +53,6 @@ jobs:
|
||||
yarn-lock-file: 'galaxy root/client/yarn.lock'
|
||||
- name: Install tox
|
||||
run: pip install tox
|
||||
# Use this job to test the latest migrations
|
||||
- run: wget -q https://github.com/jmchilton/galaxy-downloads/raw/master/db_gx_rev_0141.sqlite
|
||||
- run: mv db_gx_rev_0141.sqlite 'galaxy root'/database/universe.sqlite
|
||||
- name: run tests
|
||||
run: tox -e first_startup
|
||||
working-directory: 'galaxy root'
|
||||
env:
|
||||
APP_WEBSERVER: ${{ matrix.webserver }}
|
||||
|
||||
@@ -10,6 +10,9 @@ on:
|
||||
- 'client/**'
|
||||
- 'doc/**'
|
||||
- 'lib/galaxy_test/selenium/**'
|
||||
schedule:
|
||||
# Run at midnight UTC every Tuesday
|
||||
- cron: '0 0 * * 2'
|
||||
env:
|
||||
GALAXY_TEST_DBURI: 'postgresql://postgres:postgres@localhost:5432/galaxy?client_encoding=utf8'
|
||||
GALAXY_TEST_RAISE_EXCEPTION_ON_HISTORYLESS_HDA: '1'
|
||||
@@ -33,6 +36,10 @@ jobs:
|
||||
ports:
|
||||
- 5432:5432
|
||||
steps:
|
||||
- if: github.event_name == 'schedule'
|
||||
run: |
|
||||
echo "GALAXY_CONFIG_OVERRIDE_METADATA_STRATEGY=extended" >> $GITHUB_ENV
|
||||
echo "GALAXY_CONFIG_OVERRIDE_OUTPUTS_TO_WORKING_DIRECTORY=true" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
path: 'galaxy root'
|
||||
@@ -51,11 +58,15 @@ jobs:
|
||||
- name: Cache galaxy venv
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: .venv
|
||||
path: 'galaxy root/.venv'
|
||||
key: gxy-venv-${{ runner.os }}-${{ steps.full-python-version.outputs.version }}-${{ hashFiles('galaxy root/requirements.txt') }}-framework
|
||||
- name: Run tests
|
||||
run: ./run_tests.sh --framework
|
||||
run: ./run_tests.sh --coverage --framework
|
||||
working-directory: 'galaxy root'
|
||||
- uses: codecov/codecov-action@v2
|
||||
with:
|
||||
flags: framework
|
||||
working-directory: 'galaxy root'
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: failure()
|
||||
with:
|
||||
|
||||
@@ -10,11 +10,15 @@ on:
|
||||
- 'client/**'
|
||||
- 'doc/**'
|
||||
- 'lib/galaxy_test/selenium/**'
|
||||
schedule:
|
||||
# Run at midnight UTC every Tuesday
|
||||
- cron: '0 0 * * 2'
|
||||
env:
|
||||
GALAXY_TEST_AMQP_URL: 'amqp://localhost:5672//'
|
||||
GALAXY_TEST_DBURI: 'postgresql://postgres:postgres@localhost:5432/galaxy?client_encoding=utf8'
|
||||
GALAXY_TEST_RAISE_EXCEPTION_ON_HISTORYLESS_HDA: '1'
|
||||
GALAXY_CONFIG_SQLALCHEMY_WARN_20: '1'
|
||||
GALAXY_DEPENDENCIES_INSTALL_WEASYPRINT: '1'
|
||||
concurrency:
|
||||
group: integration-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
@@ -41,6 +45,11 @@ jobs:
|
||||
ports:
|
||||
- 5672:5672
|
||||
steps:
|
||||
- if: github.event_name == 'schedule'
|
||||
run: |
|
||||
echo "GALAXY_CONFIG_OVERRIDE_METADATA_STRATEGY=extended" >> $GITHUB_ENV
|
||||
# Skip outputs_to_working_directory: true in integration tests, doesn't work with pulsar
|
||||
# echo "GALAXY_CONFIG_OVERRIDE_OUTPUTS_TO_WORKING_DIRECTORY=true" >> $GITHUB_ENV
|
||||
- name: Prune unused docker image, volumes and containers
|
||||
run: docker system prune -a -f
|
||||
- name: Clean dotnet folder for space
|
||||
@@ -74,15 +83,19 @@ jobs:
|
||||
- name: Cache galaxy venv
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: .venv
|
||||
path: 'galaxy root/.venv'
|
||||
key: gxy-venv-${{ runner.os }}-${{ steps.full-python-version.outputs.version }}-${{ hashFiles('galaxy root/requirements.txt') }}-integration
|
||||
- name: Install ffmpeg
|
||||
run: sudo apt-get update && sudo apt-get -y install ffmpeg
|
||||
- name: Run tests
|
||||
run: |
|
||||
. .ci/minikube-test-setup/start_services.sh
|
||||
./run_tests.sh -integration test/integration -- --num-shards=4 --shard-id=${{ matrix.chunk }}
|
||||
./run_tests.sh --coverage -integration test/integration -- --num-shards=4 --shard-id=${{ matrix.chunk }}
|
||||
working-directory: 'galaxy root'
|
||||
- uses: codecov/codecov-action@v2
|
||||
with:
|
||||
flags: integration
|
||||
working-directory: 'galaxy root'
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: failure()
|
||||
with:
|
||||
|
||||
@@ -6,6 +6,9 @@ on:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- 'doc/**'
|
||||
schedule:
|
||||
# Run at midnight UTC every Tuesday
|
||||
- cron: '0 0 * * 2'
|
||||
env:
|
||||
GALAXY_SKIP_CLIENT_BUILD: '0'
|
||||
GALAXY_TEST_DBURI: 'postgresql://postgres:postgres@localhost:5432/galaxy?client_encoding=utf8'
|
||||
@@ -13,6 +16,7 @@ env:
|
||||
GALAXY_TEST_SELENIUM_RETRIES: 1
|
||||
YARN_INSTALL_OPTS: --frozen-lockfile
|
||||
GALAXY_CONFIG_SQLALCHEMY_WARN_20: '1'
|
||||
GALAXY_DEPENDENCIES_INSTALL_WEASYPRINT: '1'
|
||||
concurrency:
|
||||
group: integration-selenium-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
@@ -33,6 +37,10 @@ jobs:
|
||||
ports:
|
||||
- 5432:5432
|
||||
steps:
|
||||
- if: github.event_name == 'schedule'
|
||||
run: |
|
||||
echo "GALAXY_CONFIG_OVERRIDE_METADATA_STRATEGY=extended" >> $GITHUB_ENV
|
||||
echo "GALAXY_CONFIG_OVERRIDE_OUTPUTS_TO_WORKING_DIRECTORY=true" >> $GITHUB_ENV
|
||||
- name: Prune unused docker image, volumes and containers
|
||||
run: docker system prune -a -f
|
||||
- uses: actions/checkout@v2
|
||||
@@ -53,15 +61,19 @@ jobs:
|
||||
- name: Cache galaxy venv
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: .venv
|
||||
path: 'galaxy root/.venv'
|
||||
key: gxy-venv-${{ runner.os }}-${{ steps.full-python-version.outputs.version }}-${{ hashFiles('galaxy root/requirements.txt') }}-integration-selenium
|
||||
- uses: mvdbeek/gha-yarn-cache@master
|
||||
with:
|
||||
yarn-lock-file: 'galaxy root/client/yarn.lock'
|
||||
- uses: nanasess/setup-chromedriver@master
|
||||
- name: Run tests
|
||||
run: ./run_tests.sh -integration test/integration_selenium
|
||||
run: ./run_tests.sh --coverage -integration test/integration_selenium
|
||||
working-directory: 'galaxy root'
|
||||
- uses: codecov/codecov-action@v2
|
||||
with:
|
||||
flags: integration-selenium
|
||||
working-directory: 'galaxy root'
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: failure()
|
||||
with:
|
||||
|
||||
@@ -3,9 +3,11 @@ on:
|
||||
push:
|
||||
paths:
|
||||
- 'client/**'
|
||||
- '.github/workflows/jest.yaml'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'client/**'
|
||||
- '.github/workflows/jest.yaml'
|
||||
concurrency:
|
||||
group: client-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
@@ -14,7 +16,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node: [14]
|
||||
node: [16]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup node
|
||||
|
||||
@@ -3,9 +3,13 @@ on:
|
||||
push:
|
||||
paths:
|
||||
- '**.py'
|
||||
- '.github/workflows/lint.yaml'
|
||||
- lib/galaxy/dependencies/**
|
||||
pull_request:
|
||||
paths:
|
||||
- '**.py'
|
||||
- '.github/workflows/lint.yaml'
|
||||
- lib/galaxy/dependencies/**
|
||||
concurrency:
|
||||
group: py-lint-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
@@ -43,3 +47,5 @@ jobs:
|
||||
run: tox -e lint_docstring_include_list
|
||||
- name: Run mypy checks
|
||||
run: tox -e mypy
|
||||
- uses: psf/black@stable
|
||||
- uses: jamescurtin/isort-action@master
|
||||
|
||||
@@ -10,6 +10,9 @@ on:
|
||||
- 'client/**'
|
||||
- 'doc/**'
|
||||
- 'lib/galaxy_test/selenium/**'
|
||||
schedule:
|
||||
# Run at midnight UTC every Tuesday
|
||||
- cron: '0 0 * * 2'
|
||||
env:
|
||||
GALAXY_TEST_DBURI: 'postgresql://postgres:postgres@localhost:5432/galaxy?client_encoding=utf8'
|
||||
concurrency:
|
||||
@@ -32,6 +35,10 @@ jobs:
|
||||
ports:
|
||||
- 5432:5432
|
||||
steps:
|
||||
- if: github.event_name == 'schedule'
|
||||
run: |
|
||||
echo "GALAXY_CONFIG_OVERRIDE_METADATA_STRATEGY=extended" >> $GITHUB_ENV
|
||||
echo "GALAXY_CONFIG_OVERRIDE_OUTPUTS_TO_WORKING_DIRECTORY=true" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
path: 'galaxy root'
|
||||
@@ -50,7 +57,7 @@ jobs:
|
||||
- name: Cache galaxy venv
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: .venv
|
||||
path: 'galaxy root/.venv'
|
||||
key: gxy-venv-${{ runner.os }}-${{ steps.full-python-version.outputs.version }}-${{ hashFiles('galaxy root/requirements.txt') }}-performance
|
||||
- name: Run tests
|
||||
run: ./run_tests.sh --ci_test_metrics --structured_data_html --structured_data_report_file "test.json" --skip_flakey_fails -api lib/galaxy_test/performance
|
||||
|
||||
@@ -12,7 +12,6 @@ concurrency:
|
||||
group: reports-startup-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
jobs:
|
||||
|
||||
test:
|
||||
name: Reports startup test
|
||||
runs-on: ubuntu-latest
|
||||
@@ -41,16 +40,16 @@ jobs:
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: pip-cache-${{ matrix.python-version }}-${{ hashFiles('galaxy root/requirements.txt') }}
|
||||
- name: Cache tox env
|
||||
- name: Cache galaxy venv
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: .tox
|
||||
key: tox-cache-${{ runner.os }}-${{ steps.full-python-version.outputs.version }}-${{ hashFiles('galaxy root/requirements.txt') }}-reports-startup
|
||||
path: 'galaxy root/.venv'
|
||||
key: gxy-venv-${{ runner.os }}-${{ steps.full-python-version.outputs.version }}-${{ hashFiles('galaxy root/requirements.txt') }}-reports-startup
|
||||
- uses: mvdbeek/gha-yarn-cache@master
|
||||
with:
|
||||
yarn-lock-file: 'galaxy root/client/yarn.lock'
|
||||
- name: Install tox
|
||||
run: pip install tox
|
||||
- name: run tests
|
||||
- name: Run tests
|
||||
run: tox -e reports_startup
|
||||
working-directory: 'galaxy root'
|
||||
|
||||
@@ -6,6 +6,9 @@ on:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- 'doc/**'
|
||||
schedule:
|
||||
# Run at midnight UTC every Tuesday
|
||||
- cron: '0 0 * * 2'
|
||||
env:
|
||||
GALAXY_CONFIG_GALAXY_URL_PREFIX: '/galaxypf'
|
||||
GALAXY_TEST_DBURI: 'postgresql://postgres:postgres@localhost:5432/galaxy?client_encoding=utf8'
|
||||
@@ -36,6 +39,10 @@ jobs:
|
||||
ports:
|
||||
- 5432:5432
|
||||
steps:
|
||||
- if: github.event_name == 'schedule'
|
||||
run: |
|
||||
echo "GALAXY_CONFIG_OVERRIDE_METADATA_STRATEGY=extended" >> $GITHUB_ENV
|
||||
echo "GALAXY_CONFIG_OVERRIDE_OUTPUTS_TO_WORKING_DIRECTORY=true" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
path: 'galaxy root'
|
||||
@@ -54,15 +61,19 @@ jobs:
|
||||
- name: Cache galaxy venv
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: .venv
|
||||
path: 'galaxy root/.venv'
|
||||
key: gxy-venv-${{ runner.os }}-${{ steps.full-python-version.outputs.version }}-${{ hashFiles('galaxy root/requirements.txt') }}-selenium
|
||||
- uses: mvdbeek/gha-yarn-cache@master
|
||||
with:
|
||||
yarn-lock-file: 'galaxy root/client/yarn.lock'
|
||||
- uses: nanasess/setup-chromedriver@master
|
||||
- name: Run tests
|
||||
run: ./run_tests.sh -selenium lib/galaxy_test/selenium -- --num-shards=3 --shard-id=${{ matrix.chunk }}
|
||||
run: ./run_tests.sh --coverage -selenium lib/galaxy_test/selenium -- --num-shards=3 --shard-id=${{ matrix.chunk }}
|
||||
working-directory: 'galaxy root'
|
||||
- uses: codecov/codecov-action@v2
|
||||
with:
|
||||
flags: selenium
|
||||
working-directory: 'galaxy root'
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: failure()
|
||||
with:
|
||||
|
||||
@@ -6,8 +6,12 @@ on:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- 'doc/**'
|
||||
schedule:
|
||||
# Run at midnight UTC every Tuesday
|
||||
- cron: '0 0 * * 2'
|
||||
env:
|
||||
GALAXY_CONFIG_GALAXY_URL_PREFIX: '/galaxypf'
|
||||
GALAXY_CONFIG_USE_LEGACY_HISTORY: false
|
||||
GALAXY_TEST_DBURI: 'postgresql://postgres:postgres@localhost:5432/galaxy?client_encoding=utf8'
|
||||
GALAXY_TEST_RAISE_EXCEPTION_ON_HISTORYLESS_HDA: '1'
|
||||
GALAXY_TEST_SELENIUM_RETRIES: 1
|
||||
@@ -37,6 +41,10 @@ jobs:
|
||||
ports:
|
||||
- 5432:5432
|
||||
steps:
|
||||
- if: github.event_name == 'schedule'
|
||||
run: |
|
||||
echo "GALAXY_CONFIG_OVERRIDE_METADATA_STRATEGY=extended" >> $GITHUB_ENV
|
||||
echo "GALAXY_CONFIG_OVERRIDE_OUTPUTS_TO_WORKING_DIRECTORY=true" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
path: 'galaxy root'
|
||||
@@ -55,15 +63,19 @@ jobs:
|
||||
- name: Cache galaxy venv
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: .venv
|
||||
path: 'galaxy root/.venv'
|
||||
key: gxy-venv-${{ runner.os }}-${{ steps.full-python-version.outputs.version }}-${{ hashFiles('galaxy root/requirements.txt') }}-selenium-beta
|
||||
- uses: mvdbeek/gha-yarn-cache@master
|
||||
with:
|
||||
yarn-lock-file: 'galaxy root/client/yarn.lock'
|
||||
- uses: nanasess/setup-chromedriver@master
|
||||
- name: Run tests
|
||||
run: ./run_tests.sh -selenium lib/galaxy_test/selenium -- --num-shards=3 --shard-id=${{ matrix.chunk }}
|
||||
run: ./run_tests.sh --coverage -selenium lib/galaxy_test/selenium -- --num-shards=3 --shard-id=${{ matrix.chunk }}
|
||||
working-directory: 'galaxy root'
|
||||
- uses: codecov/codecov-action@v2
|
||||
with:
|
||||
flags: selenium-beta-history
|
||||
working-directory: 'galaxy root'
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: failure()
|
||||
with:
|
||||
|
||||
@@ -2,11 +2,15 @@ name: Test Galaxy release script
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- '.github/workflows/test_galaxy_release.yaml'
|
||||
- lib/galaxy/dependencies/**
|
||||
- lib/galaxy/version.py
|
||||
- scripts/release.sh
|
||||
- test/release.sh
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/test_galaxy_release.yaml'
|
||||
- lib/galaxy/dependencies/**
|
||||
- lib/galaxy/version.py
|
||||
- scripts/release.sh
|
||||
- test/release.sh
|
||||
|
||||
@@ -50,7 +50,7 @@ jobs:
|
||||
- name: Cache galaxy venv
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: .venv
|
||||
path: 'galaxy root/.venv'
|
||||
key: gxy-venv-${{ runner.os }}-${{ steps.full-python-version.outputs.version }}-${{ hashFiles('galaxy root/requirements.txt') }}-toolshed
|
||||
- name: Run tests
|
||||
run: './run_tests.sh -toolshed'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: API (legacy paste) tests
|
||||
name: Unit w/postgres tests
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
@@ -11,24 +11,21 @@ on:
|
||||
- 'doc/**'
|
||||
- 'lib/galaxy_test/selenium/**'
|
||||
env:
|
||||
GALAXY_TEST_DBURI: 'postgresql://postgres:postgres@localhost:5432/galaxy?client_encoding=utf8'
|
||||
GALAXY_TEST_USE_UVICORN: false
|
||||
GALAXY_TEST_RAISE_EXCEPTION_ON_HISTORYLESS_HDA: '1'
|
||||
GALAXY_CONFIG_SQLALCHEMY_WARN_20: '1'
|
||||
GALAXY_TEST_DBURI: 'postgresql://postgres:postgres@localhost:5432/postgres?client_encoding=utf8' # using postgres as the db
|
||||
concurrency:
|
||||
group: api-legacy-${{ github.ref }}
|
||||
group: py-unit-postgres-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
jobs:
|
||||
test:
|
||||
name: Test
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version: ['3.7']
|
||||
services:
|
||||
postgres:
|
||||
# Run tests on the oldest Galaxy-supported version
|
||||
image: postgres:9.6
|
||||
image: postgres:13
|
||||
env:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
@@ -54,13 +51,8 @@ jobs:
|
||||
- name: Cache galaxy venv
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: .venv
|
||||
key: gxy-venv-${{ runner.os }}-${{ steps.full-python-version.outputs.version }}-${{ hashFiles('galaxy root/requirements.txt') }}-api-paste
|
||||
- name: Run tests
|
||||
run: ./run_tests.sh --skip_flakey_fails -api
|
||||
path: 'galaxy root/.venv'
|
||||
key: gxy-venv-${{ runner.os }}-${{ steps.full-python-version.outputs.version }}-${{ hashFiles('galaxy root/requirements.txt') }}-unit-postgres
|
||||
- name: Run migration tests
|
||||
run: ./run_tests.sh -unit test/unit/data/model/migrations/test_migrations.py
|
||||
working-directory: 'galaxy root'
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: failure()
|
||||
with:
|
||||
name: API (legacy paste) test results (${{ matrix.python-version }})
|
||||
path: 'galaxy root/run_api_tests.html'
|
||||
@@ -37,18 +37,20 @@ jobs:
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: pip-cache-${{ matrix.python-version }}-${{ hashFiles('galaxy root/requirements.txt') }}
|
||||
- name: Cache tox env
|
||||
- name: Cache galaxy venv
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: .tox
|
||||
key: tox-cache-${{ runner.os }}-${{ steps.full-python-version.outputs.version }}-${{ hashFiles('galaxy root/requirements.txt') }}-unit
|
||||
path: 'galaxy root/.venv'
|
||||
key: gxy-venv-${{ runner.os }}-${{ steps.full-python-version.outputs.version }}-${{ hashFiles('galaxy root/requirements.txt') }}-unit
|
||||
- name: Install ffmpeg
|
||||
run: sudo apt-get update && sudo apt-get -y install ffmpeg
|
||||
- name: Install tox
|
||||
run: pip install tox
|
||||
- name: Run tests
|
||||
run: tox -e unit
|
||||
run: ./run_tests.sh --coverage -u
|
||||
working-directory: 'galaxy root'
|
||||
- uses: codecov/codecov-action@v2
|
||||
with:
|
||||
flags: py-unit
|
||||
working-directory: 'galaxy root'
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: failure()
|
||||
with:
|
||||
|
||||
+6
-21
@@ -1,4 +1,5 @@
|
||||
.hg*
|
||||
|
||||
# Downloaded and locally built eggs
|
||||
eggs
|
||||
scripts/scramble/build
|
||||
@@ -21,27 +22,7 @@ lib/galaxy/web/framework/static/scripts
|
||||
lib/galaxy/web/framework/static/style
|
||||
|
||||
# Database stuff
|
||||
database/*.sqlite
|
||||
database/beaker_sessions
|
||||
database/cache
|
||||
database/citations
|
||||
database/community_files
|
||||
database/compiled_templates
|
||||
database/dependencies
|
||||
database/files
|
||||
database/job_working_directory
|
||||
database/jobs_directory
|
||||
database/mulled
|
||||
database/object_store_cache
|
||||
database/objects
|
||||
database/openid_consumer_cache
|
||||
database/pbs
|
||||
database/shed_tools
|
||||
database/test_errors
|
||||
database/tmp
|
||||
database/tool_cache
|
||||
database/tool_search_index
|
||||
database/tool_recommendation_model.hdf5
|
||||
/database/
|
||||
|
||||
|
||||
# Python bytecode
|
||||
@@ -53,6 +34,9 @@ database/tool_recommendation_model.hdf5
|
||||
*.pid
|
||||
celerybeat-schedule
|
||||
|
||||
# Celery state files
|
||||
celerybeat-schedule.*
|
||||
|
||||
# Tool Shed Runtime Files
|
||||
tool_shed_webapp.lock
|
||||
tool_shed_webapp.log
|
||||
@@ -145,6 +129,7 @@ test/unit/**.log
|
||||
.vscode
|
||||
client/**/jsconfig.json
|
||||
vetur.config.js
|
||||
.pre-commit-config.yaml
|
||||
|
||||
# Chrom len files
|
||||
*.len
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
[settings]
|
||||
extend_skip=doc/source/conf.py,lib/galaxy/util/jstree.py
|
||||
force_alphabetical_sort_within_sections=true
|
||||
# Override force_grid_wrap value from profile=black, but black is still happy
|
||||
force_grid_wrap=2
|
||||
# Same line length as for black
|
||||
line_length=120
|
||||
no_lines_before=LOCALFOLDER
|
||||
profile=black
|
||||
reverse_relative=true
|
||||
skip_gitignore=true
|
||||
# Make isort run faster by skipping database
|
||||
skip_glob=database/*
|
||||
src_paths=lib
|
||||
+8
-2
@@ -22,6 +22,7 @@ ARG STAGE1_BASE=python:3.7-slim
|
||||
ARG FINAL_STAGE_BASE=$STAGE1_BASE
|
||||
ARG GALAXY_USER=galaxy
|
||||
ARG GALAXY_PLAYBOOK_REPO=https://github.com/galaxyproject/galaxy-docker-k8s
|
||||
ARG GALAXY_PLAYBOOK_BRANCH=v2.0.0
|
||||
|
||||
ARG GIT_COMMIT=unspecified
|
||||
ARG BUILD_DATE=unspecified
|
||||
@@ -34,6 +35,7 @@ FROM $STAGE1_BASE AS stage1
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
ARG SERVER_DIR
|
||||
ARG GALAXY_PLAYBOOK_REPO
|
||||
ARG GALAXY_PLAYBOOK_BRANCH
|
||||
|
||||
# Init Env
|
||||
ENV LC_ALL=en_US.UTF-8
|
||||
@@ -57,7 +59,7 @@ RUN set -xe; \
|
||||
WORKDIR /tmp/ansible
|
||||
RUN rm -rf *
|
||||
ENV LC_ALL en_US.UTF-8
|
||||
RUN git clone --depth 1 $GALAXY_PLAYBOOK_REPO galaxy-docker
|
||||
RUN git clone --depth 1 --branch $GALAXY_PLAYBOOK_BRANCH $GALAXY_PLAYBOOK_REPO galaxy-docker
|
||||
WORKDIR /tmp/ansible/galaxy-docker
|
||||
RUN ansible-galaxy install -r requirements.yml -p roles --force-with-deps
|
||||
|
||||
@@ -176,6 +178,10 @@ COPY --chown=$GALAXY_USER:$GALAXY_USER --from=server_build $ROOT_DIR .
|
||||
COPY --chown=$GALAXY_USER:$GALAXY_USER --from=client_build $SERVER_DIR/static ./server/static
|
||||
|
||||
WORKDIR $SERVER_DIR
|
||||
|
||||
# The data in version.json will be displayed in Galaxy's /api/version endpoint
|
||||
RUN printf "{\n \"git_commit\": \"$(cat GITREVISION)\",\n \"build_date\": \"$BUILD_DATE\",\n \"image_tag\": \"$IMAGE_TAG\"\n}\n" > version.json
|
||||
|
||||
EXPOSE 8080
|
||||
USER $GALAXY_USER
|
||||
|
||||
@@ -185,4 +191,4 @@ ENV GALAXY_CONFIG_CONDA_AUTO_INIT=False
|
||||
ENTRYPOINT ["tini", "--"]
|
||||
|
||||
# [optional] to run:
|
||||
CMD uwsgi --yaml config/galaxy.yml
|
||||
CMD galaxy
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
repos:
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 22.1.0
|
||||
hooks:
|
||||
- id: black
|
||||
language_version: python3.7
|
||||
- repo: https://gitlab.com/pycqa/flake8
|
||||
rev: 4.0.1
|
||||
hooks:
|
||||
- id: flake8
|
||||
- repo: https://github.com/pre-commit/mirrors-prettier
|
||||
rev: v2.6.0 # Use the sha or tag you want to point at
|
||||
hooks:
|
||||
- id: prettier
|
||||
types: [file]
|
||||
types_or: [javascript, jsx, ts, tsx, vue]
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.1.0 # Use the ref you want to point at
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: check-merge-conflict
|
||||
- id: check-symlinks
|
||||
- id: destroyed-symlinks
|
||||
- id: end-of-file-fixer
|
||||
- repo: https://github.com/detailyang/pre-commit-shell
|
||||
rev: v1.0.6
|
||||
hooks:
|
||||
- id: shell-lint
|
||||
args: [--format=json]
|
||||
- repo: https://github.com/python-jsonschema/check-jsonschema
|
||||
rev: 0.14.0
|
||||
hooks:
|
||||
- id: check-github-workflows
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: eslint
|
||||
name: client eslint
|
||||
language: system
|
||||
files: ^client/
|
||||
entry: make client-eslint-precommit
|
||||
types: [file]
|
||||
types_or: [javascript, jsx, ts, tsx, vue]
|
||||
pass_filenames: false
|
||||
# - repo: https://github.com/pycqa/isort
|
||||
# rev: 5.10.1
|
||||
# hooks:
|
||||
# - id: isort
|
||||
# name: isort (python)
|
||||
+7
-5
@@ -130,11 +130,13 @@ https://help.github.com/en/github/getting-started-with-github/git-and-github-lea
|
||||
- Galaxy follows [PEP-8](https://www.python.org/dev/peps/pep-0008/), with
|
||||
particular emphasis on readability being the ultimate goal:
|
||||
- 4 spaces (not tabs!) per indentation level
|
||||
- divergences from PEP-8 are listed in the `[flake8]` section of the `setup.cfg`
|
||||
file.
|
||||
- Python imports should be ordered following the
|
||||
[smarkets](https://github.com/PyCQA/flake8-import-order/blob/master/tests/test_cases/complete_smarkets.py)
|
||||
style.
|
||||
- divergences from PEP-8 are listed in the `[flake8]` section of the
|
||||
`setup.cfg` file
|
||||
- The Python code base is automatically formatted using
|
||||
[isort](https://pycqa.github.io/isort/) (for imports) and
|
||||
[black](https://black.readthedocs.io). To easily format your Python code
|
||||
before submitting your contribution, please either use `make diff-format`
|
||||
or run `isort FILE; black FILE` for each FILE you modify.
|
||||
- Python [docstrings](http://www.python.org/dev/peps/pep-0257/) need to be in
|
||||
[reStructured Text (RST)](https://docutils.sourceforge.io/rst.html) format and
|
||||
compatible with [Sphinx](https://www.sphinx-doc.org).
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
VENV?=.venv
|
||||
# Source virtualenv to execute command (flake8, sphinx, twine, etc...)
|
||||
IN_VENV=if [ -f "$(VENV)/bin/activate" ]; then . "$(VENV)/bin/activate"; fi;
|
||||
RELEASE_CURR:=22.01
|
||||
RELEASE_CURR:=22.05
|
||||
RELEASE_UPSTREAM:=upstream
|
||||
TARGET_BRANCH=$(RELEASE_UPSTREAM)/dev
|
||||
CONFIG_MANAGE=$(IN_VENV) python lib/galaxy/config/config_manage.py
|
||||
@@ -38,8 +38,12 @@ docs-develop: ## Fast doc generation and more warnings (for development)
|
||||
setup-venv:
|
||||
if [ ! -f $(VENV)/bin/activate ]; then bash scripts/common_startup.sh --dev-wheels; fi
|
||||
|
||||
diff-format:
|
||||
$(IN_VENV) darker -r $(TARGET_BRANCH)
|
||||
diff-format: ## Format Python code changes since last commit
|
||||
$(IN_VENV) darker .
|
||||
|
||||
format: ## Format Python code base
|
||||
$(IN_VENV) isort .
|
||||
$(IN_VENV) black .
|
||||
|
||||
list-dependency-updates: setup-venv
|
||||
$(IN_VENV) pip list --outdated --format=columns
|
||||
@@ -60,9 +64,6 @@ open-docs: docs _open-docs ## generate Sphinx HTML documentation and open in bro
|
||||
open-project: ## open project on github
|
||||
$(OPEN_RESOURCE) $(PROJECT_URL)
|
||||
|
||||
uwsgi-rebuild-validation: ## rebuild uwsgi_config.yml kwalify schema against latest uwsgi master.
|
||||
$(CONFIG_MANAGE) build_uwsgi_yaml
|
||||
|
||||
tool-shed-config-validate: ## validate tool shed YAML configuration file
|
||||
$(CONFIG_MANAGE) validate tool_shed
|
||||
|
||||
@@ -188,6 +189,9 @@ client-dev-server: node-deps ## Starts a webpack dev server for client developme
|
||||
client-test: node-deps ## Run JS unit tests
|
||||
cd client && yarn run test
|
||||
|
||||
client-eslint-precommit: node-deps # Client linting for pre-commit hook; skips glob input and takes specific paths
|
||||
cd client && yarn run eslint-precommit
|
||||
|
||||
client-eslint: node-deps # Run client linting
|
||||
cd client && yarn run eslint
|
||||
|
||||
|
||||
+2
-2
@@ -24,12 +24,12 @@ Community support is available at `Galaxy Help <https://help.galaxyproject.org/>
|
||||
Galaxy Quickstart
|
||||
=================
|
||||
|
||||
Galaxy requires Python 3.6 . To check your Python version, run:
|
||||
Galaxy requires Python 3.7 . To check your Python version, run:
|
||||
|
||||
.. code:: console
|
||||
|
||||
$ python -V
|
||||
Python 3.6.10
|
||||
Python 3.7.6
|
||||
|
||||
Start Galaxy:
|
||||
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
src/qunit
|
||||
src/mocha
|
||||
src/libs
|
||||
src/nls
|
||||
src/legacy
|
||||
@@ -1,38 +1,37 @@
|
||||
module.exports = {
|
||||
extends: [
|
||||
{
|
||||
"root": true,
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:vue/strongly-recommended",
|
||||
"plugin:vue/strongly-recommended"
|
||||
//"airbnb-base", eventually
|
||||
],
|
||||
env: {
|
||||
browser: true,
|
||||
commonjs: true,
|
||||
es6: true,
|
||||
node: true,
|
||||
jest: true,
|
||||
"env": {
|
||||
"browser": true,
|
||||
"commonjs": true,
|
||||
"es6": true,
|
||||
"node": true,
|
||||
"jest": true
|
||||
},
|
||||
parserOptions: {
|
||||
parser: "babel-eslint",
|
||||
sourceType: "module",
|
||||
"parserOptions": {
|
||||
"parser": "@babel/eslint-parser",
|
||||
"babelOptions": { "configFile": "./babel.config.json" }
|
||||
},
|
||||
rules: {
|
||||
"rules": {
|
||||
// Standard rules
|
||||
"no-console": "off",
|
||||
"no-unused-vars": ["error", { args: "none" }],
|
||||
"no-unused-vars": ["error", { "args": "none" }],
|
||||
"prefer-const": "error",
|
||||
"one-var": ["error", "never"],
|
||||
"curly": "error",
|
||||
|
||||
"vue/valid-v-slot": "error",
|
||||
"vue/v-slot-style": ["error", { atComponent: "v-slot", default: "v-slot", named: "longform" }],
|
||||
|
||||
// Now in strongly-recommended, enforce instead of warn.
|
||||
"vue/attribute-hyphenation": "error",
|
||||
"vue/v-slot-style": ["error", { "atComponent": "v-slot", "default": "v-slot", "named": "longform" }],
|
||||
|
||||
// Vue TODO (enable these)
|
||||
"vue/require-default-prop": "warn",
|
||||
"vue/require-prop-types": "warn",
|
||||
"vue/prop-name-casing": "warn",
|
||||
"vue/multi-word-component-names": "warn",
|
||||
|
||||
// Prettier compromises/workarounds -- mostly #wontfix?
|
||||
"vue/html-indent": "off",
|
||||
@@ -41,6 +40,7 @@ module.exports = {
|
||||
"vue/singleline-html-element-content-newline": "off",
|
||||
"vue/multiline-html-element-content-newline": "off",
|
||||
"vue/html-closing-bracket-newline": "off",
|
||||
"vue/html-closing-bracket-spacing": "off",
|
||||
"vue/html-closing-bracket-spacing": "off"
|
||||
},
|
||||
};
|
||||
"ignorePatterns": ["src/qunit", "src/mocha", "src/libs", "src/nls", "src/legacy"]
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
14.15.0
|
||||
16.13.2
|
||||
|
||||
+2
-3
@@ -75,8 +75,7 @@ This will start up an extra client development server running on port 8081.
|
||||
Open your browser to http://localhost:8081 (instead of the default 8080 that
|
||||
Galaxy would run on), and you should see Galaxy like normal. Except now, when
|
||||
you change client code it'll automatically rebuild *and* reload the relevant
|
||||
portion of the application for you. Note that unlike previous versions of this
|
||||
functionality, it is no longer required to use uWSGI for this. Lastly, if you
|
||||
portion of the application for you. Lastly, if you
|
||||
are running Galaxy at a location other than the default, you can specify a
|
||||
different proxy target (in this example, port 8000) using the GALAXY_URL
|
||||
environment variable:
|
||||
@@ -91,7 +90,7 @@ Sometimes you want to run your local UI against a remote Galaxy server. This is
|
||||
|
||||
Galaxy uses Sass for its styling, which is a superset of CSS that compiles down
|
||||
to regular CSS. Most Galaxy styling source (.scss) files are kept in
|
||||
`client/src/style/scss. There are additionally style blocks alongside some Vue
|
||||
`client/src/style/scss`. There are additionally style blocks alongside some Vue
|
||||
components -- styles that are particular to that individual component and do
|
||||
not apply site-wide.
|
||||
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
The \*.md files in this directory are rendered as a style guide generated when
|
||||
running ```yarn run styleguide```.
|
||||
|
||||
For more details see [Vue
|
||||
Styleguidist](https://github.com/vue-styleguidist/vue-styleguidist).
|
||||
@@ -1,15 +0,0 @@
|
||||
/**
|
||||
* Component root for rendering inside the styleguide, injects vuex store and other common elements
|
||||
* into each component example section.
|
||||
*/
|
||||
|
||||
import store from "../src/store";
|
||||
|
||||
export default (previewComponent) => {
|
||||
return {
|
||||
store,
|
||||
render(h) {
|
||||
return h(previewComponent);
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -1,142 +0,0 @@
|
||||
/**
|
||||
* Functions for generating the sections for the style guide. Has the old style doc glob and a
|
||||
* recursive tree-walker that looks through the components folder and tries to arrange found
|
||||
* markdown docs in a tree.
|
||||
*/
|
||||
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
const glob = require("glob");
|
||||
const { humanize, titleize } = require("underscore.string");
|
||||
|
||||
/**
|
||||
* Gets the table of contents sections for documentation in the components folder.
|
||||
*
|
||||
* @param {string} rootPath absolute path to components folder
|
||||
* @return {Object} Rootnode, section map, childMap
|
||||
*/
|
||||
function getDocSections(rootPath, options = {}) {
|
||||
const { ignore = [], docSelector = "*.@(vue|md)" } = options;
|
||||
|
||||
const sections = new Map(); // absolute section directory path -> section object
|
||||
const childToParent = new Map(); // absolute child path -> absolute parent path
|
||||
|
||||
// create root node
|
||||
const rootNode = newSection(rootPath, ignore);
|
||||
sections.set(rootPath, rootNode);
|
||||
childToParent.set(rootPath, null);
|
||||
|
||||
// get all matching doc files
|
||||
const selector = path.join(rootPath, "**", docSelector);
|
||||
const allFiles = glob.sync(selector, { ignore });
|
||||
|
||||
allFiles.forEach((file) => {
|
||||
const p = path.parse(file);
|
||||
|
||||
// intermediate dir names between this dir and root
|
||||
const relPath = path.relative(rootPath, p.dir);
|
||||
const midFolders = relPath.split(path.sep);
|
||||
|
||||
// build intermediate parent sections in the event that this is deeply-nested
|
||||
while (midFolders.length) {
|
||||
const sectionPath = path.join(rootPath, ...midFolders);
|
||||
if (!sections.has(sectionPath)) {
|
||||
// create new section
|
||||
const section = newSection(sectionPath, ignore);
|
||||
sections.set(sectionPath, section);
|
||||
// register relationship for tree-build later
|
||||
const parentPath = path.join(sectionPath, "..");
|
||||
childToParent.set(sectionPath, parentPath);
|
||||
}
|
||||
midFolders.pop();
|
||||
}
|
||||
|
||||
// if it's a MD file, add to parent as a subsection
|
||||
const section = sections.get(p.dir);
|
||||
if (section && isSubsection(section, file)) {
|
||||
section.children.add({
|
||||
name: titleize(humanize(p.name)),
|
||||
content: file,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// assemble recursive section tree under rootNode
|
||||
buildSectionTree(sections, childToParent);
|
||||
|
||||
// rootNode is the main result, returning the maps so the user has the option to manipulate the
|
||||
// tree before handing it over to the styleguide configs
|
||||
return { sections, childToParent, rootNode };
|
||||
}
|
||||
|
||||
function isSubsection(section, file) {
|
||||
const p = path.parse(file);
|
||||
|
||||
// it's not a subsection if it's not a markdown file
|
||||
if (p.ext !== ".md") return false;
|
||||
|
||||
// it's not a subsection if it's the same file as the section content
|
||||
if (file === section.content) return false;
|
||||
|
||||
// it's not a subsection if it's an example for an existing component
|
||||
const matchingComponentPath = path.join(p.dir, `${p.name}.vue`);
|
||||
const componentExists = fs.existsSync(matchingComponentPath);
|
||||
if (componentExists) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new section in the TOC from passed path.
|
||||
*
|
||||
* @param {string} dir absolute directory path
|
||||
* @return {Object} section object
|
||||
*/
|
||||
function newSection(dir, ignore = []) {
|
||||
const section = {
|
||||
name: titleize(humanize(path.basename(dir))),
|
||||
components: () => glob.sync(path.join(dir, "*.vue"), { ignore }),
|
||||
children: new Set(),
|
||||
sectionDepth: 1,
|
||||
};
|
||||
|
||||
// summary doc is readme/docs/index.md
|
||||
const summarySelector = path.join(dir, "@(readme|docs|index).md");
|
||||
const summaryDocs = glob.sync(summarySelector, { ignore, nocase: true });
|
||||
if (summaryDocs.length) {
|
||||
section.content = summaryDocs[0];
|
||||
}
|
||||
|
||||
return section;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns the pair of maps into a nested object for use in the styleguide config script. Operates on
|
||||
* sections array by reference.
|
||||
*
|
||||
* @param {Map} sections Map of path -> section object
|
||||
* @param {Map} childToParent Map of childPath -> parentPath
|
||||
*/
|
||||
function buildSectionTree(sections, childToParent) {
|
||||
// add each child to its parent
|
||||
for (const [childPath, parentPath] of childToParent) {
|
||||
const child = sections.get(childPath);
|
||||
const parent = sections.get(parentPath);
|
||||
if (child && parent && parent.children) {
|
||||
parent.children.add(child);
|
||||
}
|
||||
}
|
||||
// convert child Sets to arrays now that de-dupe not required
|
||||
for (const section of sections.values()) {
|
||||
section.sections = Array.from(section.children).sort((a, b) => a.name.localeCompare(b.name));
|
||||
delete section.children;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
// public function
|
||||
getDocSections,
|
||||
// exporting these in case user wants to tweak the result tree and rebuild it
|
||||
buildSectionTree,
|
||||
newSection,
|
||||
};
|
||||
@@ -1,379 +0,0 @@
|
||||
## Buttons
|
||||
|
||||
Regular buttons
|
||||
|
||||
```vue
|
||||
<div>
|
||||
<button type="button" class="btn btn-primary">Primary</button>
|
||||
<button type="button" class="btn btn-secondary">Secondary</button>
|
||||
<button type="button" class="btn btn-success">Success</button>
|
||||
<button type="button" class="btn btn-info">Info</button>
|
||||
<button type="button" class="btn btn-warning">Warning</button>
|
||||
<button type="button" class="btn btn-danger">Danger</button>
|
||||
<button type="button" class="btn btn-link">Link</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
Disabled
|
||||
|
||||
```vue
|
||||
<div>
|
||||
<button type="button" class="btn btn-primary disabled">Primary</button>
|
||||
<button type="button" class="btn btn-secondary disabled">Secondary</button>
|
||||
<button type="button" class="btn btn-success disabled">Success</button>
|
||||
<button type="button" class="btn btn-info disabled">Info</button>
|
||||
<button type="button" class="btn btn-warning disabled">Warning</button>
|
||||
<button type="button" class="btn btn-danger disabled">Danger</button>
|
||||
<button type="button" class="btn btn-link disabled">Link</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
```vue
|
||||
<div>
|
||||
<button type="button" class="btn btn-outline-primary">Primary</button>
|
||||
<button type="button" class="btn btn-outline-secondary">Secondary</button>
|
||||
<button type="button" class="btn btn-outline-success">Success</button>
|
||||
<button type="button" class="btn btn-outline-info">Info</button>
|
||||
<button type="button" class="btn btn-outline-warning">Warning</button>
|
||||
<button type="button" class="btn btn-outline-danger">Danger</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
```vue
|
||||
<div class="btn-group" role="group" aria-label="Button group with nested dropdown">
|
||||
<button type="button" class="btn btn-primary">Primary</button>
|
||||
<div class="btn-group" role="group">
|
||||
<button id="btnGroupDrop1" type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></button>
|
||||
<div class="dropdown-menu" aria-labelledby="btnGroupDrop1">
|
||||
<a class="dropdown-item" href="javascript:void(0)">Dropdown link</a>
|
||||
<a class="dropdown-item" href="javascript:void(0)">Dropdown link</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
```vue
|
||||
<div class="btn-group" role="group" aria-label="Button group with nested dropdown">
|
||||
<button type="button" class="btn btn-success">Success</button>
|
||||
<div class="btn-group" role="group">
|
||||
<button id="btnGroupDrop2" type="button" class="btn btn-success dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></button>
|
||||
<div class="dropdown-menu" aria-labelledby="btnGroupDrop2">
|
||||
<a class="dropdown-item" href="javascript:void(0)">Dropdown link</a>
|
||||
<a class="dropdown-item" href="javascript:void(0)">Dropdown link</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
```vue
|
||||
<div class="btn-group" role="group" aria-label="Button group with nested dropdown">
|
||||
<button type="button" class="btn btn-info">Info</button>
|
||||
<div class="btn-group" role="group">
|
||||
<button id="btnGroupDrop3" type="button" class="btn btn-info dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></button>
|
||||
<div class="dropdown-menu" aria-labelledby="btnGroupDrop3">
|
||||
<a class="dropdown-item" href="javascript:void(0)">Dropdown link</a>
|
||||
<a class="dropdown-item" href="javascript:void(0)">Dropdown link</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
```vue
|
||||
<div class="btn-group" role="group" aria-label="Button group with nested dropdown">
|
||||
<button type="button" class="btn btn-danger">Danger</button>
|
||||
<div class="btn-group" role="group">
|
||||
<button id="btnGroupDrop4" type="button" class="btn btn-danger dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></button>
|
||||
<div class="dropdown-menu" aria-labelledby="btnGroupDrop4">
|
||||
<a class="dropdown-item" href="javascript:void(0)">Dropdown link</a>
|
||||
<a class="dropdown-item" href="javascript:void(0)">Dropdown link</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
```vue
|
||||
<div>
|
||||
<button type="button" class="btn btn-primary btn-lg">Large button</button>
|
||||
<button type="button" class="btn btn-primary">Default button</button>
|
||||
<button type="button" class="btn btn-primary btn-sm">Small button</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Alerts
|
||||
|
||||
```vue
|
||||
<div class="alert alert-dismissable alert-warning">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
<h4>Warning!</h4>
|
||||
<p>Best check yo self, you're not looking too good. Nulla vitae elit libero, a pharetra augue. Praesent commodo cursus magna, <a href="javascript:void(0)" class="alert-link">vel scelerisque nisl consectetur et</a>.</p>
|
||||
</div>
|
||||
```
|
||||
|
||||
```vue
|
||||
<div class="alert alert-dismissable alert-danger">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
<strong>Oh snap!</strong> <a href="javascript:void(0)" class="alert-link">Change a few things up</a> and try submitting again.
|
||||
</div>
|
||||
```
|
||||
|
||||
```vue
|
||||
<div class="alert alert-dismissable alert-success">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
<strong>Well done!</strong> You successfully read <a href="javascript:void(0)" class="alert-link">this important alert message</a>.
|
||||
</div>
|
||||
```
|
||||
|
||||
```vue
|
||||
<div class="alert alert-dismissable alert-info">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
<strong>Heads up!</strong> This <a href="javascript:void(0)" class="alert-link">alert needs your attention</a>, but it's not super important.
|
||||
</div>
|
||||
```
|
||||
|
||||
## Badges
|
||||
|
||||
```vue
|
||||
<div>
|
||||
<span class="badge badge-primary">Primary</span>
|
||||
<span class="badge badge-secondary">Secondary</span>
|
||||
<span class="badge badge-success">Success</span>
|
||||
<span class="badge badge-warning">Warning</span>
|
||||
<span class="badge badge-danger">Danger</span>
|
||||
<span class="badge badge-info">Info</span>
|
||||
</div>
|
||||
```
|
||||
|
||||
```vue
|
||||
<div class="">
|
||||
<ul class="nav nav-pills">
|
||||
<li class="active"><a href="javascript:void(0)">Home <span class="badge">42</span></a></li>
|
||||
<li><a href="javascript:void(0)">Profile <span class="badge-pill"></span></a></li>
|
||||
<li><a href="javascript:void(0)">Messages <span class="badge-pill">3</span></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Tables
|
||||
|
||||
```vue
|
||||
<div class="">
|
||||
<table class="table table-striped table-bordered table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Column heading</th>
|
||||
<th>Column heading</th>
|
||||
<th>Column heading</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>Column content</td>
|
||||
<td>Column content</td>
|
||||
<td>Column content</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>2</td>
|
||||
<td>Column content</td>
|
||||
<td>Column content</td>
|
||||
<td>Column content</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>3</td>
|
||||
<td>Column content</td>
|
||||
<td>Column content</td>
|
||||
<td>Column content</td>
|
||||
</tr>
|
||||
<tr class="table-success">
|
||||
<td>4</td>
|
||||
<td>Column content</td>
|
||||
<td>Column content</td>
|
||||
<td>Column content</td>
|
||||
</tr>
|
||||
<tr class="table-danger">
|
||||
<td>5</td>
|
||||
<td>Column content</td>
|
||||
<td>Column content</td>
|
||||
<td>Column content</td>
|
||||
</tr>
|
||||
<tr class="table-warning">
|
||||
<td>6</td>
|
||||
<td>Column content</td>
|
||||
<td>Column content</td>
|
||||
<td>Column content</td>
|
||||
</tr>
|
||||
<tr class="table-active">
|
||||
<td>7</td>
|
||||
<td>Column content</td>
|
||||
<td>Column content</td>
|
||||
<td>Column content</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Cards
|
||||
|
||||
```vue
|
||||
<div class="row">
|
||||
<div class="col-lg-4">
|
||||
<div class="bs-component">
|
||||
<div class="card text-white bg-primary mb-3" style="max-width: 20rem;">
|
||||
<div class="card-header">Header</div>
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">Primary card title</h4>
|
||||
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card text-white bg-secondary mb-3" style="max-width: 20rem;">
|
||||
<div class="card-header">Header</div>
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">Secondary card title</h4>
|
||||
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card text-white bg-success mb-3" style="max-width: 20rem;">
|
||||
<div class="card-header">Header</div>
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">Success card title</h4>
|
||||
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card text-white bg-danger mb-3" style="max-width: 20rem;">
|
||||
<div class="card-header">Header</div>
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">Danger card title</h4>
|
||||
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card text-white bg-warning mb-3" style="max-width: 20rem;">
|
||||
<div class="card-header">Header</div>
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">Warning card title</h4>
|
||||
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card text-white bg-info mb-3" style="max-width: 20rem;">
|
||||
<div class="card-header">Header</div>
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">Info card title</h4>
|
||||
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card bg-light mb-3" style="max-width: 20rem;">
|
||||
<div class="card-header">Header</div>
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">Light card title</h4>
|
||||
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card text-white bg-dark mb-3" style="max-width: 20rem;">
|
||||
<div class="card-header">Header</div>
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">Dark card title</h4>
|
||||
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<div class="bs-component">
|
||||
<div class="card border-primary mb-3" style="max-width: 20rem;">
|
||||
<div class="card-header">Header</div>
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">Primary card title</h4>
|
||||
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card border-secondary mb-3" style="max-width: 20rem;">
|
||||
<div class="card-header">Header</div>
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">Secondary card title</h4>
|
||||
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card border-success mb-3" style="max-width: 20rem;">
|
||||
<div class="card-header">Header</div>
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">Success card title</h4>
|
||||
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card border-danger mb-3" style="max-width: 20rem;">
|
||||
<div class="card-header">Header</div>
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">Danger card title</h4>
|
||||
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card border-warning mb-3" style="max-width: 20rem;">
|
||||
<div class="card-header">Header</div>
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">Warning card title</h4>
|
||||
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card border-info mb-3" style="max-width: 20rem;">
|
||||
<div class="card-header">Header</div>
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">Info card title</h4>
|
||||
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card border-light mb-3" style="max-width: 20rem;">
|
||||
<div class="card-header">Header</div>
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">Light card title</h4>
|
||||
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card border-dark mb-3" style="max-width: 20rem;">
|
||||
<div class="card-header">Header</div>
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">Dark card title</h4>
|
||||
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-4">
|
||||
<div class="bs-component">
|
||||
<div class="card mb-3">
|
||||
<h3 class="card-header">Card header</h3>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Special title treatment</h5>
|
||||
<h6 class="card-subtitle text-muted">Support card subtitle</h6>
|
||||
</div>
|
||||
<img style="height: 200px; width: 100%; display: block;" src="data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22318%22%20height%3D%22180%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20318%20180%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_158bd1d28ef%20text%20%7B%20fill%3Argba(255%2C255%2C255%2C.75)%3Bfont-weight%3Anormal%3Bfont-family%3AHelvetica%2C%20monospace%3Bfont-size%3A16pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_158bd1d28ef%22%3E%3Crect%20width%3D%22318%22%20height%3D%22180%22%20fill%3D%22%23777%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%22129.359375%22%20y%3D%2297.35%22%3EImage%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E" alt="Card image">
|
||||
<div class="card-body">
|
||||
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item">Cras justo odio</li>
|
||||
<li class="list-group-item">Dapibus ac facilisis in</li>
|
||||
<li class="list-group-item">Vestibulum at eros</li>
|
||||
</ul>
|
||||
<div class="card-body">
|
||||
<a href="javascript:void(0)" class="card-link">Card link</a>
|
||||
<a href="javascript:void(0)" class="card-link">Another link</a>
|
||||
</div>
|
||||
<div class="card-footer text-muted">
|
||||
2 days ago
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">Card title</h4>
|
||||
<h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
|
||||
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
|
||||
<a href="javascript:void(0)" class="card-link">Card link</a>
|
||||
<a href="javascript:void(0)" class="card-link">Another link</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
@@ -1,49 +0,0 @@
|
||||
## Buttons
|
||||
|
||||
```vue
|
||||
<button>Just a default button</button>
|
||||
```
|
||||
|
||||
```vue
|
||||
<a href="javascript:void(0)" class="action-button">An anchor with .action-button</a>
|
||||
```
|
||||
|
||||
All the crazy permutations of menu button...
|
||||
|
||||
```vue
|
||||
<a href="javascript:void(0)" class="menubutton">An anchor with .menu-button</a>
|
||||
```
|
||||
|
||||
```vue
|
||||
<a href="javascript:void(0)" class="menubutton popup">An anchor with .menu-button.popup</a>
|
||||
```
|
||||
|
||||
```vue
|
||||
<a href="javascript:void(0)" class="menubutton popup split">
|
||||
<a class="menubutton-label">An anchor with .menu-button.popup.split</a>
|
||||
</a>
|
||||
```
|
||||
|
||||
## Radio Buttons
|
||||
|
||||
As generated by mvc.ui.ui-options
|
||||
|
||||
```vue
|
||||
<div class="btn-group btn-group-toggle" data-toggle="buttons">
|
||||
<label class="btn btn-secondary active">
|
||||
<input type="radio" name="options" id="option1" autocomplete="off" checked> Active
|
||||
</label>
|
||||
<label class="btn btn-secondary">
|
||||
<input type="radio" name="options" id="option2" autocomplete="off"> Radio
|
||||
</label>
|
||||
<label class="btn btn-secondary">
|
||||
<input type="radio" name="options" id="option3" autocomplete="off"> Radio
|
||||
</label>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Pagination
|
||||
|
||||
```vue
|
||||
<ul class="pagination"><li class="page-item disabled"><a class="page-link" href="#page/1"><span class="fa fa-angle-double-left"></span></a></li><li class="page-item disabled"><a class="page-link" href="#page/1">0</a></li><li class="page-item active"><a class="page-link" href="#page/1">1</a></li><li class="page-item"><a class="page-link" href="#page/2">2</a></li><li class="page-item"><a class="page-link" href="#page/4"><span class="fa fa-angle-double-right"></span></a></li></ul>
|
||||
```
|
||||
@@ -1,55 +0,0 @@
|
||||
## Forms
|
||||
|
||||
Manually crafted "tool" form class, this is broken in the BS4 branch I think.
|
||||
|
||||
```vue
|
||||
<div class="tool-form">
|
||||
<form name="foo">
|
||||
<div class="form-row">
|
||||
<label>Label Input 1</label>
|
||||
<input id="input1" type="text" name="input1" size="40" />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<input type="submit" id="send" name="submit" value="submit" />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
```
|
||||
|
||||
Portlet variant generated by mvc.form.form-view
|
||||
|
||||
```vue
|
||||
<div class="ui-portlet-limited" id="uid-8">
|
||||
<div class="portlet-header">
|
||||
<div class="portlet-operations"></div>
|
||||
<div class="portlet-title">
|
||||
<i class="portlet-title-icon fa fa-unlock-alt" style="display: inline;"></i>
|
||||
<span class="portlet-title-text">Portlet Title</span></div>
|
||||
</div>
|
||||
<div class="portlet-content">
|
||||
<div class="portlet-body">
|
||||
<div class="ui-message alert alert-info"></div>
|
||||
<div>
|
||||
<div class="ui-form-element section-row">
|
||||
<div class="ui-form-error ui-error"><span class="fa fa-arrow-down"></span><span class="ui-form-error-text"></span></div>
|
||||
<div class="ui-form-title">
|
||||
<div class="ui-form-collapsible" style="display: none;">
|
||||
<i class="ui-form-collapsible-icon"></i>
|
||||
<span class="ui-form-collapsible-text"></span>
|
||||
</div>
|
||||
<span class="ui-form-title-text">Text Field 1</span>
|
||||
</div>
|
||||
<div class="ui-form-field">
|
||||
<input class="ui-input" id="field-uid-4" type="text">
|
||||
<span class="ui-form-info"></span>
|
||||
<div class="ui-form-backdrop" style="display: none;"></div>
|
||||
</div>
|
||||
<div class="ui-form-preview" style="display: none;"></div>
|
||||
</div>
|
||||
<div class="ui-form-element section-row">
|
||||
<div class="ui-form-error ui-error" style="display: none;"><span class="fa fa-arrow-down"></span><span class="ui-form-error-text"></span></div>
|
||||
<div class="ui-form-title"><div class="ui-form-collapsible" style="display: none;"><i class="ui-form-collapsible-icon"></i><span class="ui-form-collapsible-text"></span></div><span class="ui-form-title-text" style="display: inline;">Password</span></div>
|
||||
<div class="ui-form-field">
|
||||
<input class="ui-input" type="password"><span class="ui-form-info"></span><div class="ui-form-backdrop" style="display: none;"></div></div><div class="ui-form-preview" style="display: none;"></div></div>
|
||||
<div class="ui-form-element section-row" id="uid-7" style="display: none;"><div class="ui-form-error ui-error" style="display: none;"><span class="fa fa-arrow-down"></span><span class="ui-form-error-text"></span></div><div class="ui-form-title"><div class="ui-form-collapsible" style="display: none;"><i class="ui-form-collapsible-icon"></i><span class="ui-form-collapsible-text"></span></div><span class="ui-form-title-text" style="display: inline;">token</span></div><div class="ui-form-field"><div id="field-uid-7"><div style="display: none;"></div><div></div></div><span class="ui-form-info"></span><div class="ui-form-backdrop" style="display: none;"></div></div><div class="ui-form-preview" style="display: none;"></div></div></div></div><div class="portlet-buttons"><button type="button" class="ui-button-default btn btn-primary ui-clear-float" id="submit" data-original-title="" title=""><i class="icon fa fa-save ui-margin-right"></i><span class="title">Submit</span><div class="progress" style="display: none;"><div class="progress-bar" style="width: 0%;"></div></div></button></div></div><div class="portlet-backdrop"></div></div>
|
||||
```
|
||||
@@ -1,69 +0,0 @@
|
||||
## Masthead
|
||||
|
||||
As generated by layout.masthead.js
|
||||
|
||||
Default height:
|
||||
|
||||
```vue
|
||||
<nav id="masthead" class="navbar navbar-expand justify-content-center navbar-dark bg-dark">
|
||||
<a class="navbar-brand">
|
||||
<img class="navbar-brand-image"/>
|
||||
<span class="navbar-brand-title">Title</span>
|
||||
</a>
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link">Header 1</a>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle">Header 2</a>
|
||||
</li>
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link">Active Header</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
```
|
||||
|
||||
Override height with "height: 80px;"
|
||||
|
||||
```vue
|
||||
<nav id="masthead" class="navbar navbar-expand justify-content-center navbar-dark bg-dark" style="height: 80px;">
|
||||
<a class="navbar-brand">
|
||||
<img class="navbar-brand-image"/>
|
||||
<span class="navbar-brand-title">Title</span>
|
||||
</a>
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link">Header 1</a>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle">Header 2</a>
|
||||
</li>
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link">Active Header</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
```
|
||||
|
||||
Override height with "height: 4rem;"
|
||||
|
||||
```vue
|
||||
<nav id="masthead" class="navbar navbar-expand justify-content-center navbar-dark bg-dark" style="height: 4rem;">
|
||||
<a class="navbar-brand">
|
||||
<img class="navbar-brand-image"/>
|
||||
<span class="navbar-brand-title">Title</span>
|
||||
</a>
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link">Header 1</a>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle">Header 2</a>
|
||||
</li>
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link">Active Header</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
```
|
||||
@@ -1,19 +0,0 @@
|
||||
## Popup menus
|
||||
|
||||
These are the classes for the menu itself, normally floating, so placed inside
|
||||
a relative element.
|
||||
|
||||
```vue
|
||||
<div style="position: relative; height: 12rem;">
|
||||
<div class="popmenu-wrapper" style="top: 0;">
|
||||
<ul class="dropdown-menu">
|
||||
<li class="head"><a>Heading one</a></li>
|
||||
<li><a>An item</a></li>
|
||||
<li><a>Another item</a></li>
|
||||
<li class="head"><a>Heading two</a></li>
|
||||
<li><a>Third item</a></li>
|
||||
<li><a>Last item</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
@@ -1,33 +0,0 @@
|
||||
### Panel Messages
|
||||
|
||||
Messages that appear across the top of the panel view below the masthead.
|
||||
|
||||
```vue
|
||||
<div>
|
||||
<div v-for="type in ['done', 'info', 'warning', 'error']">
|
||||
<div v-bind:class="'panel-' + type + '-message'">I'm a panel-{{type}}-message</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Large Messages
|
||||
|
||||
Used for providing feedback inline.
|
||||
|
||||
```vue
|
||||
<div>
|
||||
<div v-for="type in ['done', 'info', 'warning', 'error']">
|
||||
<div v-bind:class="type + 'messagelarge'">I'm a {{type}}messagelarge</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Small Messages
|
||||
|
||||
```vue
|
||||
<div>
|
||||
<div v-for="type in ['done', 'info', 'warning', 'error']">
|
||||
<div v-bind:class="type + 'message'">I'm a {{type}}message</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
@@ -1,20 +0,0 @@
|
||||
## Panel (toolMenuContainer)
|
||||
|
||||
```vue
|
||||
<div style="height: 200px;">
|
||||
<div style="position: absolute; width: 250px; height: 200px; border: dotted gray 1px">
|
||||
<div class="unified-panel-header">
|
||||
<div class="unified-panel-header-inner">
|
||||
Header
|
||||
</div>
|
||||
</div>
|
||||
<div class="unified-panel-body">
|
||||
<div class="toolMenuContainer">
|
||||
one<br>
|
||||
two<br>
|
||||
three<br>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
@@ -1,14 +0,0 @@
|
||||
## Tables
|
||||
|
||||
Tables using the .grid class
|
||||
|
||||
```vue
|
||||
<table class="grid">
|
||||
<thead class="grid-table-header">
|
||||
<tr><th>One</th><th>Two</th></tr>
|
||||
</thead>
|
||||
<tbody class="grid-table-body">
|
||||
<tr><td>Value 1</td><td>Value 2</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
```
|
||||
@@ -1,32 +0,0 @@
|
||||
## Tabs
|
||||
|
||||
Tabs as generated by mvc.ui.ui-tabs
|
||||
|
||||
```vue
|
||||
<ul style="display: flex;" class="tab-navigation nav nav-tabs">
|
||||
<li class="tab-element nav-item" id="tab-attribute" style="display: list-item;" data-original-title="" title="">
|
||||
<a class="nav-link active" id="tab-title-link-attribute">
|
||||
<i class="tab-icon fa fa-bars"></i>
|
||||
<span id="tab-title-text-attribute" class="tab-title-text">Attributes</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="tab-element nav-item" id="tab-convert" style="display: list-item;" data-original-title="" title="">
|
||||
<a class="nav-link" id="tab-title-link-convert">
|
||||
<i class="tab-icon fa fa-gear"></i>
|
||||
<span id="tab-title-text-convert" class="tab-title-text">Convert</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="tab-element nav-item" id="tab-datatype" style="display: list-item;" data-original-title="" title="">
|
||||
<a class="nav-link" id="tab-title-link-datatype">
|
||||
<i class="tab-icon fa fa-database"></i>
|
||||
<span id="tab-title-text-datatype" class="tab-title-text">Datatypes</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="tab-element nav-item" id="tab-permissions" style="display: list-item;" data-original-title="" title="">
|
||||
<a class="nav-link" id="tab-title-link-permissions">
|
||||
<i class="tab-icon fa fa-user"></i>
|
||||
<span id="tab-title-text-permissions" class="tab-title-text">Permissions</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
```
|
||||
@@ -1 +0,0 @@
|
||||
# testdocs/a-file.md
|
||||
@@ -1 +0,0 @@
|
||||
# testdocs/another-file.md
|
||||
@@ -1,3 +0,0 @@
|
||||
<template>
|
||||
<div>Yay!</div>
|
||||
</template>
|
||||
@@ -1 +0,0 @@
|
||||
# ignored-file.md
|
||||
@@ -1 +0,0 @@
|
||||
# some-loose-file.md
|
||||
@@ -1 +0,0 @@
|
||||
# loose file
|
||||
@@ -1 +0,0 @@
|
||||
# testdocs/abc/readme.md
|
||||
@@ -1 +0,0 @@
|
||||
# foo
|
||||
@@ -1 +0,0 @@
|
||||
# another loose file
|
||||
@@ -1 +0,0 @@
|
||||
# Loose file
|
||||
@@ -1,62 +0,0 @@
|
||||
/**
|
||||
* Tests the functions which build the sections for the styleguide.
|
||||
*/
|
||||
|
||||
const path = require("path");
|
||||
const { getDocSections } = require("../sections");
|
||||
|
||||
const getSectionByName = (sections, name) => sections.find((o) => o.name == name);
|
||||
|
||||
describe("getDocSections", () => {
|
||||
const ignore = [
|
||||
// ignoring a whole directory
|
||||
"**/ignored/*",
|
||||
// ignoring a file
|
||||
"**/ignore*",
|
||||
];
|
||||
const testDocRoot = path.join(__dirname, "sample-docs");
|
||||
const { rootNode } = getDocSections(testDocRoot, { ignore });
|
||||
|
||||
test("section generation", () => {
|
||||
expect(rootNode.name).toEqual("Sample Docs");
|
||||
expect(rootNode.sections.length).toEqual(7);
|
||||
expect(rootNode.content).toBeUndefined();
|
||||
});
|
||||
|
||||
test("nested subsection should appear even if no docs in intermediate folders", () => {
|
||||
const deepSection = getSectionByName(rootNode.sections, "Nested Folders");
|
||||
expect(deepSection.sections.length).toEqual(1);
|
||||
});
|
||||
|
||||
test("subdirectory with readme should register as summary", () => {
|
||||
const subsection = getSectionByName(rootNode.sections, "Has Readme");
|
||||
|
||||
// readme interpreted as content file and not as loose section
|
||||
// one other loose file
|
||||
expect(subsection.content).toContain("readme.md");
|
||||
expect(subsection.sections.length).toEqual(1);
|
||||
});
|
||||
|
||||
test("subdirectory with no readme file", () => {
|
||||
const subsection = getSectionByName(rootNode.sections, "No Summary File");
|
||||
|
||||
// should just see 2 folders no summary
|
||||
expect(subsection.content).toBeUndefined();
|
||||
expect(subsection.sections.length).toEqual(2);
|
||||
});
|
||||
|
||||
test("subdirectory with ignored file", () => {
|
||||
const subsection = getSectionByName(rootNode.sections, "Contains Ommitted File");
|
||||
expect(subsection.sections.length).toEqual(1);
|
||||
});
|
||||
|
||||
test("ignored subdirectory", () => {
|
||||
const ignoredSection = getSectionByName(rootNode.sections, "Ignored");
|
||||
expect(ignoredSection).toBeUndefined();
|
||||
});
|
||||
|
||||
test("should not create a section for a component example file", () => {
|
||||
const section = getSectionByName(rootNode.sections, "Component Example");
|
||||
expect(section.sections.length).toEqual(0);
|
||||
});
|
||||
});
|
||||
+72
-44
@@ -30,21 +30,12 @@ const STATIC_PLUGIN_BUILD_IDS = [
|
||||
"nora",
|
||||
"venn",
|
||||
];
|
||||
|
||||
const DIST_PLUGIN_BUILD_IDS = ["new_user"];
|
||||
|
||||
const PLUGIN_BUILD_IDS = Array.prototype.concat(DIST_PLUGIN_BUILD_IDS, STATIC_PLUGIN_BUILD_IDS);
|
||||
|
||||
const paths = {
|
||||
node_modules: "./node_modules",
|
||||
plugin_dirs: [
|
||||
"../config/plugins/{visualizations,interactive_environments,welcome_page}/*/static/**/*",
|
||||
"../config/plugins/{visualizations,interactive_environments,welcome_page}/*/*/static/**/*",
|
||||
],
|
||||
plugin_build_modules: [
|
||||
`../config/plugins/{visualizations,welcome_page}/{${PLUGIN_BUILD_IDS.join(",")}}/package.json`,
|
||||
],
|
||||
lib_locs: {
|
||||
const PATHS = {
|
||||
nodeModules: "./node_modules",
|
||||
stagedLibraries: {
|
||||
// This is a stepping stone towards having all this staged
|
||||
// automatically. Eventually, this dictionary and staging step will
|
||||
// not be necessary.
|
||||
@@ -57,13 +48,26 @@ const paths = {
|
||||
requirejs: ["require.js", "require.js"],
|
||||
underscore: ["underscore.js", "underscore.js"],
|
||||
},
|
||||
libs: ["src/libs/**/*.js"],
|
||||
};
|
||||
|
||||
PATHS.pluginBaseDir =
|
||||
(process.env.GALAXY_PLUGIN_PATH && process.env.GALAXY_PLUGIN_PATH !== "None"
|
||||
? process.env.GALAXY_PLUGIN_PATH
|
||||
: undefined) || "../config/plugins/";
|
||||
|
||||
PATHS.pluginDirs = [
|
||||
path.join(PATHS.pluginBaseDir, "{visualizations,welcome_page}/*/static/**/*"),
|
||||
path.join(PATHS.pluginBaseDir, "{visualizations,welcome_page}/*/*/static/**/*"),
|
||||
];
|
||||
|
||||
PATHS.pluginBuildModules = [
|
||||
path.join(PATHS.pluginBaseDir, `{visualizations,welcome_page}/{${PLUGIN_BUILD_IDS.join(",")}}/package.json`),
|
||||
];
|
||||
|
||||
function stageLibs(callback) {
|
||||
Object.keys(paths.lib_locs).forEach((lib) => {
|
||||
var p1 = path.resolve(path.join(paths.node_modules, lib, paths.lib_locs[lib][0]));
|
||||
var p2 = path.resolve(path.join("src", "libs", paths.lib_locs[lib][1]));
|
||||
Object.keys(PATHS.stagedLibraries).forEach((lib) => {
|
||||
var p1 = path.resolve(path.join(PATHS.nodeModules, lib, PATHS.stagedLibraries[lib][0]));
|
||||
var p2 = path.resolve(path.join("src", "libs", PATHS.stagedLibraries[lib][1]));
|
||||
if (fs.existsSync(p1)) {
|
||||
del.sync(p2);
|
||||
fs.createReadStream(p1).pipe(fs.createWriteStream(p2));
|
||||
@@ -78,57 +82,75 @@ function stageLibs(callback) {
|
||||
}
|
||||
|
||||
function fonts() {
|
||||
return src(path.resolve(path.join(paths.node_modules, "font-awesome/fonts/**/*"))).pipe(
|
||||
return src(path.resolve(path.join(PATHS.nodeModules, "font-awesome/fonts/**/*"))).pipe(
|
||||
dest("../static/images/fonts")
|
||||
);
|
||||
}
|
||||
|
||||
function stagePlugins() {
|
||||
return src(paths.plugin_dirs).pipe(dest("../static/plugins/"));
|
||||
return src(PATHS.pluginDirs).pipe(dest("../static/plugins/"));
|
||||
}
|
||||
|
||||
function buildPlugins(callback) {
|
||||
function buildPlugins(callback, forceRebuild) {
|
||||
/*
|
||||
* Walk plugin_build_modules glob and attempt to build modules.
|
||||
* Walk pluginBuildModules glob and attempt to build modules.
|
||||
* */
|
||||
paths.plugin_build_modules.map((build_module) => {
|
||||
glob(build_module, {}, (er, files) => {
|
||||
PATHS.pluginBuildModules.map((buildModule) => {
|
||||
glob(buildModule, {}, (er, files) => {
|
||||
files.map((file) => {
|
||||
let skip_build = false;
|
||||
const f = path.join(process.cwd(), file).slice(0, -12);
|
||||
const plugin_name = path.dirname(file).split(path.sep).pop();
|
||||
const hash_file_path = path.join(
|
||||
f,
|
||||
DIST_PLUGIN_BUILD_IDS.indexOf(plugin_name) > -1 ? "dist" : "static",
|
||||
let skipBuild = false;
|
||||
const pluginDir = path.dirname(file);
|
||||
const pluginName = pluginDir.split(path.sep).pop();
|
||||
|
||||
const hashFilePath = path.join(
|
||||
pluginDir,
|
||||
DIST_PLUGIN_BUILD_IDS.indexOf(pluginName) > -1 ? "dist" : "static",
|
||||
"plugin_build_hash.txt"
|
||||
);
|
||||
|
||||
if (fs.existsSync(hash_file_path)) {
|
||||
skip_build =
|
||||
child_process.spawnSync("git", ["diff", "--quiet", `$(cat ${hash_file_path})`, "--", f], {
|
||||
stdio: "inherit",
|
||||
shell: true,
|
||||
}).status === 0;
|
||||
if (forceRebuild) {
|
||||
skipBuild = false;
|
||||
} else {
|
||||
console.log(`No build hashfile detected for ${plugin_name}, generating now.`);
|
||||
if (fs.existsSync(hashFilePath)) {
|
||||
skipBuild =
|
||||
child_process.spawnSync(
|
||||
"git",
|
||||
["diff", "--quiet", `$(cat ${hashFilePath})`, "--", pluginDir],
|
||||
{
|
||||
stdio: "inherit",
|
||||
shell: true,
|
||||
}
|
||||
).status === 0;
|
||||
} else {
|
||||
console.log(`No build hashfile detected for ${pluginName}, generating now.`);
|
||||
}
|
||||
}
|
||||
|
||||
if (skip_build) {
|
||||
console.log(`No changes detected for ${plugin_name}`);
|
||||
if (skipBuild) {
|
||||
console.log(`No changes detected for ${pluginName}`);
|
||||
} else {
|
||||
console.log(`Installing Dependencies for ${plugin_name}`);
|
||||
console.log(`Installing Dependencies for ${pluginName}`);
|
||||
child_process.spawnSync(
|
||||
"yarn",
|
||||
["install", "--production=false", "--network-timeout=300000", "--check-files"],
|
||||
{
|
||||
cwd: f,
|
||||
cwd: pluginDir,
|
||||
stdio: "inherit",
|
||||
shell: true,
|
||||
}
|
||||
);
|
||||
console.log(`Building ${plugin_name}`);
|
||||
child_process.spawnSync("yarn", ["build"], { cwd: f, stdio: "inherit", shell: true });
|
||||
child_process.exec(`(git rev-parse HEAD 2>/dev/null || echo \`\`) > ${hash_file_path}`);
|
||||
console.log(`Building ${pluginName}`);
|
||||
if (
|
||||
child_process.spawnSync("yarn", ["build"], { cwd: pluginDir, stdio: "inherit", shell: true })
|
||||
.status === 0
|
||||
) {
|
||||
console.log(`Successfully built, saving build state to ${hashFilePath}`);
|
||||
child_process.exec(`(git rev-parse HEAD 2>/dev/null || echo \`\`) > ${hashFilePath}`);
|
||||
} else {
|
||||
console.error(
|
||||
`Error building ${pluginName}, not saving build state. Please report this issue to the Galaxy Team.`
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -136,21 +158,27 @@ function buildPlugins(callback) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
function forceBuildPlugins(callback) {
|
||||
return buildPlugins(callback, true);
|
||||
}
|
||||
|
||||
function cleanPlugins() {
|
||||
return del(["../static/plugins/{visualizations,interactive_environments,welcome_page}/*"], { force: true });
|
||||
return del(["../static/plugins/{visualizations,welcome_page}/*"], { force: true });
|
||||
}
|
||||
|
||||
const client = parallel(fonts, stageLibs);
|
||||
const plugins = series(buildPlugins, cleanPlugins, stagePlugins);
|
||||
const pluginsRebuild = series(forceBuildPlugins, cleanPlugins, stagePlugins);
|
||||
|
||||
function watchPlugins() {
|
||||
const BUILD_PLUGIN_WATCH_GLOB = [
|
||||
`../config/plugins/{visualizations,welcome_page}/{${PLUGIN_BUILD_IDS.join(",")}}/**/*`,
|
||||
path.join(PATHS.pluginBaseDir, `{visualizations,welcome_page}/{${PLUGIN_BUILD_IDS.join(",")}}/**/*`),
|
||||
];
|
||||
watch(BUILD_PLUGIN_WATCH_GLOB, { queue: false }, plugins);
|
||||
}
|
||||
|
||||
module.exports.client = client;
|
||||
module.exports.plugins = plugins;
|
||||
module.exports.pluginsRebuild = pluginsRebuild;
|
||||
module.exports.watchPlugins = watchPlugins;
|
||||
module.exports.default = parallel(client, plugins);
|
||||
|
||||
+70
-74
@@ -11,8 +11,7 @@
|
||||
},
|
||||
"license": "AFL-3.0",
|
||||
"resolutions": {
|
||||
"**/chokidar": "3.5.2",
|
||||
"**/ua-parser-js": "0.7.30"
|
||||
"**/chokidar": "3.5.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "^5.15.4",
|
||||
@@ -20,21 +19,22 @@
|
||||
"@fortawesome/free-brands-svg-icons": "^5.15.4",
|
||||
"@fortawesome/free-regular-svg-icons": "^5.15.4",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.15.4",
|
||||
"@fortawesome/vue-fontawesome": "^2.0.2",
|
||||
"@galaxyproject/bootstrap-tour": "^0.12.1",
|
||||
"@fortawesome/vue-fontawesome": "^2.0.6",
|
||||
"@galaxyproject/bootstrap-tour": "0.12.2",
|
||||
"@handsontable/vue": "^2.0.0-beta1",
|
||||
"@hirez_io/observer-spy": "^2.1.0",
|
||||
"@hirez_io/observer-spy": "^2.1.2",
|
||||
"@johmun/vue-tags-input": "^2.1.0",
|
||||
"@sentry/browser": "^5.20.1",
|
||||
"axios": "^0.21.1",
|
||||
"@sentry/browser": "^6.17.4",
|
||||
"assert": "^2.0.0",
|
||||
"axios": "^0.25.0",
|
||||
"babel-runtime": "^6.26.0",
|
||||
"backbone": "1.4.0",
|
||||
"bootstrap": "4.5.0",
|
||||
"bootstrap": "4.6",
|
||||
"bootstrap-vue": "^2.21.2",
|
||||
"citation-js": "^0.5.1",
|
||||
"core-js": "^3.19.1",
|
||||
"citation-js": "^0.5.5",
|
||||
"core-js": "^3.21.0",
|
||||
"d3": "3",
|
||||
"date-fns": "^2.24.0",
|
||||
"date-fns": "^2.28.0",
|
||||
"decode-uri-component": "^0.2.0",
|
||||
"deep-diff": "^1.0.2",
|
||||
"deep-equal": "^2.0.5",
|
||||
@@ -44,62 +44,60 @@
|
||||
"flush-promises": "^1.0.2",
|
||||
"glob": "^7.2.0",
|
||||
"handsontable": "^2.0.0",
|
||||
"imask": "^6.2.2",
|
||||
"imask": "^6.4.0",
|
||||
"in-viewport": "^3.6.0",
|
||||
"is-promise": "^4.0.0",
|
||||
"isotope-layout": "^3.0.6",
|
||||
"iter-tools": "7.1.4",
|
||||
"iter-tools": "7.2.0",
|
||||
"jquery": "2",
|
||||
"jquery-migrate": "~1.4",
|
||||
"jquery-mousewheel": "^3.1.13",
|
||||
"jquery-ui": "^1.12.1",
|
||||
"jquery-ui": "^1.13.1",
|
||||
"jquery.cookie": "^1.4.1",
|
||||
"jspdf": "^2.4.0",
|
||||
"linkifyjs": "^2.1.9",
|
||||
"jspdf": "^2.5.1",
|
||||
"linkify-html": "^3.0.5",
|
||||
"linkifyjs": "^3.0.5",
|
||||
"localforage": "^1.10.0",
|
||||
"markdown-it": "^12.3.2",
|
||||
"markdown-it-regexp": "^0.4.0",
|
||||
"moment": "2.29.1",
|
||||
"moment": "2.29.2",
|
||||
"object-hash": "^2.2.0",
|
||||
"popper.js": "^1.16.1",
|
||||
"pouchdb": "^7.2.2",
|
||||
"pouchdb-adapter-memory": "^7.2.2",
|
||||
"pouchdb-debug": "^7.2.1",
|
||||
"pouchdb-erase": "^1.0.2",
|
||||
"pouchdb-find": "^7.2.2",
|
||||
"pouchdb-upsert": "^2.2.0",
|
||||
"pretty-bytes": "^5.6.0",
|
||||
"proper-skip-list": "^4.0.2",
|
||||
"proper-skip-list": "^4.1.0",
|
||||
"pyre-to-regexp": "^0.0.5",
|
||||
"querystring-es3": "^0.2.1",
|
||||
"regenerator-runtime": "^0.13.9",
|
||||
"regression": "^2.0.1",
|
||||
"requirejs": "2.3.6",
|
||||
"rxjs": "^7.4.0",
|
||||
"rxjs-spy": "^8.0.0",
|
||||
"rxjs": "^7.5.2",
|
||||
"rxjs-spy": "^8.0.2",
|
||||
"rxjs-spy-devtools-plugin": "^0.0.4",
|
||||
"slugify": "^1.6.0",
|
||||
"slugify": "^1.6.5",
|
||||
"snake-case": "^3.0.4",
|
||||
"splitpanes": "2.3.8",
|
||||
"stream-browserify": "^3.0.0",
|
||||
"threads": "^1.6.5",
|
||||
"threads": "^1.7.0",
|
||||
"timers-browserify": "^2.0.12",
|
||||
"toastr": "^2.1.4",
|
||||
"tus-js-client": "^2.3.0",
|
||||
"underscore": "^1.10.2",
|
||||
"underscore.string": "^3.3.5",
|
||||
"underscore": "^1.13.2",
|
||||
"underscore.string": "^3.3.6",
|
||||
"util": "^0.12.4",
|
||||
"vue": "^2.6.14",
|
||||
"vue-infinite-scroll": "^2.0.2",
|
||||
"vue-multiselect": "^2.1.0",
|
||||
"vue-multiselect": "^2.1.6",
|
||||
"vue-observe-visibility": "^1.0.0",
|
||||
"vue-prismjs": "^1.2.0",
|
||||
"vue-router": "^3.5.2",
|
||||
"vue-router": "^3.5.3",
|
||||
"vue-rx": "^6.2.0",
|
||||
"vue-scrollto": "^2.20.0",
|
||||
"vue-virtual-scroll-list": "^2.3.3",
|
||||
"vuedraggable": "2.24.3",
|
||||
"vueisotope": "^3.1.2",
|
||||
"vuex": "^3.4.0",
|
||||
"vuex-cache": "^3.2.0",
|
||||
"vuex-persist": "^3.1.3",
|
||||
"vuex-persistedstate": "^4.1.0",
|
||||
"vuex": "^3.6.2",
|
||||
"vuex-cache": "^3.4.0",
|
||||
"vuex-persist": "2.3",
|
||||
"xml-beautifier": "^0.5.0"
|
||||
},
|
||||
"scripts": {
|
||||
@@ -117,76 +115,74 @@
|
||||
"save-build-hash": "(git rev-parse HEAD 2>/dev/null || echo '') >../static/client_build_hash.txt",
|
||||
"prettier": "prettier --write 'src/style/scss/**/*.scss' 'src/**/{*.js,*.vue}' '!src/libs/**'",
|
||||
"prettier-check": "prettier --check 'src/style/scss/**/*.scss' 'src/**/{*.js,*.vue}' '!src/libs/**'",
|
||||
"styleguide": "vue-styleguidist server",
|
||||
"styleguide:build": "vue-styleguidist build",
|
||||
"test": "yarn run qunit && yarn run jest",
|
||||
"jest": "jest --config tests/jest/jest.config.js",
|
||||
"jest-watch": "jest --config tests/jest/jest.config.js --watch",
|
||||
"qunit": "karma start tests/karma/karma.config.qunit.js",
|
||||
"eslint": "eslint -c .eslintrc.js src --ext .js,.vue"
|
||||
"eslint-precommit": "eslint -c .eslintrc.json --quiet --ext .js,.vue src",
|
||||
"eslint": "eslint -c .eslintrc.json --ext .js,.vue src"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.15.8",
|
||||
"@babel/helper-validator-identifier": "^7.15.7",
|
||||
"@babel/core": "^7.17.0",
|
||||
"@babel/eslint-parser": "^7.17.0",
|
||||
"@babel/helper-validator-identifier": "^7.16.7",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
||||
"@babel/plugin-transform-runtime": "^7.15.8",
|
||||
"@babel/preset-env": "^7.15.8",
|
||||
"@babel/plugin-transform-runtime": "^7.17.0",
|
||||
"@babel/preset-env": "^7.16.11",
|
||||
"@cerner/duplicate-package-checker-webpack-plugin": "^2.1.0",
|
||||
"@testing-library/jest-dom": "^5.14.1",
|
||||
"@vue/test-utils": "^1.2.2",
|
||||
"@vue/vue2-jest": "^27.0.0-alpha.2",
|
||||
"@testing-library/jest-dom": "^5.16.2",
|
||||
"@vue/test-utils": "^1.3.0",
|
||||
"@vue/vue2-jest": "^27.0.0-alpha.4",
|
||||
"amdi18n-loader": "^0.9.3",
|
||||
"autoprefixer": "^10.3.5",
|
||||
"autoprefixer": "^10.4.2",
|
||||
"axios-mock-adapter": "^1.20.0",
|
||||
"babel-core": "^7.0.0-bridge.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"babel-jest": "^27.2.5",
|
||||
"babel-loader": "^8.2.2",
|
||||
"babel-jest": "^27.4.6",
|
||||
"babel-loader": "^8.2.3",
|
||||
"babel-plugin-transform-inline-environment-variables": "^0.4.3",
|
||||
"babel-plugin-transform-vue-template": "^0.4.2",
|
||||
"buffer": "^6.0.3",
|
||||
"chai": "^4.2.0",
|
||||
"chokidar": "^3.5.2",
|
||||
"css-loader": "^6.3.0",
|
||||
"css-minimizer-webpack-plugin": "^3.0.2",
|
||||
"chai": "^4.3.6",
|
||||
"css-loader": "^6.6.0",
|
||||
"css-minimizer-webpack-plugin": "^3.4.1",
|
||||
"del": "^6.0.0",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-plugin-vue": "^7.18.0",
|
||||
"expose-loader": "^3.0.0",
|
||||
"eslint": "^8.8.0",
|
||||
"eslint-plugin-vue": "^8.4.1",
|
||||
"expose-loader": "^3.1.0",
|
||||
"gulp": "^4.0.2",
|
||||
"ignore-loader": "^0.1.2",
|
||||
"imports-loader": "^3.0.0",
|
||||
"jest": "^27.2.5",
|
||||
"imports-loader": "^3.1.1",
|
||||
"jest": "^27.4.7",
|
||||
"jest-location-mock": "^1.0.9",
|
||||
"jest-raw-loader": "^1.0.1",
|
||||
"jest-transform-yaml": "0.1.2",
|
||||
"jest-transform-yaml": "1.0.0",
|
||||
"json-loader": "^0.5.7",
|
||||
"karma": "^6.3.4",
|
||||
"karma": "^6.3.16",
|
||||
"karma-chrome-launcher": "^3.1.0",
|
||||
"karma-polyfill": "^1.1.0",
|
||||
"karma-qunit": "^4.1.1",
|
||||
"karma-qunit": "^4.1.2",
|
||||
"karma-webpack": "^5.0.0",
|
||||
"mini-css-extract-plugin": "^2.4.2",
|
||||
"postcss-loader": "^6.1.1",
|
||||
"prettier": "^2.4.1",
|
||||
"mini-css-extract-plugin": "^2.5.3",
|
||||
"postcss-loader": "^6.2.1",
|
||||
"prettier": "^2.5.1",
|
||||
"process": "^0.11.10",
|
||||
"qunit": "^2.17.2",
|
||||
"raw-loader": "^4.0.2",
|
||||
"sass": "^1.42.1",
|
||||
"sass-loader": "^12.1.0",
|
||||
"sinon": "^11.1.2",
|
||||
"sass": "^1.49.7",
|
||||
"sass-loader": "^12.4.0",
|
||||
"sinon": "^13.0.1",
|
||||
"store": "^2.0.12",
|
||||
"style-loader": "^3.3.0",
|
||||
"style-loader": "^3.3.1",
|
||||
"uuid": "^8.3.2",
|
||||
"vue-loader": "^15.9.8",
|
||||
"vue-styleguidist": "^4.41.2",
|
||||
"vue-template-compiler": "^2.6.14",
|
||||
"webpack": "^5.58.0",
|
||||
"webpack-cli": "^4.9.0",
|
||||
"webpack-dev-server": "^4.2.1",
|
||||
"webpack": "^5.68.0",
|
||||
"webpack-cli": "^4.9.2",
|
||||
"webpack-dev-server": "^4.7.4",
|
||||
"webpack-merge": "^5.8.0",
|
||||
"yaml-loader": "^0.6.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"postcss": "^8.2.8"
|
||||
"postcss": "^8.4.6"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ export { default as HDAModel } from "mvc/history/hda-model";
|
||||
export { default as LegacyGridView } from "legacy/grid/grid-view";
|
||||
export { create_chart, create_histogram } from "reports/run_stats";
|
||||
export { default as ToolshedGroups } from "toolshed/toolshed.groups";
|
||||
export { default as IES } from "galaxy.interactive_environments";
|
||||
export { openGlobalUploadModal } from "components/Upload";
|
||||
|
||||
export { Toast } from "ui/toast"; // TODO: remove when external consumers are updated/gone (IES right now)
|
||||
|
||||
@@ -45,12 +45,11 @@ describe("PairedListCollectionCreator", () => {
|
||||
});
|
||||
await wrapper.vm.$nextTick();
|
||||
//change filter to .1.fastq/.2.fastq
|
||||
wrapper.find("div.forward-unpaired-filter > div.input-group-append > button").trigger("click");
|
||||
wrapper
|
||||
await wrapper.find("div.forward-unpaired-filter > div.input-group-append > button").trigger("click");
|
||||
await wrapper
|
||||
.findAll("div.dropdown-menu > a.dropdown-item")
|
||||
.wrappers.find((e) => e.text() == ".1.fastq")
|
||||
.trigger("click");
|
||||
await wrapper.vm.$nextTick();
|
||||
//assert forward filter
|
||||
const forwardFilter = wrapper.find("div.forward-unpaired-filter > input").element.value;
|
||||
expect(forwardFilter).toBe(".1.fastq");
|
||||
@@ -58,8 +57,7 @@ describe("PairedListCollectionCreator", () => {
|
||||
const reverseFilter = wrapper.find("div.reverse-unpaired-filter > input").element.value;
|
||||
expect(reverseFilter).toBe(".2.fastq");
|
||||
// click Autopair
|
||||
wrapper.find("a.autopair-link").trigger("click");
|
||||
await wrapper.vm.$nextTick();
|
||||
await wrapper.find("a.autopair-link").trigger("click");
|
||||
//assert pair-name longer name
|
||||
const pairname = wrapper.find("span.pair-name");
|
||||
expect(pairname.text()).toBe("DP134_1_FS_PSII_FSB_42C_A10");
|
||||
@@ -83,12 +81,11 @@ describe("PairedListCollectionCreator", () => {
|
||||
});
|
||||
await wrapper.vm.$nextTick();
|
||||
//change filter to .1.fastq/.2.fastq
|
||||
wrapper.find("div.forward-unpaired-filter > div.input-group-append > button").trigger("click");
|
||||
wrapper
|
||||
await wrapper.find("div.forward-unpaired-filter > div.input-group-append > button").trigger("click");
|
||||
await wrapper
|
||||
.findAll("div.dropdown-menu > a.dropdown-item")
|
||||
.wrappers.find((e) => e.text() == ".1.fastq")
|
||||
.trigger("click");
|
||||
await wrapper.vm.$nextTick();
|
||||
//assert forward filter
|
||||
const forwardFilter = wrapper.find("div.forward-unpaired-filter > input").element.value;
|
||||
expect(forwardFilter).toBe(".1.fastq");
|
||||
@@ -96,8 +93,7 @@ describe("PairedListCollectionCreator", () => {
|
||||
const reverseFilter = wrapper.find("div.reverse-unpaired-filter > input").element.value;
|
||||
expect(reverseFilter).toBe(".2.fastq");
|
||||
// click Autopair
|
||||
wrapper.find("a.autopair-link").trigger("click");
|
||||
await wrapper.vm.$nextTick();
|
||||
await wrapper.find("a.autopair-link").trigger("click");
|
||||
//assert pair-name longer name
|
||||
const pairname = wrapper.find("span.pair-name");
|
||||
expect(pairname.text()).toBe("UII_moo_1");
|
||||
@@ -121,10 +117,8 @@ describe("PairedListCollectionCreator", () => {
|
||||
});
|
||||
await wrapper.vm.$nextTick();
|
||||
//change filter to _R1/_R2
|
||||
wrapper.find("div.forward-unpaired-filter > input").setValue("_R1");
|
||||
await wrapper.vm.$nextTick();
|
||||
wrapper.find("div.reverse-unpaired-filter > input").setValue("_R2");
|
||||
await wrapper.vm.$nextTick();
|
||||
await wrapper.find("div.forward-unpaired-filter > input").setValue("_R1");
|
||||
await wrapper.find("div.reverse-unpaired-filter > input").setValue("_R2");
|
||||
//assert forward filter
|
||||
const forwardFilter = wrapper.find("div.forward-unpaired-filter > input").element.value;
|
||||
expect(forwardFilter).toBe("_R1");
|
||||
@@ -132,8 +126,7 @@ describe("PairedListCollectionCreator", () => {
|
||||
const reverseFilter = wrapper.find("div.reverse-unpaired-filter > input").element.value;
|
||||
expect(reverseFilter).toBe("_R2");
|
||||
// click Autopair
|
||||
wrapper.find("a.autopair-link").trigger("click");
|
||||
await wrapper.vm.$nextTick();
|
||||
await wrapper.find("a.autopair-link").trigger("click");
|
||||
//assert all pairs matched
|
||||
expect(wrapper.findAll("li.dataset unpaired").length == 0).toBeTruthy();
|
||||
});
|
||||
|
||||
@@ -248,13 +248,7 @@
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="
|
||||
unpaired-filter
|
||||
forward-unpaired-filter
|
||||
float-left
|
||||
search-input search-query
|
||||
input-group
|
||||
">
|
||||
class="unpaired-filter forward-unpaired-filter float-left search-input search-query input-group">
|
||||
<input
|
||||
type="text"
|
||||
:placeholder="filterTextPlaceholder"
|
||||
@@ -307,13 +301,7 @@
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
class="
|
||||
unpaired-filter
|
||||
reverse-unpaired-filter
|
||||
float-left
|
||||
search-input search-query
|
||||
input-group
|
||||
">
|
||||
class="unpaired-filter reverse-unpaired-filter float-left search-input search-query input-group">
|
||||
<input
|
||||
type="text"
|
||||
:placeholder="filterTextPlaceholder"
|
||||
|
||||
@@ -1,42 +1,38 @@
|
||||
<template>
|
||||
<div class="search-input">
|
||||
<b-input-group>
|
||||
<b-input
|
||||
class="search-query"
|
||||
:placeholder="placeholder"
|
||||
ref="toolInput"
|
||||
size="sm"
|
||||
autocomplete="off"
|
||||
v-model="queryInput"
|
||||
:placeholder="placeholder"
|
||||
@input="delayQuery"
|
||||
@change="setQuery"
|
||||
@keydown.esc="setQuery()" />
|
||||
<font-awesome-icon v-if="loading" class="search-clear" icon="spinner" spin />
|
||||
<font-awesome-icon
|
||||
v-else
|
||||
v-b-tooltip.hover
|
||||
:title="titleClearSearch"
|
||||
class="search-clear"
|
||||
icon="times-circle"
|
||||
@click="setQuery()" />
|
||||
</div>
|
||||
@keydown.esc="setQuery('')" />
|
||||
<b-input-group-append>
|
||||
<b-button
|
||||
class="search-clear"
|
||||
size="sm"
|
||||
:title="titleClear | l"
|
||||
@click="clearBox"
|
||||
data-description="reset query">
|
||||
<icon v-if="loading" icon="spinner" spin />
|
||||
<icon v-else icon="times" />
|
||||
</b-button>
|
||||
</b-input-group-append>
|
||||
</b-input-group>
|
||||
</template>
|
||||
<script>
|
||||
import _l from "utils/localization";
|
||||
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
||||
import { library } from "@fortawesome/fontawesome-svg-core";
|
||||
import { faTimesCircle } from "@fortawesome/free-solid-svg-icons";
|
||||
import { faSpinner } from "@fortawesome/free-solid-svg-icons";
|
||||
|
||||
library.add(faSpinner);
|
||||
library.add(faTimesCircle);
|
||||
|
||||
export default {
|
||||
components: {
|
||||
FontAwesomeIcon,
|
||||
},
|
||||
props: {
|
||||
query: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
@@ -52,7 +48,7 @@ export default {
|
||||
queryInput: null,
|
||||
queryTimer: null,
|
||||
queryCurrent: null,
|
||||
titleClearSearch: _l("clear search (esc)"),
|
||||
titleClear: "clear search (esc)",
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
@@ -80,9 +76,13 @@ export default {
|
||||
this.clearTimer();
|
||||
if (this.queryCurrent !== this.queryInput || this.queryCurrent !== queryNew) {
|
||||
this.queryCurrent = this.queryInput = queryNew;
|
||||
this.$emit("onChange", this.queryCurrent);
|
||||
this.$emit("change", this.queryCurrent);
|
||||
}
|
||||
},
|
||||
clearBox() {
|
||||
this.setQuery("");
|
||||
this.$refs.toolInput.focus();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
:multiple="multiple"
|
||||
:filter="filter"
|
||||
@clicked="onClick"
|
||||
@load="load" />
|
||||
@open="onLoad" />
|
||||
</template>
|
||||
<template v-slot:buttons>
|
||||
<b-btn size="sm" class="float-left" v-if="undoShow" @click="load()">
|
||||
@@ -148,6 +148,10 @@ export default {
|
||||
this.modalShow = false;
|
||||
this.$emit("onCancel");
|
||||
},
|
||||
/** On clicking folder name div: overloader for the @click.stop in DataDialogTable **/
|
||||
onLoad(record) {
|
||||
this.load(record.url);
|
||||
},
|
||||
/** Performs server request to retrieve data records **/
|
||||
load(url) {
|
||||
url = this.urlTracker.getUrl(url);
|
||||
|
||||
@@ -11,7 +11,7 @@ export default {
|
||||
item: Object,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(["getHistoryNameById"]),
|
||||
...mapGetters("history", ["getHistoryNameById"]),
|
||||
historyName() {
|
||||
return this.getHistoryNameById(this.item.history_id);
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<div v-if="error" class="alert alert-danger" show>{{ error }}</div>
|
||||
<div v-else>
|
||||
<b-alert :variant="messageVariant" :show="showMessage">{{ message }}</b-alert>
|
||||
<delayed-input class="mb-3" @onChange="onQuery" placeholder="Search Datasets" />
|
||||
<delayed-input class="m-1 mb-3" @change="onQuery" placeholder="Search Datasets" />
|
||||
<b-table id="dataset-table" striped no-local-sorting @sort-changed="onSort" :fields="fields" :items="rows">
|
||||
<template v-slot:cell(name)="row">
|
||||
<DatasetName :item="row.item" @showDataset="onShowDataset" @copyDataset="onCopyDataset" />
|
||||
@@ -104,13 +104,13 @@ export default {
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.fetchHistories();
|
||||
this.loadHistories();
|
||||
this.root = getAppRoot();
|
||||
this.services = new Services({ root: this.root });
|
||||
this.load();
|
||||
},
|
||||
methods: {
|
||||
...mapActions(["fetchHistories"]),
|
||||
...mapActions("history", ["loadHistories"]),
|
||||
load(concat = false) {
|
||||
this.loading = true;
|
||||
this.services
|
||||
@@ -151,7 +151,7 @@ export default {
|
||||
const Galaxy = getGalaxyInstance();
|
||||
this.services
|
||||
.setHistory(item.history_id)
|
||||
.then((history) => {
|
||||
.then(() => {
|
||||
Galaxy.currHistoryPanel.loadCurrentHistory();
|
||||
})
|
||||
.catch((error) => {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import Vue from "vue";
|
||||
import axios from "axios";
|
||||
import { mount } from "@vue/test-utils";
|
||||
import { getLocalVue } from "jest/helpers";
|
||||
@@ -9,8 +8,8 @@ import flushPromises from "flush-promises";
|
||||
|
||||
const localVue = getLocalVue();
|
||||
|
||||
function buildWrapper(conversion_disable = false) {
|
||||
return mount(DatasetAttributes, {
|
||||
async function buildWrapper(conversion_disable = false) {
|
||||
const wrapper = mount(DatasetAttributes, {
|
||||
propsData: {
|
||||
datasetId: "dataset_id",
|
||||
},
|
||||
@@ -29,14 +28,15 @@ function buildWrapper(conversion_disable = false) {
|
||||
FormElement: false,
|
||||
},
|
||||
});
|
||||
await flushPromises();
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
describe("DatasetAttributes", () => {
|
||||
it("check rendering", async () => {
|
||||
const axiosMock = new MockAdapter(axios);
|
||||
axiosMock.onPut(`/dataset/set_edit`).reply(200, { message: "success", status: "success" });
|
||||
const wrapper = buildWrapper();
|
||||
await Vue.nextTick();
|
||||
const wrapper = await buildWrapper();
|
||||
expect(wrapper.findAll("button").length).toBe(6);
|
||||
expect(wrapper.findAll("#attribute_text").length).toBe(1);
|
||||
expect(wrapper.findAll("#conversion_text").length).toBe(1);
|
||||
@@ -50,8 +50,7 @@ describe("DatasetAttributes", () => {
|
||||
});
|
||||
|
||||
it("check rendering without conversion option", async () => {
|
||||
const wrapper = buildWrapper(true);
|
||||
await Vue.nextTick();
|
||||
const wrapper = await buildWrapper(true);
|
||||
expect(wrapper.findAll("button").length).toBe(5);
|
||||
expect(wrapper.findAll("#attribute_text").length).toBe(1);
|
||||
expect(wrapper.findAll("#conversion_text").length).toBe(0);
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<b-alert v-if="messageText" :variant="messageVariant" show>
|
||||
{{ messageText | l }}
|
||||
</b-alert>
|
||||
<DatasetAttributesProvider :id="datasetId" :use-cache="false" v-slot="{ result, loading }" @error="onError">
|
||||
<DatasetAttributesProvider :id="datasetId" v-slot="{ result, loading }" @error="onError">
|
||||
<div v-if="!loading" class="mt-3">
|
||||
<b-tabs>
|
||||
<b-tab v-if="!result['attribute_disable']">
|
||||
|
||||
+3
-3
@@ -2,7 +2,7 @@
|
||||
<ConfigProvider v-slot="{ config }">
|
||||
<DatasetProvider
|
||||
:id="datasetId"
|
||||
v-slot="{ item: dataset, loading: isDatasetLoading, error: datasetLoadingError }">
|
||||
v-slot="{ result: dataset, loading: isDatasetLoading, error: datasetLoadingError }">
|
||||
<div>
|
||||
<LoadingSpan v-if="isDatasetLoading" />
|
||||
<Alert v-else-if="datasetLoadingError" :message="datasetLoadingError" variant="error" />
|
||||
@@ -10,8 +10,8 @@
|
||||
<JobDetailsProvider
|
||||
v-if="!isDatasetLoading && dataset.creating_job !== null"
|
||||
:jobid="dataset.creating_job"
|
||||
v-slot="{ result: job, loading: isJobLoading }"
|
||||
:use-cache="false">
|
||||
auto-refresh
|
||||
v-slot="{ result: job, loading: isJobLoading }">
|
||||
<div v-if="!isJobLoading">
|
||||
<dataset-information class="detail" :hda_id="datasetId" />
|
||||
<job-parameters class="detail" dataset_type="hda" :dataset-id="datasetId" />
|
||||
@@ -29,7 +29,6 @@ function buildWrapper(has_duplicate_inputs = true, has_empty_inputs = true, user
|
||||
result: { has_duplicate_inputs: has_duplicate_inputs, has_empty_inputs: has_empty_inputs },
|
||||
}),
|
||||
DatasetProvider: MockProvider({
|
||||
resultLabel: "item",
|
||||
result: { id: "dataset_id", creating_job: "creating_job" },
|
||||
}),
|
||||
FontAwesomeIcon: false,
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<h4 class="alert-heading">Failed to access Dataset details.</h4>
|
||||
{{ errorMessage }}
|
||||
</b-alert>
|
||||
<DatasetProvider :id="datasetId" v-slot="{ item: dataset, loading: datasetLoading }">
|
||||
<DatasetProvider :id="datasetId" v-slot="{ result: dataset, loading: datasetLoading }">
|
||||
<JobDetailsProvider
|
||||
v-if="!datasetLoading"
|
||||
:jobid="dataset.creating_job"
|
||||
|
||||
@@ -13,7 +13,7 @@ const mockDatasetProvider = {
|
||||
render() {
|
||||
return this.$scopedSlots.default({
|
||||
loading: false,
|
||||
item: datasetResponse,
|
||||
result: datasetResponse,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<DatasetProvider :id="hda_id" v-slot="{ item: dataset, loading }">
|
||||
<DatasetProvider :id="hda_id" v-slot="{ result: dataset, loading }">
|
||||
<div v-if="!loading">
|
||||
<h3>Dataset Information</h3>
|
||||
<table id="dataset-details" class="tabletip info_data_table">
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { mount } from "@vue/test-utils";
|
||||
import { getLocalVue } from "jest/helpers";
|
||||
import FormBoolean from "./FormBoolean";
|
||||
import Vue from "vue";
|
||||
|
||||
const localVue = getLocalVue();
|
||||
|
||||
@@ -20,12 +19,11 @@ describe("FormBoolean", () => {
|
||||
it("check initial value and value change", async () => {
|
||||
const input = wrapper.find("input");
|
||||
expect(wrapper.vm.currentValue).toBe(false);
|
||||
wrapper.setProps({ value: true });
|
||||
await Vue.nextTick();
|
||||
await wrapper.setProps({ value: true });
|
||||
expect(wrapper.vm.currentValue).toBe(true);
|
||||
input.trigger("click");
|
||||
await input.trigger("click");
|
||||
expect(input.element.checked).toBe(false);
|
||||
input.trigger("click");
|
||||
await input.trigger("click");
|
||||
expect(input.element.checked).toBe(true);
|
||||
expect(wrapper.emitted().input[0][0]).toBe(true);
|
||||
});
|
||||
|
||||
@@ -52,9 +52,8 @@ describe("FormInput", () => {
|
||||
const props = { value: 50, type: "float", min: 10, max: 100 };
|
||||
const wrapper = await mountFormNumber(props);
|
||||
const input = await getInput(wrapper);
|
||||
input.setValue(number);
|
||||
input.trigger("change");
|
||||
await flushPromises();
|
||||
await input.setValue(number);
|
||||
await input.trigger("change");
|
||||
const alert = await getAlert(wrapper);
|
||||
expect(alert.exists()).toBeTruthy();
|
||||
expect(alert.text().includes(`${number} is out`)).toBeTruthy();
|
||||
|
||||
@@ -34,12 +34,12 @@ export default {
|
||||
validator: (prop) => ["integer", "float"].includes(prop.toLowerCase()),
|
||||
},
|
||||
min: {
|
||||
type: Number,
|
||||
type: [Number, String],
|
||||
required: false,
|
||||
default: undefined,
|
||||
},
|
||||
max: {
|
||||
type: Number,
|
||||
type: [Number, String],
|
||||
required: false,
|
||||
default: undefined,
|
||||
},
|
||||
@@ -51,7 +51,7 @@ export default {
|
||||
dismissCountDown: 0,
|
||||
errorMessage: "",
|
||||
fractionWarning: "This output doesn't allow fractions!",
|
||||
decimalPlaces: this.isInteger ? 0 : this.getNumberOfDecimals(this.value),
|
||||
decimalPlaces: this.type.toLowerCase() === "integer" ? 0 : this.getNumberOfDecimals(this.value),
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { mount } from "@vue/test-utils";
|
||||
import { getLocalVue } from "jest/helpers";
|
||||
import Vue from "vue";
|
||||
import FormElement from "./FormElement";
|
||||
|
||||
const localVue = getLocalVue();
|
||||
@@ -46,19 +45,17 @@ describe("FormElement", () => {
|
||||
expect(wrapper.find(".ui-form-title-text").text()).toEqual("title_text");
|
||||
expect(wrapper.findAll("span[title='Disable']").length).toEqual(1);
|
||||
expect(wrapper.emitted().input[0][0]).toEqual("initial_value");
|
||||
wrapper.find(".ui-form-collapsible-icon").trigger("click");
|
||||
await wrapper.find(".ui-form-collapsible-icon").trigger("click");
|
||||
expect(wrapper.emitted().input[1][0]).toEqual("collapsible_value");
|
||||
expect(wrapper.emitted().input[1][1]).toEqual("input");
|
||||
await Vue.nextTick();
|
||||
await wrapper.setProps({
|
||||
collapsedEnableText: "Enable Collapsible",
|
||||
collapsedDisableText: "Disable Collapsible",
|
||||
});
|
||||
expect(wrapper.findAll("span[title='Enable Collapsible']").length).toEqual(1);
|
||||
expect(wrapper.findAll("span[title='Disable Collapsible']").length).toEqual(0);
|
||||
wrapper.find(".ui-form-collapsible-icon").trigger("click");
|
||||
await wrapper.find(".ui-form-collapsible-icon").trigger("click");
|
||||
expect(wrapper.emitted().input[2][0]).toEqual("default_value");
|
||||
await Vue.nextTick();
|
||||
expect(wrapper.findAll("span[title='Disable Collapsible']").length).toEqual(1);
|
||||
expect(wrapper.findAll("span[title='Enable Collapsible']").length).toEqual(0);
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div
|
||||
class="galaxy-loader"
|
||||
:class="[`galaxy-loader_${style}`, { 'galaxy-loader_center': center }]"
|
||||
:class="[`galaxy-loader_${variant}`, { 'galaxy-loader_center': center }]"
|
||||
:style="{ transform: `scale(${size / 100})` }">
|
||||
<div class="galaxy-loader_strip-1"></div>
|
||||
<div class="galaxy-loader_strip-2"></div>
|
||||
@@ -12,7 +12,7 @@
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
style: {
|
||||
variant: {
|
||||
type: String,
|
||||
default: "light",
|
||||
},
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import { mount } from "@vue/test-utils";
|
||||
import { getLocalVue } from "jest/helpers";
|
||||
import CollectionDescription from "./CollectionDescription";
|
||||
|
||||
const localVue = getLocalVue();
|
||||
|
||||
describe("CollectionDescription", () => {
|
||||
let wrapper;
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = mount(CollectionDescription, {
|
||||
propsData: {
|
||||
collectionType: "list",
|
||||
elementCount: 10,
|
||||
},
|
||||
localVue,
|
||||
});
|
||||
});
|
||||
|
||||
it("check basics", async () => {
|
||||
const details = wrapper.findAll("span");
|
||||
expect(details.at(0).text()).toBe("a list");
|
||||
expect(details.at(1).text()).toBe("with 10 items");
|
||||
await wrapper.setProps({ elementCount: 1 });
|
||||
expect(details.at(1).text()).toBe("with 1 item");
|
||||
await wrapper.setProps({ collectionType: "paired" });
|
||||
expect(details.at(0).text()).toBe("a dataset pair");
|
||||
await wrapper.setProps({ collectionType: "list:paired" });
|
||||
expect(details.at(0).text()).toBe("a list of pairs");
|
||||
await wrapper.setProps({ collectionType: "other" });
|
||||
expect(details.at(0).text()).toBe("a nested list");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,30 @@
|
||||
<template>
|
||||
<h6 class="description mt-1">
|
||||
<span>a {{ collectionLabel | localize }}</span>
|
||||
<span v-if="elementCount == 1">with 1 item</span>
|
||||
<span v-else-if="elementCount > 1">with {{ elementCount }} items</span>
|
||||
</h6>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
collectionType: { type: String, required: true },
|
||||
elementCount: { type: Number, required: true },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
labels: {
|
||||
list: "list",
|
||||
paired: "dataset pair",
|
||||
"list:paired": "list of pairs",
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
collectionLabel() {
|
||||
return this.labels[this.collectionType] || "nested list";
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,60 @@
|
||||
import { mount } from "@vue/test-utils";
|
||||
import { getLocalVue } from "jest/helpers";
|
||||
import ContentItem from "./ContentItem";
|
||||
|
||||
const localVue = getLocalVue();
|
||||
|
||||
describe("ContentItem", () => {
|
||||
let wrapper;
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = mount(ContentItem, {
|
||||
propsData: {
|
||||
expandDataset: false,
|
||||
item: {
|
||||
id: "item_id",
|
||||
some_data: "some_data",
|
||||
tags: ["tag1", "tag2", "tag3"],
|
||||
},
|
||||
id: 1,
|
||||
isDataset: true,
|
||||
isHistoryItem: false,
|
||||
name: "name",
|
||||
selected: false,
|
||||
selectable: false,
|
||||
},
|
||||
localVue,
|
||||
});
|
||||
});
|
||||
|
||||
it("check basics", async () => {
|
||||
expect(wrapper.attributes("data-hid")).toBe("1");
|
||||
expect(wrapper.find(".content-title").text()).toBe("name");
|
||||
const tags = wrapper.find(".nametags").findAll(".badge");
|
||||
// verify tags
|
||||
expect(tags.length).toBe(3);
|
||||
for (let i = 0; i < 3; i++) {
|
||||
expect(tags.at(i).text()).toBe(`tag${i + 1}`);
|
||||
}
|
||||
// expansion button
|
||||
const $el = wrapper.find(".cursor-pointer");
|
||||
$el.trigger("click");
|
||||
expect(wrapper.emitted()["update:expand-dataset"]).toBeDefined();
|
||||
// select and unselect
|
||||
const noSelector = wrapper.find(".selector > svg");
|
||||
expect(noSelector.exists()).toBe(false);
|
||||
await wrapper.setProps({ selectable: true });
|
||||
expect(wrapper.classes()).toEqual(expect.arrayContaining(["alert-success"]));
|
||||
const selector = wrapper.find(".selector > svg");
|
||||
expect(selector.attributes("data-icon")).toBe("square");
|
||||
selector.trigger("click");
|
||||
await localVue.nextTick();
|
||||
expect(wrapper.emitted()["update:selected"][0][0]).toBe(true);
|
||||
await wrapper.setProps({ selected: true });
|
||||
selector.trigger("click");
|
||||
await localVue.nextTick();
|
||||
expect(wrapper.emitted()["update:selected"][1][0]).toBe(false);
|
||||
expect(wrapper.classes()).toEqual(expect.arrayContaining(["alert-info"]));
|
||||
expect(selector.attributes("data-icon")).toBe("check-square");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,233 @@
|
||||
<template>
|
||||
<div
|
||||
:id="contentId"
|
||||
:class="['content-item m-1 p-0 rounded', contentCls]"
|
||||
draggable
|
||||
:data-hid="id"
|
||||
:data-state="state"
|
||||
@dragstart="onDragStart">
|
||||
<div class="p-1 cursor-pointer" @click.stop="onClick">
|
||||
<div class="d-flex justify-content-between">
|
||||
<span class="p-1 font-weight-bold">
|
||||
<span v-if="selectable" class="selector">
|
||||
<icon
|
||||
v-if="selected"
|
||||
fixed-width
|
||||
size="lg"
|
||||
:icon="['far', 'check-square']"
|
||||
@click.stop="$emit('update:selected', false)" />
|
||||
<icon
|
||||
v-else
|
||||
fixed-width
|
||||
size="lg"
|
||||
:icon="['far', 'square']"
|
||||
@click.stop="$emit('update:selected', true)" />
|
||||
</span>
|
||||
<span v-if="hasStateIcon">
|
||||
<icon fixed-width :icon="contentState.icon" :spin="contentState.spin" />
|
||||
</span>
|
||||
<span class="id hid">{{ id }}</span>
|
||||
<span>:</span>
|
||||
<span class="content-title name">{{ name }}</span>
|
||||
<CollectionDescription
|
||||
v-if="!isDataset"
|
||||
:collection-type="item.collection_type"
|
||||
:element-count="item.element_count" />
|
||||
<div v-if="item.tags && item.tags.length > 0" class="nametags">
|
||||
<Nametag v-for="tag in item.tags" :key="tag" :tag="tag" />
|
||||
</div>
|
||||
</span>
|
||||
<span class="align-self-start btn-group">
|
||||
<b-button
|
||||
v-if="isDataset"
|
||||
:disabled="displayDisabled"
|
||||
:title="displayButtonTitle"
|
||||
class="px-1"
|
||||
size="sm"
|
||||
variant="link"
|
||||
@click.stop="onDisplay">
|
||||
<icon icon="eye" />
|
||||
</b-button>
|
||||
<b-button
|
||||
v-if="isHistoryItem"
|
||||
:disabled="editDisabled"
|
||||
class="px-1"
|
||||
title="Edit attributes"
|
||||
size="sm"
|
||||
variant="link"
|
||||
@click.stop="onEdit">
|
||||
<icon icon="pen" />
|
||||
</b-button>
|
||||
<b-button
|
||||
v-if="isHistoryItem && !item.deleted"
|
||||
class="px-1"
|
||||
title="Delete"
|
||||
size="sm"
|
||||
variant="link"
|
||||
:disabled="item.purged"
|
||||
@click.stop="$emit('delete', item)">
|
||||
<icon icon="trash" />
|
||||
</b-button>
|
||||
<b-button
|
||||
v-if="isHistoryItem && item.deleted"
|
||||
class="px-1"
|
||||
title="Undelete"
|
||||
size="sm"
|
||||
variant="link"
|
||||
:disabled="item.purged"
|
||||
@click.stop="$emit('undelete', item)">
|
||||
<icon icon="trash-restore" />
|
||||
</b-button>
|
||||
<b-button
|
||||
v-if="isHistoryItem && !item.visible"
|
||||
class="px-1"
|
||||
title="Unhide"
|
||||
size="sm"
|
||||
variant="link"
|
||||
@click.stop="$emit('unhide', item)">
|
||||
<icon icon="unlock" />
|
||||
</b-button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- collections are not expandable, so we only need the DatasetDetails component here -->
|
||||
<div class="detail-animation-wrapper" :class="expandDataset ? '' : 'collapsed'">
|
||||
<DatasetDetails v-if="expandDataset" @edit="onEdit" :dataset="item" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { backboneRoute, useGalaxy, iframeRedirect } from "components/plugins/legacyNavigation";
|
||||
import { Nametag } from "components/Nametags";
|
||||
import CollectionDescription from "./Collection/CollectionDescription";
|
||||
import DatasetDetails from "./Dataset/DatasetDetails";
|
||||
import STATES from "./contentStates";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CollectionDescription,
|
||||
DatasetDetails,
|
||||
Nametag,
|
||||
},
|
||||
props: {
|
||||
expandDataset: { type: Boolean, required: true },
|
||||
item: { type: Object, required: true },
|
||||
id: { type: Number, required: true },
|
||||
isDataset: { type: Boolean, default: true },
|
||||
isHistoryItem: { type: Boolean, default: true },
|
||||
name: { type: String, required: true },
|
||||
selected: { type: Boolean, default: false },
|
||||
selectable: { type: Boolean, default: false },
|
||||
},
|
||||
computed: {
|
||||
contentId() {
|
||||
return `dataset-${this.item.id}`;
|
||||
},
|
||||
contentCls() {
|
||||
const status = this.contentState && this.contentState.status;
|
||||
if (this.selected) {
|
||||
return "alert-info";
|
||||
} else if (!status) {
|
||||
return `alert-success`;
|
||||
} else {
|
||||
return `alert-${status}`;
|
||||
}
|
||||
},
|
||||
contentState() {
|
||||
return STATES[this.state] && STATES[this.state];
|
||||
},
|
||||
displayButtonTitle() {
|
||||
if (this.item.purged) {
|
||||
return "Cannot display datasets removed from disk.";
|
||||
}
|
||||
if (this.displayDisabled) {
|
||||
return "This dataset is not yet viewable.";
|
||||
}
|
||||
return "Display";
|
||||
},
|
||||
displayDisabled() {
|
||||
return this.item.purged || ["discarded", "new", "upload"].includes(this.state);
|
||||
},
|
||||
editButtonTitle() {
|
||||
if (this.item.purged) {
|
||||
return "Cannot edit attributes of datasets removed from disk.";
|
||||
}
|
||||
if (this.editDisabled) {
|
||||
return "This dataset is not yet editable.";
|
||||
}
|
||||
return "Edit Attributes";
|
||||
},
|
||||
editDisabled() {
|
||||
return (
|
||||
this.item.purged || ["discarded", "new", "upload", "queued", "running", "waiting"].includes(this.state)
|
||||
);
|
||||
},
|
||||
hasStateIcon() {
|
||||
return this.contentState && this.contentState.icon;
|
||||
},
|
||||
state() {
|
||||
if (this.item.job_state_summary) {
|
||||
for (const key of ["error", "failed", "paused", "upload", "running"]) {
|
||||
if (this.item.job_state_summary[key] > 0) {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
return "ok";
|
||||
} else {
|
||||
return this.item.state;
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onDragStart(evt) {
|
||||
evt.dataTransfer.dropEffect = "move";
|
||||
evt.dataTransfer.effectAllowed = "move";
|
||||
evt.dataTransfer.setData("text", JSON.stringify([this.item]));
|
||||
},
|
||||
onDisplay() {
|
||||
const id = this.item.id;
|
||||
useGalaxy((Galaxy) => {
|
||||
if (Galaxy.frame && Galaxy.frame.active) {
|
||||
Galaxy.frame.addDataset(id);
|
||||
} else {
|
||||
iframeRedirect(`datasets/${id}/display/?preview=True`);
|
||||
}
|
||||
});
|
||||
},
|
||||
onEdit() {
|
||||
if (this.item.collection_type) {
|
||||
backboneRoute(`collection/edit/${this.item.id}`);
|
||||
} else {
|
||||
backboneRoute("datasets/edit", { dataset_id: this.item.id });
|
||||
}
|
||||
},
|
||||
onClick() {
|
||||
if (this.isDataset) {
|
||||
this.$emit("update:expand-dataset", !this.expandDataset);
|
||||
} else {
|
||||
this.$emit("view-collection", this.item, this.name);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
.content-item:hover {
|
||||
filter: brightness(105%);
|
||||
}
|
||||
.content-item {
|
||||
.name {
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
.detail-animation-wrapper {
|
||||
overflow: hidden;
|
||||
transition: max-height 0.2s ease-out;
|
||||
height: auto;
|
||||
max-height: 400px;
|
||||
}
|
||||
.detail-animation-wrapper.collapsed {
|
||||
max-height: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,159 @@
|
||||
<template>
|
||||
<div class="dataset-actions mb-1">
|
||||
<div class="clearfix">
|
||||
<div class="btn-group float-left">
|
||||
<b-button v-if="showError" class="px-1" title="Error" size="sm" variant="link" @click.stop="onError">
|
||||
<span class="fa fa-bug" />
|
||||
</b-button>
|
||||
<b-button
|
||||
v-if="showDownloads"
|
||||
class="download-btn px-1"
|
||||
title="Download"
|
||||
size="sm"
|
||||
variant="link"
|
||||
@click.stop="onDownload">
|
||||
<span class="fa fa-save" />
|
||||
</b-button>
|
||||
<b-button
|
||||
v-if="showDownloads"
|
||||
class="px-1"
|
||||
title="Copy Link"
|
||||
size="sm"
|
||||
variant="link"
|
||||
@click.stop="onCopyLink">
|
||||
<span class="fa fa-link" />
|
||||
</b-button>
|
||||
<b-button
|
||||
v-if="showInfo"
|
||||
class="params-btn px-1"
|
||||
title="Dataset Details"
|
||||
size="sm"
|
||||
variant="link"
|
||||
@click.stop="onInfo">
|
||||
<span class="fa fa-info-circle" />
|
||||
</b-button>
|
||||
<b-button
|
||||
v-if="showRerun"
|
||||
class="rerun-btn px-1"
|
||||
title="Run Job Again"
|
||||
size="sm"
|
||||
variant="link"
|
||||
@click.stop="onRerun">
|
||||
<span class="fa fa-redo" />
|
||||
</b-button>
|
||||
<b-button
|
||||
v-if="showVisualizations"
|
||||
class="px-1"
|
||||
title="Visualize"
|
||||
size="sm"
|
||||
variant="link"
|
||||
@click.stop="onVisualize">
|
||||
<span class="fa fa-bar-chart-o" />
|
||||
</b-button>
|
||||
<b-button v-if="showRerun" class="px-1" title="Help" size="sm" variant="link" @click.stop="onRerun">
|
||||
<span class="fa fa-question" />
|
||||
</b-button>
|
||||
</div>
|
||||
<div class="btn-group float-right">
|
||||
<b-button
|
||||
class="px-1"
|
||||
title="Edit Tags"
|
||||
size="sm"
|
||||
variant="link"
|
||||
:pressed="showTags"
|
||||
@click="onToggleTags">
|
||||
<span class="fa fa-tags" />
|
||||
</b-button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="showTags" class="mb-2">
|
||||
<StatelessTags class="tags" v-model="tags" @input="onTags" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { StatelessTags } from "components/Tags";
|
||||
import { legacyNavigationMixin } from "components/plugins/legacyNavigation";
|
||||
import { prependPath } from "utils/redirect";
|
||||
import { copy as sendToClipboard } from "utils/clipboard";
|
||||
import { absPath } from "utils/redirect";
|
||||
import { updateContentFields } from "components/History/model/queries";
|
||||
|
||||
export default {
|
||||
mixins: [legacyNavigationMixin],
|
||||
components: {
|
||||
StatelessTags,
|
||||
},
|
||||
props: {
|
||||
item: { type: Object, required: true },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showTags: false,
|
||||
tags: this.item.tags,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
downloadUrl() {
|
||||
return prependPath(`api/datasets/${this.item.id}/display?to_ext=${this.item.extension}`);
|
||||
},
|
||||
showDownloads() {
|
||||
return !this.item.purged && ["ok", "failed_metadata", "error"].includes(this.item.state);
|
||||
},
|
||||
showError() {
|
||||
return this.item.state == "error";
|
||||
},
|
||||
showInfo() {
|
||||
return this.item.state != "noPermission";
|
||||
},
|
||||
showRerun() {
|
||||
return (
|
||||
this.item.rerunnable &&
|
||||
this.item.creating_job &&
|
||||
this.item.state != "upload" &&
|
||||
this.item.state != "noPermission"
|
||||
);
|
||||
},
|
||||
showVisualizations() {
|
||||
// TODO: Check hasViz, if visualizations are activated in the config
|
||||
return !this.item.purged && ["ok", "failed_metadata", "error"].includes(this.item.state);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onCopyLink() {
|
||||
const msg = this.localize("Link is copied to your clipboard");
|
||||
sendToClipboard(absPath(this.downloadUrl), msg);
|
||||
},
|
||||
onDownload() {
|
||||
window.location.href = this.downloadUrl;
|
||||
},
|
||||
onError() {
|
||||
this.backboneRoute("datasets/error", { dataset_id: this.item.id });
|
||||
},
|
||||
onInfo() {
|
||||
this.backboneRoute(`datasets/${this.item.id}/details`);
|
||||
},
|
||||
onRerun() {
|
||||
this.backboneRoute(`root?job_id=${this.item.creating_job}`);
|
||||
},
|
||||
onTags() {
|
||||
updateContentFields(this.item, { tags: this.tags });
|
||||
},
|
||||
onToggleTags() {
|
||||
this.showTags = !this.showTags;
|
||||
},
|
||||
onVisualize() {
|
||||
const path = `visualizations?dataset_id=${this.item.id}`;
|
||||
const redirectParams = {
|
||||
path: path,
|
||||
title: "Dataset details",
|
||||
tryIframe: false,
|
||||
};
|
||||
if (!this.iframeAdd(redirectParams)) {
|
||||
this.backboneRoute(path);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<DatasetProvider :id="dataset.id" auto-refresh v-slot="{ loading, result }">
|
||||
<div v-if="!loading" class="dataset">
|
||||
<div class="p-2 details">
|
||||
<div class="summary">
|
||||
<div v-if="stateText" class="mb-1">{{ stateText }}</div>
|
||||
<div v-else-if="result.misc_blurb" class="blurb">
|
||||
<span class="value">{{ result.misc_blurb }}</span>
|
||||
</div>
|
||||
<span v-if="result.file_ext" class="datatype">
|
||||
<label class="prompt" v-localize>format</label>
|
||||
<span class="value">{{ result.file_ext }}</span>
|
||||
</span>
|
||||
<span v-if="result.genome_build" class="dbkey">
|
||||
<label class="prompt" v-localize>database</label>
|
||||
<b-link class="value" @click.stop="$emit('edit', dataset)" data-label="Database/Build">{{
|
||||
result.genome_build
|
||||
}}</b-link>
|
||||
</span>
|
||||
<div v-if="result.misc_info" class="info">
|
||||
<span class="value">{{ result.misc_info }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<DatasetActions :item="result" />
|
||||
<pre v-if="result.peek" class="dataset-peek p-1" v-html="result.peek" />
|
||||
</div>
|
||||
</div>
|
||||
</DatasetProvider>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import STATES from "components/History/Content/contentStates";
|
||||
import { DatasetProvider } from "components/providers/storeProviders";
|
||||
import DatasetActions from "./DatasetActions";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
DatasetActions,
|
||||
DatasetProvider,
|
||||
},
|
||||
props: {
|
||||
dataset: { type: Object, required: true },
|
||||
},
|
||||
computed: {
|
||||
stateText() {
|
||||
return STATES[this.dataset.state] && STATES[this.dataset.state].text;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<div>
|
||||
<ContentItem
|
||||
:item="item.object"
|
||||
:id="item.element_index"
|
||||
:name="item.element_identifier"
|
||||
:is-dataset="item.element_type == 'hda'"
|
||||
:expand-dataset="expandDataset"
|
||||
:is-history-item="false"
|
||||
@update:expand-dataset="expandDataset = $event"
|
||||
@view-collection="viewCollection = !viewCollection" />
|
||||
<GenericItem v-if="viewCollection" :itemId="item.object.id" :itemSrc="item.object.history_content_type" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ContentItem from "./ContentItem";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ContentItem,
|
||||
GenericItem: () => import("components/History/Content/GenericItem"),
|
||||
},
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
viewCollection: false,
|
||||
expandDataset: false,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<component :is="providerComponent" :id="itemId" auto-refresh v-slot="{ result: item, loading }">
|
||||
<loading-span v-if="loading" message="Loading dataset" />
|
||||
<div v-else>
|
||||
<ContentItem
|
||||
:item="item"
|
||||
:id="item.hid"
|
||||
:name="item.name"
|
||||
:expand-dataset="expandDataset"
|
||||
:is-dataset="item.history_content_type == 'dataset'"
|
||||
@update:expand-dataset="expandDataset = $event"
|
||||
@view-collection="viewCollection = !viewCollection"
|
||||
@delete="onDelete"
|
||||
@undelete="onUndelete"
|
||||
@unhide="onUnhide" />
|
||||
<div v-if="viewCollection">
|
||||
<div v-for="(collectionItem, collectionIndex) in item.elements" :key="collectionIndex">
|
||||
<GenericElement :item="collectionItem" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</component>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import LoadingSpan from "components/LoadingSpan";
|
||||
import { DatasetCollectionProvider, DatasetProvider } from "components/providers";
|
||||
import { deleteContent, updateContentFields } from "components/History/model/queries";
|
||||
import ContentItem from "./ContentItem";
|
||||
import GenericElement from "./GenericElement";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ContentItem,
|
||||
GenericElement,
|
||||
DatasetProvider,
|
||||
DatasetCollectionProvider,
|
||||
LoadingSpan,
|
||||
},
|
||||
props: {
|
||||
itemId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
itemSrc: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
viewCollection: false,
|
||||
expandDataset: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
providerComponent() {
|
||||
return this.itemSrc == "hda" ? "DatasetProvider" : "DatasetCollectionProvider";
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onDelete(item) {
|
||||
deleteContent(item);
|
||||
},
|
||||
onUndelete(item) {
|
||||
updateContentFields(item, { deleted: false });
|
||||
},
|
||||
onHide(item) {
|
||||
updateContentFields(item, { visible: false });
|
||||
},
|
||||
onUnhide(item) {
|
||||
updateContentFields(item, { visible: true });
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
+31
-15
@@ -1,38 +1,55 @@
|
||||
/**
|
||||
* This is simple list management for the monent, but if I remember right we have plans for a more
|
||||
* abstracted selection scheme in the future for purposes of handling large bulk operations on the
|
||||
* server, this is a good place to implement that logic.
|
||||
* This component deals with item selection inside a history.
|
||||
* It allows to select individual items or perform a query selection.
|
||||
*/
|
||||
|
||||
import { getFilters, testFilters } from "store/historyStore/model/filtering";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
scopeKey: { type: String, required: true },
|
||||
getItemKey: { type: Function, required: true },
|
||||
filterText: { type: String, required: true },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
items: new Map(),
|
||||
showSelection: false,
|
||||
totalItemsInQuery: 0,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
selectionSize() {
|
||||
return this.items.size;
|
||||
return this.isQuerySelection ? this.totalItemsInQuery : this.items.size;
|
||||
},
|
||||
isQuerySelection() {
|
||||
return this.totalItemsInQuery !== this.items.size;
|
||||
},
|
||||
currentFilters() {
|
||||
return getFilters(this.filterText);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
setShowSelection(val) {
|
||||
this.showSelection = val;
|
||||
},
|
||||
selectAllInCurrentQuery(loadedItems = [], totalItemsInQuery) {
|
||||
this.selectItems(loadedItems);
|
||||
this.totalItemsInQuery = totalItemsInQuery;
|
||||
},
|
||||
isSelected(item) {
|
||||
if (this.isQuerySelection) {
|
||||
return testFilters(this.currentFilters, item);
|
||||
}
|
||||
const key = this.getItemKey(item);
|
||||
return this.items.has(key);
|
||||
},
|
||||
setSelected(item, bSelected) {
|
||||
setSelected(item, selected) {
|
||||
const key = this.getItemKey(item);
|
||||
const newSelected = new Map(this.items);
|
||||
bSelected ? newSelected.set(key, item) : newSelected.delete(key);
|
||||
selected ? newSelected.set(key, item) : newSelected.delete(key);
|
||||
this.items = newSelected;
|
||||
this.resetQuerySelection();
|
||||
},
|
||||
selectItems(items = []) {
|
||||
const newItems = [...this.items.values(), ...items];
|
||||
@@ -41,17 +58,14 @@ export default {
|
||||
return [key, item];
|
||||
});
|
||||
this.items = new Map(newEntries);
|
||||
this.resetQuerySelection();
|
||||
},
|
||||
resetQuerySelection() {
|
||||
this.totalItemsInQuery = this.items.size;
|
||||
},
|
||||
// unselectItems(items = []) {
|
||||
// const doomedKeys = items.map((item) => this.getItemKey(item));
|
||||
// const existing = this.items.entries();
|
||||
// const newEntries = Array.from(existing).filter(([key]) => {
|
||||
// return !doomedKeys.includes(key);
|
||||
// });
|
||||
// this.items = new Map(newEntries);
|
||||
// },
|
||||
reset() {
|
||||
this.items = new Map();
|
||||
this.resetQuerySelection();
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
@@ -75,9 +89,11 @@ export default {
|
||||
return this.$scopedSlots.default({
|
||||
selectedItems: this.items,
|
||||
showSelection: this.showSelection,
|
||||
isQuerySelection: this.isQuerySelection,
|
||||
selectionSize: this.selectionSize,
|
||||
setShowSelection: this.setShowSelection,
|
||||
selectAllInCurrentQuery: this.selectAllInCurrentQuery,
|
||||
selectItems: this.selectItems,
|
||||
// unselectItems: this.unselectItems,
|
||||
isSelected: this.isSelected,
|
||||
setSelected: this.setSelected,
|
||||
resetSelection: this.reset,
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
Client representation of state and state messages. See: https://github.com/galaxyproject/galaxy/blob/dev/lib/galaxy/model/__init__.py#L3292
|
||||
for a list of available states.
|
||||
*/
|
||||
export default {
|
||||
/** deleted while uploading */
|
||||
discarded: {
|
||||
status: "danger",
|
||||
text: "The job creating this dataset was cancelled before completion.",
|
||||
icon: "exclamation-triangle",
|
||||
},
|
||||
/** has no data */
|
||||
empty: {
|
||||
status: "success",
|
||||
text: "No data.",
|
||||
},
|
||||
/** the tool producing this dataset failed */
|
||||
error: {
|
||||
status: "danger",
|
||||
text: "An error occurred with this dataset:",
|
||||
icon: "exclamation-triangle",
|
||||
},
|
||||
/** metadata discovery/setting failed or errored (but otherwise ok) */
|
||||
failed_metadata: {
|
||||
status: "danger",
|
||||
icon: "exclamation-triangle",
|
||||
},
|
||||
/** was created without a tool */
|
||||
new: {
|
||||
status: "warning",
|
||||
text: "This is a new dataset and not all of its data are available yet.",
|
||||
icon: "clock",
|
||||
},
|
||||
/** has successfully completed running */
|
||||
ok: {
|
||||
status: "success",
|
||||
},
|
||||
/** the job that will produce the dataset paused */
|
||||
paused: {
|
||||
status: "info",
|
||||
text: "This job is paused. Use the 'Resume Paused Jobs' in the history menu to resume.",
|
||||
icon: "pause",
|
||||
},
|
||||
/** the job that will produce the dataset queued in the runner */
|
||||
queued: {
|
||||
status: "warning",
|
||||
text: "This job is waiting to run.",
|
||||
icon: "clock",
|
||||
},
|
||||
/** the job that will produce the dataset is running */
|
||||
running: {
|
||||
status: "warning",
|
||||
text: "This job is currently running.",
|
||||
icon: "spinner",
|
||||
spin: true,
|
||||
},
|
||||
/** metadata for the dataset is being discovered/set */
|
||||
setting_metadata: {
|
||||
status: "warning",
|
||||
text: "Metadata is being auto-detected.",
|
||||
icon: "spinner",
|
||||
spin: true,
|
||||
},
|
||||
/** is uploading and not ready */
|
||||
upload: {
|
||||
status: "warning",
|
||||
text: "This dataset is currently uploading.",
|
||||
icon: "spinner",
|
||||
spin: true,
|
||||
},
|
||||
};
|
||||
@@ -1,92 +0,0 @@
|
||||
<template>
|
||||
<b-input-group v-if="params">
|
||||
<DebouncedInput v-model.trim="filterText" v-slot="{ value, input }">
|
||||
<b-form-input
|
||||
size="sm"
|
||||
:value="value"
|
||||
@input="input"
|
||||
:placeholder="'Search Filter' | localize"
|
||||
data-description="filter text input" />
|
||||
</DebouncedInput>
|
||||
|
||||
<b-input-group-append>
|
||||
<b-button
|
||||
size="sm"
|
||||
:pressed="showDeleted"
|
||||
:variant="showDeleted ? 'info' : 'secondary'"
|
||||
@click="showDeleted = !showDeleted"
|
||||
data-description="show deleted filter toggle">
|
||||
{{ "Deleted" | localize }}
|
||||
</b-button>
|
||||
<b-button
|
||||
size="sm"
|
||||
:pressed="showHidden"
|
||||
:variant="showHidden ? 'info' : 'secondary'"
|
||||
@click="showHidden = !showHidden"
|
||||
data-description="show hidden filter toggle">
|
||||
{{ "Hidden" | localize }}
|
||||
</b-button>
|
||||
</b-input-group-append>
|
||||
</b-input-group>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { SearchParams } from "components/providers/History/SearchParams";
|
||||
import DebouncedInput from "components/DebouncedInput";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
DebouncedInput,
|
||||
},
|
||||
props: {
|
||||
params: { type: SearchParams, required: true },
|
||||
},
|
||||
computed: {
|
||||
skip: {
|
||||
get() {
|
||||
return this.params.skip;
|
||||
},
|
||||
set(newSkip) {
|
||||
const newParams = this.params.clone();
|
||||
newParams.skip = newSkip;
|
||||
this.updateParams(newParams);
|
||||
},
|
||||
},
|
||||
filterText: {
|
||||
get() {
|
||||
return this.params.filterText;
|
||||
},
|
||||
set(newVal) {
|
||||
const newParams = this.params.clone();
|
||||
newParams.filterText = newVal;
|
||||
this.updateParams(newParams);
|
||||
},
|
||||
},
|
||||
showDeleted: {
|
||||
get() {
|
||||
return this.params.showDeleted;
|
||||
},
|
||||
set(newFlag) {
|
||||
const newParams = this.params.clone();
|
||||
newParams.showDeleted = newFlag;
|
||||
this.updateParams(newParams);
|
||||
},
|
||||
},
|
||||
showHidden: {
|
||||
get() {
|
||||
return this.params.showHidden;
|
||||
},
|
||||
set(newFlag) {
|
||||
const newParams = this.params.clone();
|
||||
newParams.showHidden = newFlag;
|
||||
this.updateParams(newParams);
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
updateParams(newParams) {
|
||||
this.$emit("update:params", newParams);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -1,19 +0,0 @@
|
||||
import ContentItem from "./ContentItem";
|
||||
|
||||
export default {
|
||||
mixins: [ContentItem],
|
||||
|
||||
computed: {
|
||||
contentItemComponent() {
|
||||
const { history_content_type } = this.item;
|
||||
switch (history_content_type) {
|
||||
case "dataset":
|
||||
return "Subdataset";
|
||||
case "dataset_collection":
|
||||
return "Subcollection";
|
||||
default:
|
||||
return "Placeholder";
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -1,83 +0,0 @@
|
||||
/**
|
||||
* This is a single item in the list. The main job of the contentItem is to
|
||||
* pick a specific selector for the :is attribute of the generic component
|
||||
* placeholder. This should be a dataset, collection, or other component
|
||||
* depending on the specific list implementation.
|
||||
*
|
||||
* The mixin also pulls out "selected", and "expanded" properties which are
|
||||
* derived from the current component state rather than the data itself.
|
||||
*/
|
||||
|
||||
import Placeholder from "./Placeholder";
|
||||
import Dataset from "./Dataset";
|
||||
import DatasetCollection from "./DatasetCollection";
|
||||
import Subdataset from "./Subdataset";
|
||||
import Subcollection from "./Subcollection";
|
||||
|
||||
export default {
|
||||
template: `
|
||||
<component :is="contentItemComponent"
|
||||
:data-ci-type="contentItemComponent"
|
||||
class="content-item"
|
||||
:class="{ loading }"
|
||||
:tabindex="index"
|
||||
:writable="writable"
|
||||
v-on="$listeners"
|
||||
v-bind="$attrs"
|
||||
:item="item"
|
||||
:index="index"
|
||||
:row-key="rowKey"
|
||||
:writable="writable"
|
||||
@mouseover.native.stop="setFocus(index)"
|
||||
@keydown.native.arrow-up.self.stop="setFocus(index - 1)"
|
||||
@keydown.native.arrow-down.self.stop="setFocus(index + 1)"
|
||||
/>
|
||||
`,
|
||||
|
||||
components: {
|
||||
Placeholder,
|
||||
Dataset,
|
||||
DatasetCollection,
|
||||
Subdataset,
|
||||
Subcollection,
|
||||
},
|
||||
|
||||
props: {
|
||||
item: { type: Object, required: true },
|
||||
index: { type: Number, required: false, default: null },
|
||||
rowKey: { type: [Number, String], required: false, default: "" },
|
||||
writable: { type: Boolean, required: false, default: true },
|
||||
},
|
||||
|
||||
data: () => ({
|
||||
suppressFocus: false,
|
||||
}),
|
||||
|
||||
methods: {
|
||||
setFocus(index) {
|
||||
if (this.suppressFocus) {
|
||||
return;
|
||||
}
|
||||
const ul = this.$el.closest(".scroller");
|
||||
const el = ul.querySelector(`[tabindex="${index}"]`);
|
||||
if (el) {
|
||||
el.focus();
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
loading() {
|
||||
return !this.item;
|
||||
},
|
||||
contentItemComponent() {
|
||||
// override me
|
||||
return "Placeholder";
|
||||
},
|
||||
},
|
||||
|
||||
created() {
|
||||
this.$root.$on("bv::dropdown::show", () => (this.suppressFocus = true));
|
||||
this.$root.$on("bv::dropdown::hide", () => (this.suppressFocus = false));
|
||||
},
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user