diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1023daa8..c112dac0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,6 +11,5 @@ jobs: ci: uses: shlinkio/github-actions/.github/workflows/web-app-ci.yml@main with: - node-version: 20.2 + node-version: 20.5 publish-coverage: true - force-install: true diff --git a/.github/workflows/deploy-preview.yml b/.github/workflows/deploy-preview.yml index 02d402ae..3e5ef986 100644 --- a/.github/workflows/deploy-preview.yml +++ b/.github/workflows/deploy-preview.yml @@ -5,7 +5,7 @@ on: jobs: deploy: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 continue-on-error: true steps: - name: Checkout code @@ -16,10 +16,10 @@ jobs: - name: Use node.js uses: actions/setup-node@v3 with: - node-version: 20.2 + node-version: 20.5 - name: Build run: | - npm ci --force && \ + npm ci && \ node ./scripts/set-homepage.js /shlink-web-client/${GITHUB_HEAD_REF#refs/heads/} && \ npm run build - name: Deploy preview diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index 6241ec6c..d28c30a7 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -7,16 +7,16 @@ on: jobs: build: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - name: Checkout code uses: actions/checkout@v3 - name: Use node.js uses: actions/setup-node@v3 with: - node-version: 20.2 + node-version: 20.5 - name: Generate release assets - run: npm ci --force && VERSION=${GITHUB_REF#refs/tags/v} npm run build:dist + run: npm ci && VERSION=${GITHUB_REF#refs/tags/v} npm run build:dist - name: Publish release with assets uses: docker://antonyurchenko/git-release:latest env: diff --git a/Dockerfile b/Dockerfile index 214c2292..9cdf969e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,8 @@ -FROM node:20.2-alpine as node +FROM node:20.5-alpine as node COPY . /shlink-web-client ARG VERSION="latest" ENV VERSION ${VERSION} -RUN cd /shlink-web-client && npm ci --force && npm run build +RUN cd /shlink-web-client && npm ci && npm run build FROM nginx:1.23-alpine LABEL maintainer="Alejandro Celaya " diff --git a/docker-compose.yml b/docker-compose.yml index b9bbf511..07ed7220 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,8 +3,8 @@ version: '3' services: shlink_web_client_node: container_name: shlink_web_client_node - image: node:20.2-alpine - command: /bin/sh -c "cd /home/shlink/www && npm install --force && npm run start" + image: node:20.5-alpine + command: /bin/sh -c "cd /home/shlink/www && npm install && npm run start" volumes: - ./:/home/shlink/www ports: diff --git a/package-lock.json b/package-lock.json index 928eeb18..199e96bd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -39,7 +39,7 @@ "react-redux": "^8.1.2", "react-router-dom": "^6.14.2", "react-swipeable": "^7.0.1", - "react-tag-autocomplete": "^6.3.0", + "react-tag-autocomplete": "^7.0.0", "reactstrap": "^9.2.0", "redux-localstorage-simple": "^2.5.1", "uuid": "^9.0.0", @@ -8900,16 +8900,14 @@ } }, "node_modules/react-tag-autocomplete": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/react-tag-autocomplete/-/react-tag-autocomplete-6.3.0.tgz", - "integrity": "sha512-MUBVUFh5eOqshUm5NM20qp7zXk8TzSiKO4GoktlFzBLIOLs356npaMKtL02bm0nFV2f1zInUrXn1fq6+i5YX0w==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/react-tag-autocomplete/-/react-tag-autocomplete-7.0.0.tgz", + "integrity": "sha512-PFxT7fpMB8Au+S9cJYAGRVTnacZpeXybc5SkpTCyuJHmUN1Bt8gHb9vZi3f+aX/eDX44x2WIwYiqfRBi2E5AMg==", "engines": { - "node": ">= 10.0.0" + "node": ">= 16.12.0" }, "peerDependencies": { - "prop-types": "^15.5.0", - "react": "^16.5.0 || ^17.0.0", - "react-dom": "^16.5.0 || ^17.0.0" + "react": "^18.0.0" } }, "node_modules/react-transition-group": { @@ -17211,9 +17209,9 @@ "requires": {} }, "react-tag-autocomplete": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/react-tag-autocomplete/-/react-tag-autocomplete-6.3.0.tgz", - "integrity": "sha512-MUBVUFh5eOqshUm5NM20qp7zXk8TzSiKO4GoktlFzBLIOLs356npaMKtL02bm0nFV2f1zInUrXn1fq6+i5YX0w==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/react-tag-autocomplete/-/react-tag-autocomplete-7.0.0.tgz", + "integrity": "sha512-PFxT7fpMB8Au+S9cJYAGRVTnacZpeXybc5SkpTCyuJHmUN1Bt8gHb9vZi3f+aX/eDX44x2WIwYiqfRBi2E5AMg==", "requires": {} }, "react-transition-group": { diff --git a/package.json b/package.json index 48296338..15f5cc67 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "react-redux": "^8.1.2", "react-router-dom": "^6.14.2", "react-swipeable": "^7.0.1", - "react-tag-autocomplete": "^6.3.0", + "react-tag-autocomplete": "^7.0.0", "reactstrap": "^9.2.0", "redux-localstorage-simple": "^2.5.1", "uuid": "^9.0.0", diff --git a/shlink-web-component/src/tags/helpers/TagsSelector.tsx b/shlink-web-component/src/tags/helpers/TagsSelector.tsx index 72d74fcc..53f0198e 100644 --- a/shlink-web-component/src/tags/helpers/TagsSelector.tsx +++ b/shlink-web-component/src/tags/helpers/TagsSelector.tsx @@ -1,6 +1,6 @@ import { useEffect } from 'react'; -import type { SuggestionComponentProps, TagComponentProps } from 'react-tag-autocomplete'; -import ReactTags from 'react-tag-autocomplete'; +import type { OptionRendererProps, TagRendererProps, TagSuggestion } from 'react-tag-autocomplete'; +import { ReactTags } from 'react-tag-autocomplete'; import type { ColorGenerator } from '../../utils/services/ColorGenerator'; import { useSetting } from '../../utils/settings'; import type { TagsList } from '../reducers/tagsList'; @@ -19,7 +19,7 @@ interface TagsSelectorConnectProps extends TagsSelectorProps { tagsList: TagsList; } -const toComponentTag = (tag: string) => ({ id: tag, name: tag }); +const toTagSuggestion = (tag: string): TagSuggestion => ({ label: tag, value: tag }); export const TagsSelector = (colorGenerator: ColorGenerator) => ( { selectedTags, onChange, placeholder, listTags, tagsList, allowNew = true }: TagsSelectorConnectProps, @@ -30,29 +30,30 @@ export const TagsSelector = (colorGenerator: ColorGenerator) => ( }, []); const searchMode = shortUrlCreation?.tagFilteringMode ?? 'startsWith'; - const ReactTagsTag = ({ tag, onDelete }: TagComponentProps) => - ; - const ReactTagsSuggestion = ({ item }: SuggestionComponentProps) => ( + const ReactTagsTag = ({ tag, onClick: deleteTag }: TagRendererProps) => ( + + ); + const ReactTagsSuggestion = ({ option }: OptionRendererProps) => ( <> - - {item.name} + + {option.label} ); return ( !selectedTags.includes(tag)).map(toComponentTag)} - suggestionComponent={ReactTagsSuggestion} + selected={selectedTags.map(toTagSuggestion)} + suggestions={tagsList.tags.filter((tag) => !selectedTags.includes(tag)).map(toTagSuggestion)} + renderTag={ReactTagsTag} + renderOption={ReactTagsSuggestion} allowNew={allowNew} - addOnBlur + // addOnBlur TODO Implement manually placeholderText={placeholder ?? 'Add tags to the URL'} - minQueryLength={1} - delimiters={['Enter', 'Tab', ',']} + onShouldExpand={(value) => value.length > 1} + delimiterKeys={['Enter', 'Tab', ',']} suggestionsTransform={ searchMode === 'includes' - ? (query, suggestions) => suggestions.filter(({ name }) => name.includes(query)) + ? (query, suggestions) => suggestions.filter(({ label }) => label.includes(query)) : undefined } onDelete={(removedTagIndex) => { @@ -61,7 +62,7 @@ export const TagsSelector = (colorGenerator: ColorGenerator) => ( tagsCopy.splice(removedTagIndex, 1); onChange(tagsCopy); }} - onAddition={({ name: newTag }) => onChange( + onAdd={({ label: newTag }) => onChange( // * Avoid duplicated tags (thanks to the Set), // * Split any of the new tags by comma, allowing to paste multiple comma-separated tags at once. [...new Set([...selectedTags, ...newTag.toLowerCase().split(',')])],