Merge branch 'dev' into topic/count-pairs-correctly

This commit is contained in:
M Bernt
2022-04-21 13:03:59 +02:00
committed by GitHub
2478 changed files with 114425 additions and 124243 deletions
+1 -1
View File
@@ -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/
+1 -1
View File
@@ -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
+7
View File
@@ -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
+1
View File
@@ -0,0 +1 @@
comment: false
+14 -3
View File
@@ -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:
+7
View File
@@ -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'
+6 -2
View File
@@ -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:
-9
View File
@@ -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 }}
+13 -2
View File
@@ -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:
+15 -2
View File
@@ -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:
+14 -2
View File
@@ -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 -1
View File
@@ -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
+6
View File
@@ -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
+8 -1
View File
@@ -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
+4 -5
View File
@@ -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'
+13 -2
View File
@@ -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:
+14 -2
View File
@@ -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
+1 -1
View File
@@ -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'
+8 -6
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+48
View File
@@ -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
View File
@@ -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).
+10 -6
View File
@@ -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
View File
@@ -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:
-5
View File
@@ -1,5 +0,0 @@
src/qunit
src/mocha
src/libs
src/nls
src/legacy
+20 -20
View File
@@ -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
View File
@@ -1 +1 @@
14.15.0
16.13.2
+2 -3
View File
@@ -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.
-5
View File
@@ -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).
-15
View File
@@ -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);
},
};
};
-142
View File
@@ -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,
};
-379
View File
@@ -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">&times;</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">&times;</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">&times;</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">&times;</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
View File
@@ -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
-62
View 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
View File
@@ -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
View File
@@ -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"
}
}
-1
View File
@@ -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"
+26 -26
View File
@@ -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']">
@@ -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);
});
+2 -2
View File
@@ -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>
@@ -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