diff --git a/.copier-answers.yml b/.copier-answers.yml new file mode 100644 index 0000000..0cb0b21 --- /dev/null +++ b/.copier-answers.yml @@ -0,0 +1,13 @@ +# Changes here will be overwritten by Copier; NEVER EDIT MANUALLY +_commit: v4.3.5 +_src_path: https://github.com/jupyterlab/extension-template +author_email: admin@geostorm.eu +author_name: CS Group +has_binder: false +has_settings: true +kind: server +labextension_name: eodag-labextension +project_short_description: Searching remote sensed imagery from various image providers +python_name: eodag_labextension +repository: https://github.com/CS-SI/eodag-labextension.git +test: false diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 4e68236..0000000 --- a/.eslintignore +++ /dev/null @@ -1,6 +0,0 @@ -node_modules -dist -coverage -**/*.d.ts -tests -.eslintrc.js diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index 665374b..0000000 --- a/.eslintrc.js +++ /dev/null @@ -1,39 +0,0 @@ -module.exports = { - extends: [ - 'eslint:recommended', - 'plugin:@typescript-eslint/eslint-recommended', - 'plugin:@typescript-eslint/recommended', - 'plugin:prettier/recommended' - ], - parser: '@typescript-eslint/parser', - parserOptions: { - project: 'tsconfig.json', - sourceType: 'module' - }, - plugins: ['@typescript-eslint'], - rules: { - '@typescript-eslint/naming-convention': [ - 'error', - { - selector: 'interface', - format: ['PascalCase'], - custom: { - regex: '^I[A-Z]', - match: true - } - } - ], - '@typescript-eslint/no-unused-vars': ['warn', { args: 'none' }], - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-namespace': 'off', - '@typescript-eslint/no-use-before-define': 'off', - '@typescript-eslint/quotes': [ - 'error', - 'single', - { avoidEscape: true, allowTemplateLiterals: false } - ], - curly: ['error', 'all'], - eqeqeq: 'error', - 'prefer-arrow-callback': 'error' - } -}; diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4cccebb..506ce99 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,9 +28,10 @@ jobs: - name: Checkout uses: actions/checkout@v2 - name: Install node - uses: actions/setup-node@v1 + uses: actions/setup-node@v3 with: node-version: '18.x' + cache: 'yarn' - name: Install Python uses: actions/setup-python@v2 with: @@ -38,7 +39,7 @@ jobs: architecture: 'x64' - name: Setup pip cache - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ~/.cache/pip key: pip-3.9-${{ hashFiles('package.json') }} @@ -46,20 +47,8 @@ jobs: pip-3.9- pip- - - name: Get yarn cache directory path - id: yarn-cache-dir-path - run: echo "::set-output name=dir::$(yarn cache dir)" - - name: Setup yarn cache - uses: actions/cache@v2 - id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) - with: - path: ${{ steps.yarn-cache-dir-path.outputs.dir }} - key: yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - yarn- - - name: Install dependencies - run: python -m pip install -U jupyterlab~=3.0 jupyter_packaging~=0.7.9 + run: python -m pip install -U jupyterlab~=4.0 jupyter_packaging~=0.7.9 - name: Build the extension run: | jlpm diff --git a/.gitignore b/.gitignore index 2ffdca2..f5a43ea 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,15 @@ *.bundle.* lib/ node_modules/ +*.log +.eslintcache +.stylelintcache *.egg-info/ .ipynb_checkpoints *.tsbuildinfo eodag_labextension/labextension +# Version file is handled by hatchling +eodag_labextension/_version.py # Created by https://www.gitignore.io/api/python # Edit at https://www.gitignore.io/?templates=python @@ -56,6 +61,7 @@ htmlcov/ .coverage.* .cache nosetests.xml +coverage/ coverage.xml *.cover .hypothesis/ @@ -111,6 +117,10 @@ dmypy.json # OSX files .DS_Store +# Yarn cache +.yarn/ + # IDE -.idea -.vscode +.idea/ + +.venv/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 13ef75e..35ad3f3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -38,6 +38,8 @@ repos: rev: v8.24.0 hooks: - id: eslint + language: node + language_version: '22.11.0' additional_dependencies: - eslint@8.23.1 - typescript@4.3.5 @@ -51,8 +53,12 @@ repos: rev: v2.7.1 hooks: - id: prettier + language: node + language_version: '22.11.0' - repo: https://github.com/igorshubovych/markdownlint-cli rev: v0.32.2 hooks: - id: markdownlint + language: node + language_version: '22.11.0' diff --git a/.prettierignore b/.prettierignore index 4032abd..abfcdca 100644 --- a/.prettierignore +++ b/.prettierignore @@ -2,4 +2,5 @@ node_modules **/node_modules **/lib **/package.json -eodag-labextension +!/package.json +eodag_labextension diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index b0a179d..0000000 --- a/.prettierrc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "singleQuote": true, - "trailingComma": "none", - "arrowParens": "avoid" -} diff --git a/.yarnrc.yml b/.yarnrc.yml new file mode 100644 index 0000000..43ecc39 --- /dev/null +++ b/.yarnrc.yml @@ -0,0 +1,6 @@ +compressionLevel: mixed + +enableGlobalCache: false + +enableImmutableInstalls: false +nodeLinker: node-modules diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..2d352af --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,5 @@ +# Changelog + + + + diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 0000000..d804a56 --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,80 @@ +# Making a new release of eodag_labextension + +The extension can be published to `PyPI` and `npm` manually or using the [Jupyter Releaser](https://github.com/jupyter-server/jupyter_releaser). + +## Manual release + +### Python package + +This extension can be distributed as Python packages. All of the Python +packaging instructions are in the `pyproject.toml` file to wrap your extension in a +Python package. Before generating a package, you first need to install some tools: + +```bash +pip install build twine hatch +``` + +Bump the version using `hatch`. By default this will create a tag. +See the docs on [hatch-nodejs-version](https://github.com/agoose77/hatch-nodejs-version#semver) for details. + +```bash +hatch version +``` + +Make sure to clean up all the development files before building the package: + +```bash +jlpm clean:all +``` + +You could also clean up the local git repository: + +```bash +git clean -dfX +``` + +To create a Python source package (`.tar.gz`) and the binary package (`.whl`) in the `dist/` directory, do: + +```bash +python -m build +``` + +> `python setup.py sdist bdist_wheel` is deprecated and will not work for this package. + +Then to upload the package to PyPI, do: + +```bash +twine upload dist/* +``` + +### NPM package + +To publish the frontend part of the extension as a NPM package, do: + +```bash +npm login +npm publish --access public +``` + +## Automated releases with the Jupyter Releaser + +The extension repository should already be compatible with the Jupyter Releaser. But +the GitHub repository and the package managers need to be properly set up. Please +follow the instructions of the Jupyter Releaser [checklist](https://jupyter-releaser.readthedocs.io/en/latest/how_to_guides/convert_repo_from_repo.html). + +Here is a summary of the steps to cut a new release: + +- Go to the Actions panel +- Run the "Step 1: Prep Release" workflow +- Check the draft changelog +- Run the "Step 2: Publish Release" workflow + +> [!NOTE] +> Check out the [workflow documentation](https://jupyter-releaser.readthedocs.io/en/latest/get_started/making_release_from_repo.html) +> for more information. + +## Publishing to `conda-forge` + +If the package is not on conda forge yet, check the documentation to learn how to add it: + +Otherwise a bot should pick up the new version publish to PyPI, and open a new PR on the feedstock repository automatically. diff --git a/jupyter-config/server-config/eodag_labextension.json b/jupyter-config/server-config/eodag_labextension.json new file mode 100644 index 0000000..bfd01a8 --- /dev/null +++ b/jupyter-config/server-config/eodag_labextension.json @@ -0,0 +1,7 @@ +{ + "ServerApp": { + "jpserver_extensions": { + "eodag_labextension": true + } + } +} diff --git a/package.json b/package.json index 8b2a452..c9f497a 100644 --- a/package.json +++ b/package.json @@ -1,113 +1,225 @@ { - "name": "eodag-labextension", - "version": "3.7.0", - "description": "Searching remote sensed imagery from various image providers", - "keywords": [ - "jupyter", - "jupyterlab", - "jupyterlab-extension" - ], - "homepage": "https://github.com/CS-SI/eodag-labextension", - "bugs": { - "url": "https://github.com/CS-SI/eodag-labextension/issues" - }, - "license": "Apache-2.0", - "author": { - "name": "CS Group", - "email": "admin@geostorm.eu" - }, - "files": [ - "lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}", - "style/**/*.{css,eot,gif,html,jpg,json,png,svg,woff2,ttf}", - "schema/*.json", - "node_modules/leaflet/dist/**/*.{css,png}", - "style/index.js" - ], - "main": "lib/index.js", - "types": "lib/index.d.ts", - "repository": { - "type": "git", - "url": "https://github.com/CS-SI/eodag-labextension.git" - }, - "scripts": { - "build": "jlpm run build:lib && jlpm run build:labextension:dev", - "build:labextension": "jupyter labextension build .", - "build:labextension:dev": "jupyter labextension build --development True .", - "build:lib": "tsc", - "build:prod": "jlpm run build:lib && jlpm run build:labextension", - "clean": "jlpm run clean:lib", - "clean:all": "jlpm run clean:lib && jlpm run clean:labextension", - "clean:labextension": "rimraf eodag_labextension/labextension", - "clean:lib": "rimraf lib tsconfig.tsbuildinfo", - "eslint": "eslint . --ext .ts,.tsx --fix", - "eslint:check": "eslint . --ext .ts,.tsx", - "install:extension": "jupyter labextension develop --overwrite .", - "prepare": "jlpm run clean && jlpm run build:prod", - "watch": "run-p watch:src watch:labextension", - "watch:labextension": "jupyter labextension watch .", - "watch:src": "tsc -w" - }, - "dependencies": { - "@fortawesome/fontawesome-svg-core": "6.2.0", - "@fortawesome/free-solid-svg-icons": "6.2.0", - "@fortawesome/react-fontawesome": "^0.2.0", - "@jupyterlab/application": "^3.4.8", - "@jupyterlab/apputils": "^3.4.8", - "@jupyterlab/cells": "^3.4.8", - "@jupyterlab/notebook": "^3.4.8", - "@jupyterlab/settingregistry": "^3.4.8", - "@terraformer/wkt": "^2.1.2", - "install": "^0.13.0", - "isomorphic-fetch": "^3.0.0", - "leaflet": "1.8.0", - "leaflet-draw": "^1.0.4", - "lodash": "4.17.21", - "react": "^17.0.2", - "react-datepicker": "4.8.0", - "react-dom": "^17.0.2", - "react-hook-form": "^7.39.3", - "react-leaflet": "~2.8.0", - "react-leaflet-draw": "0.19.0", - "react-loader-spinner": "^5.3.4", - "react-modal": "3.15.1", - "react-select": "5.4.0", - "react-tooltip": "~5.26.3", - "react-virtualized": "^9.22.3" - }, - "devDependencies": { - "@jupyterlab/builder": "^3.5.2", - "@types/classnames": "^2.3.1", - "@types/isomorphic-fetch": "^0.0.36", - "@types/leaflet": "1.8.0", - "@types/leaflet-draw": "^1.0.5", - "@types/lodash": "^4.14.185", - "@types/luxon": "^3.0.1", - "@types/react": "^17.0.50", - "@types/react-datepicker": "4.4.2", - "@types/react-dom": "^17.0.17", - "@types/react-leaflet": "^2.8.2", - "@types/react-modal": "3.13.1", - "@types/react-select": "4.0.18", - "@types/react-virtualized": "^9.21.21", - "@typescript-eslint/eslint-plugin": "^5.38.0", - "@typescript-eslint/parser": "^5.38.0", - "eslint": "^8.23.1", - "eslint-config-prettier": "^8.5.0", - "eslint-plugin-prettier": "^4.2.1", - "mkdirp": "^1.0.4", - "npm-run-all": "^4.1.5", - "prettier": "^2.7.1", - "rimraf": "^3.0.2", - "typescript": "4.3.5" - }, - "resolutions": { - "**/@types/react": "~17.0.50", - "**/@types/react-dom": "~17.0.17" - }, - "jupyterlab": { - "extension": true, - "schemaDir": "schema", - "outputDir": "eodag_labextension/labextension" - }, - "styleModule": "style/index.js" + "name": "eodag-labextension", + "version": "3.7.0", + "description": "Searching remote sensed imagery from various image providers", + "keywords": [ + "jupyter", + "jupyterlab", + "jupyterlab-extension" + ], + "homepage": "https://github.com/CS-SI/eodag-labextension", + "bugs": { + "url": "https://github.com/CS-SI/eodag-labextension/issues" + }, + "license": "Apache-2.0", + "author": { + "name": "CS Group", + "email": "admin@geostorm.eu" + }, + "files": [ + "lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}", + "style/**/*.{css,eot,gif,html,jpg,json,png,svg,woff2,ttf}", + "schema/*.json", + "node_modules/leaflet/dist/**/*.{css,png}", + "style/index.js" + ], + "main": "lib/index.js", + "types": "lib/index.d.ts", + "repository": { + "type": "git", + "url": "https://github.com/CS-SI/eodag-labextension.git" + }, + "scripts": { + "build": "jlpm run build:lib && jlpm run build:labextension:dev", + "build:labextension": "jupyter labextension build .", + "build:labextension:dev": "jupyter labextension build --development True .", + "build:lib": "tsc", + "build:prod": "jlpm run build:lib && jlpm run build:labextension", + "clean": "jlpm run clean:lib", + "clean:all": "jlpm run clean:lib && jlpm run clean:labextension", + "clean:labextension": "rimraf eodag_labextension/labextension", + "clean:lib": "rimraf lib tsconfig.tsbuildinfo", + "eslint": "eslint . --ext .ts,.tsx --fix", + "eslint:check": "eslint . --ext .ts,.tsx", + "install:extension": "jupyter labextension develop --overwrite .", + "prepare": "jlpm run clean && jlpm run build:prod", + "watch": "run-p watch:src watch:labextension", + "watch:labextension": "jupyter labextension watch .", + "watch:src": "tsc -w" + }, + "dependencies": { + "@emotion/react": "^11.14.0", + "@emotion/sheet": "^1.4.0", + "@fortawesome/fontawesome-svg-core": "6.2.0", + "@fortawesome/free-solid-svg-icons": "6.2.0", + "@fortawesome/react-fontawesome": "^0.2.0", + "@jupyterlab/application": "^4.3.0", + "@jupyterlab/apputils": "^4.4.0", + "@jupyterlab/cells": "^4.3.0", + "@jupyterlab/coreutils": "^6.3.4", + "@jupyterlab/notebook": "^4.3.0", + "@jupyterlab/services": "^7.3.4", + "@jupyterlab/settingregistry": "^4.3.0", + "@jupyterlab/ui-components": "^4.3.4", + "@lumino/signaling": "^2.1.3", + "@lumino/widgets": "^2.5.0", + "@terraformer/wkt": "^2.1.2", + "classnames": "^2.5.1", + "install": "^0.13.0", + "isomorphic-fetch": "^3.0.0", + "leaflet": "1.8.0", + "leaflet-draw": "^1.0.4", + "lodash": "4.17.21", + "react": "^18.0.2", + "react-datepicker": "4.8.0", + "react-dom": "^18.0.2", + "react-hook-form": "^7.39.3", + "react-leaflet": "4.0.0", + "react-leaflet-draw": "0.20.0", + "react-loader-spinner": "^6.1.6", + "react-modal": "3.15.1", + "react-select": "5.4.0", + "react-tooltip": "~5.26.3", + "react-virtualized": "^9.22.4" + }, + "devDependencies": { + "@jupyterlab/builder": "^4.3.4", + "@types/classnames": "^2.3.4", + "@types/geojson": "^7946.0.15", + "@types/isomorphic-fetch": "^0.0.36", + "@types/json-schema": "^7.0.11", + "@types/leaflet": "^1.7.11", + "@types/leaflet-draw": "^1.0.5", + "@types/lodash": "^4.14.185", + "@types/luxon": "^3.0.1", + "@types/react": "^18.0.26", + "@types/react-addons-linked-state-mixin": "^0.14.22", + "@types/react-datepicker": "4.4.2", + "@types/react-dom": "^18.3", + "@types/react-leaflet": "^2.8.2", + "@types/react-modal": "3.13.1", + "@types/react-select": "4.0.18", + "@types/react-virtualized": "^9.21.21", + "@typescript-eslint/eslint-plugin": "^6.1.0", + "@typescript-eslint/parser": "^6.1.0", + "corepack": "^0.30.0", + "css-loader": "^6.7.1", + "eslint": "^8.36.0", + "eslint-config-prettier": "^8.8.0", + "eslint-plugin-prettier": "^5.0.0", + "mkdirp": "^1.0.3", + "npm-run-all": "^4.1.5", + "prettier": "^3.0.0", + "rimraf": "^5.0.1", + "source-map-loader": "^1.0.2", + "style-loader": "^3.3.1", + "stylelint": "^15.10.1", + "stylelint-config-recommended": "^13.0.0", + "stylelint-config-standard": "^34.0.0", + "stylelint-csstree-validator": "^3.0.0", + "stylelint-prettier": "^4.0.0", + "typescript": "~5.0.2", + "yjs": "^13.5.40" + }, + "jupyterlab": { + "extension": true, + "schemaDir": "schema", + "outputDir": "eodag_labextension/labextension" + }, + "styleModule": "style/index.js", + "eslintConfig": { + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended", + "plugin:prettier/recommended" + ], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "project": "tsconfig.json", + "sourceType": "module" + }, + "plugins": [ + "@typescript-eslint" + ], + "rules": { + "@typescript-eslint/naming-convention": [ + "error", + { + "selector": "interface", + "format": [ + "PascalCase" + ], + "custom": { + "regex": "^I[A-Z]", + "match": true + } + } + ], + "@typescript-eslint/no-unused-vars": [ + "warn", + { + "args": "none" + } + ], + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-namespace": "off", + "@typescript-eslint/no-use-before-define": "off", + "@typescript-eslint/quotes": [ + "error", + "single", + { + "avoidEscape": true, + "allowTemplateLiterals": false + } + ], + "curly": [ + "error", + "all" + ], + "eqeqeq": "error", + "prefer-arrow-callback": "error" + } + }, + "eslintIgnore": [ + "node_modules", + "dist", + "coverage", + "**/*.d.ts" + ], + "prettier": { + "singleQuote": true, + "trailingComma": "none", + "arrowParens": "avoid", + "endOfLine": "auto", + "overrides": [ + { + "files": "package.json", + "options": { + "tabWidth": 4 + } + } + ] + }, + "stylelint": { + "extends": [ + "stylelint-config-recommended", + "stylelint-config-standard", + "stylelint-prettier/recommended" + ], + "plugins": [ + "stylelint-csstree-validator" + ], + "rules": { + "csstree/validator": true, + "property-no-vendor-prefix": null, + "selector-class-pattern": "^([a-z][A-z\\d]*)(-[A-z\\d]+)*$", + "selector-no-vendor-prefix": null, + "value-no-vendor-prefix": null + } + }, + "resolutions": { + "@types/react-dom": "18.3.5", + "@types/react": "^18.0.26" + } } diff --git a/pyproject.toml b/pyproject.toml index 63f26d9..56e9700 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["jupyter_packaging~=0.7.9", "jupyterlab~=3.0", "setuptools>=40.8.0", "wheel"] +requires = ["jupyter_packaging~=0.7.9", "jupyterlab~=4.0", "setuptools>=40.8.0", "wheel"] build-backend = "setuptools.build_meta" [tool.black] diff --git a/setup.py b/setup.py index 22e127b..07d7165 100644 --- a/setup.py +++ b/setup.py @@ -58,14 +58,11 @@ cmdclass=cmdclass, packages=setuptools.find_packages(), install_requires=[ - "jupyterlab~=3.0", + "jupyterlab~=4.0", "tornado>=6.0.3,<7.0.0", "notebook>=6.0.3,<7.0.0", "eodag[notebook]>=3.0.0b1", "orjson", - # ipyleaflet is not needed, but versions >= 0.17.4 will make the labextension crash - # try removing this dependency when updating leaflet and/or jupyterlab - "ipyleaflet<0.17.4", ], extras_require={ "dev": [ diff --git a/src/Autocomplete.tsx b/src/Autocomplete.tsx index 4733796..b0b5c9a 100644 --- a/src/Autocomplete.tsx +++ b/src/Autocomplete.tsx @@ -103,7 +103,7 @@ class IntegrationReactSelect extends React.Component { loadSuggestions } = this.props; - const currentValue: OptionTypeBase = value + const currentValue: OptionTypeBase | undefined = value ? suggestions.find(e => e.value === value) : undefined; diff --git a/src/BrowseComponent.tsx b/src/BrowseComponent.tsx index 6e5e5d9..61dfde0 100644 --- a/src/BrowseComponent.tsx +++ b/src/BrowseComponent.tsx @@ -15,7 +15,7 @@ import { TableHeaderProps, ColumnProps } from 'react-virtualized'; -import { get } from 'lodash'; +import { get, isUndefined } from 'lodash'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faSearchPlus } from '@fortawesome/free-solid-svg-icons'; @@ -90,11 +90,11 @@ class MuiVirtualizedTable extends React.PureComponent return {}; }; - cellRenderer = ({ cellData, columnIndex = null }: TableCellProps) => { + cellRenderer = ({ cellData, columnIndex = -1 }: TableCellProps) => { const { columns } = this.props; let isPercent = false; if ( - columnIndex !== null && + columnIndex !== -1 && columnIndex !== undefined && columns[columnIndex].percent ) { @@ -107,18 +107,21 @@ class MuiVirtualizedTable extends React.PureComponent } }; - buttonRenderer = ({ columnIndex = null, rowData }: TableCellProps) => { + buttonRenderer = ({ columnIndex = -1, rowData }: TableCellProps) => { const { columns } = this.props; // Retrieve event handler from column def let handleClick = (dataId: number | string) => { // do nothing. }; if ( - columnIndex !== null && + columnIndex !== -1 && columnIndex !== undefined && columns[columnIndex].handleClick ) { - handleClick = columns[columnIndex].handleClick; + const temp_handle = columns[columnIndex].handleClick; + if (!isUndefined(temp_handle)) { + handleClick = temp_handle; + } } return (