From 06b9befab47d44184d325be819026dd6d07cc8ac Mon Sep 17 00:00:00 2001 From: Josh <30379833+josh-chamberlain@users.noreply.github.com> Date: Mon, 18 Dec 2023 11:56:40 -0500 Subject: [PATCH] refactor: 2023 launch aesthetic tweaks (#30) Co-authored-by: Joshua Graber <68428039+joshuagraber@users.noreply.github.com> Co-authored-by: Joshua Graber --- .eslintignore | 1 + .eslintrc.json | 29 ++- .husky/pre-push | 1 + docs/CONTRIBUTING.md => CONTRIBUTING.md | 14 +- README.md | 46 ++-- badges/build.svg | 1 + badges/coverage.svg | 1 + bin/pdap-design-system-cli.js | 1 + cli/index.js | 1 + package-lock.json | 219 ++++++++++++++---- package.json | 11 +- release.config.cjs | 4 +- src/components/Button/PdapButton.vue | 4 +- .../FlexContainer/flex-container.spec.ts | 1 - src/components/Footer/PdapFooter.vue | 17 +- .../Footer/__snapshots__/footer.spec.ts.snap | 6 +- src/components/Form/PdapForm.vue | 13 +- src/components/Form/form.spec.ts | 11 +- src/components/Header/PdapHeader.vue | 2 +- src/components/Input/PdapInput.vue | 6 +- src/components/Nav/PdapNav.vue | 6 +- src/components/Nav/nav.spec.ts | 4 +- .../QuickSearchForm/QuickSearchForm.vue | 45 ++-- .../quick-search-form.spec.ts.snap | 15 +- .../QuickSearchForm/quick-search-form.spec.ts | 156 +++++++++---- tools/generateBuildBadge.js | 37 +++ tools/generateCoverageBadge.js | 73 ++++++ tools/testing/serializer.ts | 5 +- tsconfig.eslint.json | 19 +- tsconfig.json | 25 +- 30 files changed, 573 insertions(+), 201 deletions(-) rename docs/CONTRIBUTING.md => CONTRIBUTING.md (81%) create mode 100644 badges/build.svg create mode 100644 badges/coverage.svg create mode 100644 tools/generateBuildBadge.js create mode 100644 tools/generateCoverageBadge.js diff --git a/.eslintignore b/.eslintignore index d97c852..7e13951 100644 --- a/.eslintignore +++ b/.eslintignore @@ -5,3 +5,4 @@ node_modules dist *.config.* + diff --git a/.eslintrc.json b/.eslintrc.json index e92a62e..c9992a9 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -3,7 +3,9 @@ "parser": "vue-eslint-parser", "parserOptions": { "parser": "@typescript-eslint/parser", - "project": ["./tsconfig.eslint.json"], + "project": [ + "./tsconfig.eslint.json" + ], "tsConfigRootDir": "./" }, "extends": [ @@ -15,7 +17,20 @@ "@vue/typescript/recommended", "@vue/eslint-config-prettier" ], - "plugins": ["@typescript-eslint", "prettier"], + "plugins": [ + "@typescript-eslint", + "prettier" + ], + "overrides": [ + { + "extends": [ + "plugin:@typescript-eslint/disable-type-checked" + ], + "files": [ + "./**/*.js" + ] + } + ], "rules": { "vue/require-default-prop": "off", // Switch base eslint indent rule off @@ -37,11 +52,17 @@ "prettier/prettier": [ "warn", { - "indent": ["warn", "tab", { "SwitchCase": 2 }], + "indent": [ + "warn", + "tab", + { + "SwitchCase": 2 + } + ], "tabWidth": 2, "useTabs": true } ], "vue/no-multiple-template-root": "off" } -} +} \ No newline at end of file diff --git a/.husky/pre-push b/.husky/pre-push index 115cd65..7897f0e 100755 --- a/.husky/pre-push +++ b/.husky/pre-push @@ -1,4 +1,5 @@ #!/usr/bin/env sh . "$(dirname -- "$0")/_/husky.sh" +npx commitlint --from origin/HEAD --to HEAD npm run test:ci diff --git a/docs/CONTRIBUTING.md b/CONTRIBUTING.md similarity index 81% rename from docs/CONTRIBUTING.md rename to CONTRIBUTING.md index 0ce3184..2b0b44f 100644 --- a/docs/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,8 +5,8 @@ This is a `Vue` component library. It is built using `Vue3` and the `composition` API with TypeScript. Styles are created using `TailwindCSS`. The library is built using `Vite`. For testing, we use `Vitest`, for linting `eslint`, for formatting `prettier`. Conventional commits are enforced using `commitizen`, and publishing is done automatically via `semantic-release`. Some helpful resources and reading: -[Commitizen](https://commitizen-tools.github.io/commitizen/) -[Semantic Release](https://semantic-release.gitbook.io/semantic-release/) +[Commitizen](https://commitizen-tools.github.io/commitizen/) +[Semantic Release](https://semantic-release.gitbook.io/semantic-release/) [Tailwind](https://tailwindui.com/documentation) [TypeScript](https://www.typescriptlang.org/docs/) [Vite](https://vitejs.dev/guide/) @@ -16,6 +16,7 @@ Some helpful resources and reading: [Vuelidate](https://vuelidate.js.org/) ### Etiquette +[![Discord](https://img.shields.io/discord/828274060034965575?logo=discord)](https://discord.gg/vKhDv7nC8B) Head to [\#outreach](https://discord.com/channels/828274060034965575/853442226034442260/) in our [Discord](https://discord.gg/vKhDv7nC8B) if you'd like to collect feedback from the wider group. @@ -60,6 +61,9 @@ To test the package locally: 1. Run `npm link` from the root of the project directory. 2. `cd` into the local directory of the app you want to test importing into, then run `npm link pdap-design-system` 3. This will create a symlink between your local directories, allowing you to test changes in real time without actually publish to the `npm` registry. -4. To test publishing, squash merge your local feature/fix/whatever branch into `main`. Then from `main` run `npm exec semantic-release --dry-run`. Then revert the squash merge commit. - -The `lint`, `test`, and `build` scripts are all required to pass before pull requests can be merged. The `lint` scripts are run against all staged files, and you can run `test:changed` to verify test suites against all local changes (staged and unstaged) before committing. You can run `build` locally as well, if you want to verify that it will pass before pushing changes. +4. To test publishing, squash merge your local feature/fix/whatever branch into `main`. Then from `main` run `npm exec semantic-release --dry-run`. Then reset the squash merge commit using `git reset HEAD~1 && git restore .`. +5. A few things to note: +- The `lint`, `test`, and `build` scripts are all required to pass before pull requests can be merged. +- The `lint` scripts are run against all staged files before a commit will succeed. +- All local commit messages are linted and the full test suite is run using the `test:ci` before a `git push` will succeed. +- You can run `test:changed` to verify test suites against all local changes (staged and un-staged) before committing. diff --git a/README.md b/README.md index 5a2e853..acd7e2a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,14 @@ -# Design System +# PDAP Design System +_A `Vue` component library, styling system, and image asset repository for PDAP-branded client apps._ + +[![Current npm release](https://img.shields.io/npm/v/pdap-design-system?style=for-the-badge)](https://www.npmjs.com/package/pdap-design-system) +![Build status](./badges/build.svg) +![Test coverage](./badges/coverage.svg) +![License](https://img.shields.io/github/license/Police-Data-Accessibility-Project/design-system.svg?style=for-the-badge&color=green) + + +[![Discord](https://img.shields.io/discord/828274060034965575?logo=discord&style=for-the-badge&color=blue)](https://discord.gg/vKhDv7nC8B) -This is a `Vue` component library, styling system, and image asset repository for PDAP-branded client apps. ## Usage @@ -43,7 +51,7 @@ module.exports = { ``` -5. If your project is using `TypeScript`, the component props definitions and other types are exposed for import as well. +5. If the project is using `TypeScript`, the component props definitions and other types are exposed for import as well. _n.b. This can be particularly useful for composing `Form` schemas, where `Input` schema objects are defined differently depending on the `type` of input desired._ ``` @@ -54,7 +62,7 @@ import { PdapInputTypes } from 'pdap-design-system'; ### About images -PDAP image assets contained in this repo are built to the `/dist` folder. For convenience an importing alias `/images` has been added. +PDAP image assets contained in this repo are built to the `/dist` directory. For convenience an importing alias `/images` has been added. ``` import 'pdap-design-system/images/acronym.svg'; @@ -68,7 +76,7 @@ import `pdap-design-system/images`; ### Using the CLI to copy assets to your local project directory -If you want either styles or images copied to a local folder, you can do so from the root directory of your project. +If you want either styles or images copied to a local directory, you can do so from the root directory of your project. Assets can be copied using the `pdap-design-system` command line method exposed by this package. @@ -92,16 +100,16 @@ The following argument is optional: gh repo clone Police-Data-Accessibility-Project/design-system ``` -2. CD into the project folder and install dependencies +2. CD into the project directory and install dependencies ``` cd design-system npm i ``` -3. Step 2 should result in the `build` script being run after packages are installed. Check the `dist` folder for changes. You then may want to take one or both of the following steps: +3. Step 2 should result in the `build` script being run after packages are installed. Check the `dist` directory for changes. You then may want to take one or both of the following steps: -- If `build` wasn't called when you installed deps, build styles and images to the `dist` folder: +- If `build` wasn't called when you installed deps, build styles and images to the `dist` directory: ``` npm run build @@ -115,7 +123,7 @@ npm run build:watch 4. If you use VS Code as your editor, you may want to install the [tailwind VS Code extension](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss), which helps with intellisense and the custom at-rules used by TailwindCSS. -5. Read the [contributing guide](./docs/CONTRIBUTING.md) for development requirements and tips. +5. Read the [contributing guide](./CONTRIBUTING.md) for development requirements and tips. ## Assets @@ -129,16 +137,18 @@ Use this [terminology](https://docs.pdap.io/activities/terms-and-definitions). | `_commit` | Create conventional commits | | `build` | Builds the library | | `build:watch` | Builds the library and watches for file changes | -| `ci` | Removes all generated files and re-installs deps | -| `clean` | Removes all generated files (except `package-lock.json`) | -| `clean:deps` | Removes node_modules directory | -| `clean:build` | Removes dist directory | -| `clean:test` | Removes testing coverage reports | -| `lint` | Lints everything | +| `ci` | Remove all generated files and re-installs deps | +| `clean` | Remove all generated files (except `package-lock.json`) | +| `clean:deps` | Remove node_modules directory | +| `clean:build` | Remove dist directory | +| `clean:test` | Remove testing coverage reports | +| `lint` | Lint everything | | `lint:es` | Lint `ts` and `vue` with `eslint` | | `lint:css` | Lint `css` and `vue` with `stylelint` | | `lint:ts` | Lint `ts` with `tsc` | -| `test` | Runs all test suites | -| `test:changed` | Runs only test suites affected by changed files | -| `typecheck` | Runs type check on all `ts` and `vue` files | +| `test` | Run all test suites | +| `test:changed` | Run only test suites affected by changed files | +| `typecheck` | Run type check on all `ts` and `vue` files | + + _n.b. There are some other scripts defined in the `package.json` `"scripts"` field, but they are mostly for CI or cleanup post-build, etc. You shouldn't need them._ diff --git a/badges/build.svg b/badges/build.svg new file mode 100644 index 0000000..40b78d2 --- /dev/null +++ b/badges/build.svg @@ -0,0 +1 @@ +BUILD: PASSINGBUILDPASSING \ No newline at end of file diff --git a/badges/coverage.svg b/badges/coverage.svg new file mode 100644 index 0000000..1997f07 --- /dev/null +++ b/badges/coverage.svg @@ -0,0 +1 @@ +COVERAGE: 100%COVERAGE100% \ No newline at end of file diff --git a/bin/pdap-design-system-cli.js b/bin/pdap-design-system-cli.js index 8750a78..741a69c 100755 --- a/bin/pdap-design-system-cli.js +++ b/bin/pdap-design-system-cli.js @@ -1,6 +1,7 @@ #!/usr/bin/env node import minimist from 'minimist'; import cli from '../cli'; +import process from 'process'; // Convert argv to object keyed by argName where --argName is what is passed to CLI const args = minimist(process.argv); diff --git a/cli/index.js b/cli/index.js index 4265cb8..6c6b09b 100644 --- a/cli/index.js +++ b/cli/index.js @@ -1,4 +1,5 @@ import fs from 'fs-extra'; +import process from 'process'; function copyImageAssets(args) { const packageImagesPath = diff --git a/package-lock.json b/package-lock.json index 7f57dba..7cdc6df 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "pdap-design-system", - "version": "2.0.5", + "version": "0.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "pdap-design-system", - "version": "2.0.5", + "version": "0.0.0", "license": "ISC", "dependencies": { "@vuelidate/core": "^2.0.3", @@ -35,6 +35,8 @@ "@vue/eslint-config-typescript": "^12.0.0", "@vue/test-utils": "^2.4.1", "autoprefixer": "^10.4.16", + "badge-maker": "^3.3.1", + "cheerio": "1.0.0-rc.12", "conventional-changelog-angular": "^7.0.0", "cross-env": "^7.0.3", "cz-conventional-changelog": "^3.3.0", @@ -3389,6 +3391,15 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/anafanafo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anafanafo/-/anafanafo-2.0.0.tgz", + "integrity": "sha512-Nlfq7NC4AOkTJerWRIZcOAiMNtIDVIGWGvQ98O7Jl6Kr2Dk0dX5u4MqN778kSRTy5KRqchpLdF2RtLFEz9FVkQ==", + "dev": true, + "dependencies": { + "char-width-table-consumer": "^1.0.0" + } + }, "node_modules/ansi-escapes": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.0.tgz", @@ -3627,6 +3638,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/badge-maker": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/badge-maker/-/badge-maker-3.3.1.tgz", + "integrity": "sha512-OO/PS7Zg2E6qaUWzHEHt21Q5VjcFBAJVA8ztgT/fIdSZFBUwoyeo0ZhA6V5tUM8Vcjq8DJl6jfGhpjESssyqMQ==", + "dev": true, + "dependencies": { + "anafanafo": "2.0.0", + "css-color-converter": "^2.0.0" + }, + "bin": { + "badge": "lib/badge-cli.js" + }, + "engines": { + "node": ">= 10", + "npm": ">= 5" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -3677,6 +3705,12 @@ "node": ">=8" } }, + "node_modules/binary-search": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/binary-search/-/binary-search-1.3.6.tgz", + "integrity": "sha512-nbE1WxOTTrUWIfsfZ4aHGYu5DOuNkbxGokjV6Z2kxfJK3uaAb8zNK1muzOeipoLHZjInT4Br88BHpzevc681xA==", + "dev": true + }, "node_modules/bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", @@ -4020,6 +4054,15 @@ "node": ">=10" } }, + "node_modules/char-width-table-consumer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/char-width-table-consumer/-/char-width-table-consumer-1.0.0.tgz", + "integrity": "sha512-Fz4UD0LBpxPgL9i29CJ5O4KANwaMnX/OhhbxzvNa332h+9+nRKyeuLw4wA51lt/ex67+/AdsoBQJF3kgX2feYQ==", + "dev": true, + "dependencies": { + "binary-search": "^1.3.5" + } + }, "node_modules/chardet": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", @@ -4076,6 +4119,34 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/cheerio-select/node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cheerio-select/node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, "node_modules/cheerio/node_modules/htmlparser2": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", @@ -4879,6 +4950,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/css-color-converter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/css-color-converter/-/css-color-converter-2.0.0.tgz", + "integrity": "sha512-oLIG2soZz3wcC3aAl/7Us5RS8Hvvc6I8G8LniF/qfMmrm7fIKQ8RIDDRZeKyGL2SrWfNqYspuLShbnjBMVWm8g==", + "dev": true, + "dependencies": { + "color-convert": "^0.5.2", + "color-name": "^1.1.4", + "css-unit-converter": "^1.1.2" + } + }, + "node_modules/css-color-converter/node_modules/color-convert": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz", + "integrity": "sha512-RwBeO/B/vZR3dfKL1ye/vx8MHZ40ugzpyfeVG5GsiuGnrlMWe2o8wxBbLCpw9CsxV+wHuzYlCiWnybrIA0ling==", + "dev": true + }, "node_modules/css-functions-list": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.2.1.tgz", @@ -4888,22 +4976,6 @@ "node": ">=12 || >=16" } }, - "node_modules/css-select": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", - "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", - "dev": true, - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, "node_modules/css-tree": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", @@ -4917,17 +4989,11 @@ "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" } }, - "node_modules/css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", - "dev": true, - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } + "node_modules/css-unit-converter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.2.tgz", + "integrity": "sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA==", + "dev": true }, "node_modules/css.escape": { "version": "1.5.1", @@ -19592,6 +19658,15 @@ "uri-js": "^4.2.2" } }, + "anafanafo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anafanafo/-/anafanafo-2.0.0.tgz", + "integrity": "sha512-Nlfq7NC4AOkTJerWRIZcOAiMNtIDVIGWGvQ98O7Jl6Kr2Dk0dX5u4MqN778kSRTy5KRqchpLdF2RtLFEz9FVkQ==", + "dev": true, + "requires": { + "char-width-table-consumer": "^1.0.0" + } + }, "ansi-escapes": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.0.tgz", @@ -19752,6 +19827,16 @@ "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", "dev": true }, + "badge-maker": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/badge-maker/-/badge-maker-3.3.1.tgz", + "integrity": "sha512-OO/PS7Zg2E6qaUWzHEHt21Q5VjcFBAJVA8ztgT/fIdSZFBUwoyeo0ZhA6V5tUM8Vcjq8DJl6jfGhpjESssyqMQ==", + "dev": true, + "requires": { + "anafanafo": "2.0.0", + "css-color-converter": "^2.0.0" + } + }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -19782,6 +19867,12 @@ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true }, + "binary-search": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/binary-search/-/binary-search-1.3.6.tgz", + "integrity": "sha512-nbE1WxOTTrUWIfsfZ4aHGYu5DOuNkbxGokjV6Z2kxfJK3uaAb8zNK1muzOeipoLHZjInT4Br88BHpzevc681xA==", + "dev": true + }, "bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", @@ -20020,6 +20111,15 @@ "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", "dev": true }, + "char-width-table-consumer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/char-width-table-consumer/-/char-width-table-consumer-1.0.0.tgz", + "integrity": "sha512-Fz4UD0LBpxPgL9i29CJ5O4KANwaMnX/OhhbxzvNa332h+9+nRKyeuLw4wA51lt/ex67+/AdsoBQJF3kgX2feYQ==", + "dev": true, + "requires": { + "binary-search": "^1.3.5" + } + }, "chardet": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", @@ -20076,6 +20176,27 @@ "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.0.1" + }, + "dependencies": { + "css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "dev": true, + "requires": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + } + }, + "css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true + } } }, "chokidar": { @@ -20644,25 +20765,31 @@ } } }, + "css-color-converter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/css-color-converter/-/css-color-converter-2.0.0.tgz", + "integrity": "sha512-oLIG2soZz3wcC3aAl/7Us5RS8Hvvc6I8G8LniF/qfMmrm7fIKQ8RIDDRZeKyGL2SrWfNqYspuLShbnjBMVWm8g==", + "dev": true, + "requires": { + "color-convert": "^0.5.2", + "color-name": "^1.1.4", + "css-unit-converter": "^1.1.2" + }, + "dependencies": { + "color-convert": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz", + "integrity": "sha512-RwBeO/B/vZR3dfKL1ye/vx8MHZ40ugzpyfeVG5GsiuGnrlMWe2o8wxBbLCpw9CsxV+wHuzYlCiWnybrIA0ling==", + "dev": true + } + } + }, "css-functions-list": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.2.1.tgz", "integrity": "sha512-Nj5YcaGgBtuUmn1D7oHqPW0c9iui7xsTsj5lIX8ZgevdfhmjFfKB3r8moHJtNJnctnYXJyYX5I1pp90HM4TPgQ==", "dev": true }, - "css-select": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", - "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", - "dev": true, - "requires": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" - } - }, "css-tree": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", @@ -20673,10 +20800,10 @@ "source-map-js": "^1.0.1" } }, - "css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "css-unit-converter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.2.tgz", + "integrity": "sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA==", "dev": true }, "css.escape": { diff --git a/package.json b/package.json index 3707f7f..677acc7 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,8 @@ "@vue/eslint-config-typescript": "^12.0.0", "@vue/test-utils": "^2.4.1", "autoprefixer": "^10.4.16", + "badge-maker": "^3.3.1", + "cheerio": "1.0.0-rc.12", "conventional-changelog-angular": "^7.0.0", "cross-env": "^7.0.3", "cz-conventional-changelog": "^3.3.0", @@ -93,7 +95,7 @@ }, "scripts": { "_commit": "cz", - "build": "vite build && vue-tsc", + "build": "vite build && vue-tsc && npm run badges:build", "build:watch": "npm run build -- --watch", "ci": "npm run clean && npm ci", "clean": "run-p clean:*", @@ -106,10 +108,13 @@ "lint:ts": "vue-tsc", "postbuild": "run-p clean:test && rimraf dist/{config,utils}", "prepare": "husky install", - "test": "vitest --dom --run --coverage", + "test": "vitest --dom --run --coverage && npm run badges:coverage", "test:changed": "npm run test -- --changed", "test:ci": "test --watchAll=false", - "typecheck": "vue-tsc" + "typecheck": "vue-tsc", + "badges": "run-p badges:*", + "badges:build": "node ./tools/generateBuildBadge.js", + "badges:coverage": "node ./tools/generateCoverageBadge.js" }, "bin": { "pdap-design-system": "./bin/pdap-design-system-cli.js" diff --git a/release.config.cjs b/release.config.cjs index c4a91ba..cd25d45 100644 --- a/release.config.cjs +++ b/release.config.cjs @@ -62,7 +62,7 @@ module.exports = { '@semantic-release/github', - /* TODO: Use either of the below strategies. + /* TODO: Use either of the below strategies to raise PR against `main` or commit directly to `main` *** */ // 6. Update version with new release PR raised against main (in lieu of direct commit for now) // If we're going to use this, it will require a shim. See errors in console. @@ -77,7 +77,7 @@ module.exports = { // ] - // TODO: Fix token so that /git will work + // TODO: Fix token // // 6. Update version with new release commit (must be called after /changelog and /npm) // [ // '@semantic-release/git', diff --git a/src/components/Button/PdapButton.vue b/src/components/Button/PdapButton.vue index 260e571..bbe54eb 100644 --- a/src/components/Button/PdapButton.vue +++ b/src/components/Button/PdapButton.vue @@ -30,7 +30,7 @@ const classes = reactive({ @layer components { .pdap-button { - @apply cursor-pointer border-0 decoration-0 disabled:opacity-50 font-semibold inline-block mx-1 px-6 py-3 rounded-none text-center text-lg w-full; + @apply cursor-pointer border-2 border-brand-gold decoration-0 disabled:opacity-50 font-semibold inline-block mx-1 px-6 py-2 rounded-none text-center text-lg w-full; @apply hover:brightness-85 lg:text-xl sm:max-w-max; } @@ -43,7 +43,7 @@ const classes = reactive({ } .pdap-button-secondary { - @apply pdap-button bg-transparent border-brand-gold border-2 border-solid text-brand-gold; + @apply pdap-button bg-white border-2 border-brand-gold text-brand-gold; } .pdap-button-secondary[type='submit'] { diff --git a/src/components/FlexContainer/flex-container.spec.ts b/src/components/FlexContainer/flex-container.spec.ts index 936e20d..8aefe5a 100644 --- a/src/components/FlexContainer/flex-container.spec.ts +++ b/src/components/FlexContainer/flex-container.spec.ts @@ -11,7 +11,6 @@ describe('Renders container component', () => { test('Renders a container', () => { const wrapper = mount(FlexContainer, { slots: { - // Todo: Render some actual components here to test default: 'Container Content', }, }); diff --git a/src/components/Footer/PdapFooter.vue b/src/components/Footer/PdapFooter.vue index c6db71e..1a082a2 100644 --- a/src/components/Footer/PdapFooter.vue +++ b/src/components/Footer/PdapFooter.vue @@ -35,9 +35,10 @@ - Privacy Policy. + target="_blank" + >
+ Privacy Policy
contact@pdap.io

@@ -143,16 +144,8 @@ const navLogoLinkIsPath = props.logoImageAnchorPath.startsWith('/'); } .pdap-footer-social-link { - @apply cursor-pointer bg-brand-gold border-0 decoration-0 disabled:opacity-50 font-semibold inline-block mx-1 px-6 py-3 rounded-none text-center text-neutral-100 text-lg w-full; + @apply cursor-pointer border-2 border-brand-gold decoration-0 disabled:opacity-50 font-semibold inline-block mx-1 px-6 py-2 rounded-none text-center text-lg w-full bg-brand-gold text-white; @apply hover:brightness-85 lg:text-xl sm:max-w-max; } - - .pdap-footer-social-link-current { - @apply lg:border-neutral-400 lg:border-2 lg:border-solid text-neutral-700; - } - - .pdap-footer-social-link-current-exact { - @apply pdap-footer-social-link-current lg:border-neutral-700; - } } diff --git a/src/components/Footer/__snapshots__/footer.spec.ts.snap b/src/components/Footer/__snapshots__/footer.spec.ts.snap index bb83306..9467f9e 100644 --- a/src/components/Footer/__snapshots__/footer.spec.ts.snap +++ b/src/components/Footer/__snapshots__/footer.spec.ts.snap @@ -24,7 +24,11 @@ exports[`Footer component > Renders a footer 1`] = ` - +

Search our database

+

+ If you have a question to answer, we may already know about helpful data + in your area. + Learn more about the data here. +

+
- +
@@ -41,7 +52,7 @@ const formSchema = [ name: 'searchTerm', label: 'What are you looking for?', type: PdapInputTypes.TEXT, - placeholder: "Enter a keyword, type of public records, or 'all'", + placeholder: "Enter a keyword, or 'all'", value: '', }, { @@ -49,7 +60,7 @@ const formSchema = [ name: 'location', label: 'From where?', type: PdapInputTypes.TEXT, - placeholder: "Enter a state, county, municipality, or 'all'", + placeholder: "Enter a place, or 'all'", value: '', }, ]; @@ -89,28 +100,8 @@ function handleSubmit(values: { location: string; searchTerm: string }) { @tailwind components; @layer components { - .pdap-quick-search-form { - @apply h-full max-h-[75-vh] p-0; - } - - .quick-search-description { - @apply flex justify-center text-center w-full mt-0 mx-auto mb-10; - } - - .pdap-quick-search-form .pdap-form { - @apply flex flex-wrap; - - column-gap: 1rem; - } - - .pdap-quick-search-form .pdap-button { - @apply flex-grow-0 flex-shrink-0 basis-full max-w-[unset] mt-8; - } - .pdap-quick-search-form .pdap-input { - @apply flex-col flex-grow flex-shrink-0 basis-[45%]; - - row-gap: 0; + @apply flex-col flex-grow flex-shrink-0 basis-[45%] gap-y-0; } .pdap-quick-search-form .pdap-input-label { diff --git a/src/components/QuickSearchForm/__snapshots__/quick-search-form.spec.ts.snap b/src/components/QuickSearchForm/__snapshots__/quick-search-form.spec.ts.snap index 4b9d648..c4eac1c 100644 --- a/src/components/QuickSearchForm/__snapshots__/quick-search-form.spec.ts.snap +++ b/src/components/QuickSearchForm/__snapshots__/quick-search-form.spec.ts.snap @@ -1,22 +1,27 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`QuickSearchForm component > Renders a QuickSearchForm 1`] = ` -
-
+
+

Search our database

+

If you have a question to answer, we may already know about helpful data in your area. Learn more about the data here. +

+
+
+
- +
- +
- +
`; diff --git a/src/components/QuickSearchForm/quick-search-form.spec.ts b/src/components/QuickSearchForm/quick-search-form.spec.ts index aaa111a..18294fd 100644 --- a/src/components/QuickSearchForm/quick-search-form.spec.ts +++ b/src/components/QuickSearchForm/quick-search-form.spec.ts @@ -3,7 +3,7 @@ import QuickSearchForm from './QuickSearchForm.vue'; // Utils import { mount } from '@vue/test-utils'; -import { describe, expect, test, vi } from 'vitest'; +import { beforeEach, describe, expect, test, vi } from 'vitest'; import { nextTick } from 'vue'; // Mocks and stubs @@ -44,61 +44,127 @@ describe('QuickSearchForm component', () => { expect(wrapper.html()).toMatchSnapshot(); }); - test('Form triggers router push when rendered in app with /search/ route', async () => { - getRoutes.mockReturnValueOnce([{ path: '/search/:foo/:bar' }]); + describe('Form triggers router push when rendered in app with /search/ route', async () => { + let searchInput; + let locationInput; + let wrapper; - const wrapper = mount(QuickSearchForm, base); + beforeEach(() => { + getRoutes.mockReturnValueOnce([{ path: '/search/:foo/:bar' }]); - // Set input values - const searchInput = wrapper.find('#search-term'); - const locationInput = wrapper.find('#location'); - await searchInput.setValue('foo'); - await locationInput.setValue('bar'); - await nextTick(); + wrapper = mount(QuickSearchForm, base); - // Assert - expect((searchInput.element as HTMLInputElement).value).toBe('foo'); - expect((locationInput.element as HTMLInputElement).value).toBe('bar'); + // Set input values + searchInput = wrapper.find('#search-term'); + locationInput = wrapper.find('#location'); + }); - // Submit - await wrapper.find('form').trigger('submit.prevent'); - await nextTick(); - await wrapper.vm.$forceUpdate(); + test('Form triggers router push with both values set', async () => { + await searchInput.setValue('foo'); + await locationInput.setValue('bar'); + await nextTick(); - // Assert submit event - expect(wrapper.emitted()).toHaveProperty('submit'); - expect(wrapper.emitted('submit')?.length).toBe(1); - expect(push).toHaveBeenCalledWith('/search/foo/bar'); + // Submit + await wrapper.find('form').trigger('submit'); + await nextTick(); + await wrapper.vm.$forceUpdate(); + + // Assert router push + expect(push).toHaveBeenCalledWith('/search/foo/bar'); + }); + + test('Form triggers router push with first value set, second unset', async () => { + await searchInput.setValue('foo'); + await locationInput.setValue(''); + await nextTick(); + + // Submit + await wrapper.find('form').trigger('submit'); + await nextTick(); + await wrapper.vm.$forceUpdate(); + + // Assert router push + expect(push).toHaveBeenCalledWith('/search/foo/all'); + }); + + test('Form triggers router push with second value set, first unset', async () => { + await searchInput.setValue(''); + await locationInput.setValue('bar'); + await nextTick(); + + // Submit + await wrapper.find('form').trigger('submit'); + await nextTick(); + await wrapper.vm.$forceUpdate(); + + // Assert router push + expect(push).toHaveBeenCalledWith('/search/all/bar'); + }); }); - test('Form triggers window navigation when rendered in app without /search/ route — prod mode', async () => { - getRoutes.mockReturnValueOnce([ - { path: '/' }, - { path: '/foo' }, - { path: '/bar' }, - ]); + describe('Form triggers window navigation when rendered in app without /search/ route — prod mode', async () => { + let searchInput; + let locationInput; + let wrapper; - const wrapper = mount(QuickSearchForm, base); + beforeEach(() => { + getRoutes.mockReturnValueOnce([ + { path: '/' }, + { path: '/foo' }, + { path: '/bar' }, + ]); - // Set input values - const searchInput = wrapper.find('#search-term'); - const locationInput = wrapper.find('#location'); - await searchInput.setValue('foo'); - await locationInput.setValue('bar'); - await nextTick(); + wrapper = mount(QuickSearchForm, base); - // Submit - await wrapper.find('form').trigger('submit'); - await nextTick(); - await wrapper.vm.$forceUpdate(); + // Set input values + searchInput = wrapper.find('#search-term'); + locationInput = wrapper.find('#location'); + }); - // Assert submit event - expect(wrapper.emitted()).toHaveProperty('submit'); + test('Form triggers navigation with both values set', async () => { + await searchInput.setValue('foo'); + await locationInput.setValue('bar'); + await nextTick(); - // TODO: how to test window.assign called via form submit event. Not working anywhere. - expect(assign).toHaveBeenCalledWith( - 'https://data-sources.pdap.io/search/foo/bar' - ); + // Submit + await wrapper.find('form').trigger('submit'); + await nextTick(); + await wrapper.vm.$forceUpdate(); + + expect(assign).toHaveBeenCalledWith( + 'https://data-sources.pdap.io/search/foo/bar' + ); + }); + + test('Form triggers navigation with first value set, second unset', async () => { + await searchInput.setValue('foo'); + await locationInput.setValue(''); + await nextTick(); + + // Submit + await wrapper.find('form').trigger('submit'); + await nextTick(); + await wrapper.vm.$forceUpdate(); + + expect(assign).toHaveBeenCalledWith( + 'https://data-sources.pdap.io/search/foo/all' + ); + }); + + test('Form triggers navigation with second value set, first unset', async () => { + await searchInput.setValue(''); + await locationInput.setValue('bar'); + await nextTick(); + + // Submit + await wrapper.find('form').trigger('submit'); + await nextTick(); + await wrapper.vm.$forceUpdate(); + + expect(assign).toHaveBeenCalledWith( + 'https://data-sources.pdap.io/search/all/bar' + ); + }); }); test('Form triggers window navigation when rendered in app without /search/ route — dev mode', async () => { @@ -128,8 +194,6 @@ describe('QuickSearchForm component', () => { await wrapper.vm.$forceUpdate(); // Assert submit event - expect(wrapper.emitted()).toHaveProperty('submit'); - expect(assign).toHaveBeenCalledWith( 'https://data-sources.pdap.dev/search/foo/bar' ); diff --git a/tools/generateBuildBadge.js b/tools/generateBuildBadge.js new file mode 100644 index 0000000..849f284 --- /dev/null +++ b/tools/generateBuildBadge.js @@ -0,0 +1,37 @@ +#!/usr/bin/env node +/* eslint-disable no-undef */ +import fs from 'fs'; +import path from 'path'; +import url from 'url'; +import { makeBadge } from 'badge-maker'; +import { exec } from 'child_process'; + +// Shim +const __dirname = path.dirname(url.fileURLToPath(import.meta.url)); + +let isError = false; + +exec('npm run build', (error) => { + if (error) { + isError = true; + } +}); + +const badge = makeBadge({ + label: 'build', + message: isError ? 'failing' : 'passing', + color: isError ? 'red' : 'brightgreen', + style: 'for-the-badge', +}); + +fs.writeFile(path.join(__dirname, '../badges/build.svg'), badge, (err) => { + if (err) console.log(err); + else { + console.log('Build badge written successfully with contents:\n'); + console.log(fs.readFileSync('badges/build.svg', 'utf8')); + } + + if (typeof fs.readFileSync('badges/build.svg', 'utf8') !== 'undefined') { + process.exit(0); + } +}); diff --git a/tools/generateCoverageBadge.js b/tools/generateCoverageBadge.js new file mode 100644 index 0000000..12bd712 --- /dev/null +++ b/tools/generateCoverageBadge.js @@ -0,0 +1,73 @@ +#!/usr/bin/env node +import { load } from 'cheerio'; +import fs from 'fs'; +import path from 'path'; +import url from 'url'; +import { makeBadge } from 'badge-maker'; + +const __dirname = path.dirname(url.fileURLToPath(import.meta.url)); + +const $coverage = load( + fs.readFileSync( + path.join(__dirname, '../coverage/index.html'), + 'utf8', + (err, data) => { + if (err) { + console.error(err); + return; + } + console.log(data); + } + ) +); + +const totalCoverageElements = $coverage('.pad1').find('.strong'); + +// Get elements that report total coverage. +const coveragePercentage = + totalCoverageElements + .map(function () { + // Convert text content, strip % character, and convert to number + return Number($coverage(this).text().replace(/%/, '').trim()); + }) + .toArray() + // Add up + .reduce((acc, cur) => acc + cur, 0) / + // Divide by total + totalCoverageElements.length; + +fs.writeFile( + path.join(__dirname, '../badges/coverage.svg'), + getCoverageBadge(coveragePercentage), + (err) => { + if (err) console.log(err); + else { + console.log('Coverage badge written successfully with contents:\n'); + console.log(fs.readFileSync('badges/coverage.svg', 'utf8')); + } + } +); + +function getCoverageBadge(coveragePercentage) { + const level = + coveragePercentage > 98 + ? 'high' + : coveragePercentage > 90 + ? 'medium' + : 'low'; + + const colors = new Map([ + ['low', 'red'], + ['medium', 'yellow'], + ['high', 'brightgreen'], + ]); + + const format = { + label: 'coverage', + message: `${coveragePercentage}%`, + color: colors.get(level), + style: 'for-the-badge', + }; + + return makeBadge(format); +} diff --git a/tools/testing/serializer.ts b/tools/testing/serializer.ts index 0dc54b5..70ff990 100644 --- a/tools/testing/serializer.ts +++ b/tools/testing/serializer.ts @@ -2,9 +2,7 @@ /* c8 ignore next 30 */ import { print as _print } from 'jest-serializer-vue-tjw'; -// TODO: -// 1. Write tests -// 2. Fix typescript -- any type is not safe +// TODO: 1. Write tests, 2. Fix typescript -- any type is not safe const helpers = { isHtmlString: function (received: any) { @@ -31,4 +29,3 @@ export default { print, test, }; -// TODO: Devise a test for this, so that we don't need to add c8 declaration ignoring it (code is more or less boilerplate from c8 - https://github.com/tjw-lint/jest-serializer-vue-tjw/#using-with-vitest) diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json index 5808a0c..6c10986 100644 --- a/tsconfig.eslint.json +++ b/tsconfig.eslint.json @@ -1,5 +1,18 @@ { + "compilerOptions": { + "types": [ + "@types/node" + ], + "noEmit": true, + "allowJs": true + }, "extends": "./tsconfig.json", - "include": ["**/*.ts", "src/**/*.vue", "src/**/*.spec.ts"], - "exclude": ["src/**/__snapshots__/"] -} + "include": [ + "**/*.ts", + "src/**/*.vue", + "src/**/*.spec.ts" + ], + "exclude": [ + "src/**/__snapshots__/" + ], +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 1e896a1..2a56cad 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,12 +3,17 @@ "target": "ES2020", "useDefineForClassFields": true, "module": "ESNext", - "lib": ["ES2020", "DOM", "DOM.Iterable"], + "lib": [ + "ES2020", + "DOM", + "DOM.Iterable" + ], "skipLibCheck": true, "paths": { - "*": ["./*"] + "*": [ + "./*" + ] }, - /* Bundler mode */ "moduleResolution": "bundler", "allowImportingTsExtensions": true, @@ -18,19 +23,25 @@ "emitDeclarationOnly": true, "declaration": true, "outDir": "dist", - /* Linting */ "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, "noFallthroughCasesInSwitch": true }, - "exclude": ["src/**/__snapshots__/", "src/**/*.spec.ts"], + "exclude": [ + "src/**/__snapshots__/", + "src/**/*.spec.ts" + ], "include": [ "src/**/*.ts", "src/**/*.d.ts", "src/**/*.vue", "src/utils/vuelidate.ts" ], - "references": [{ "path": "./tsconfig.node.json" }] -} + "references": [ + { + "path": "./tsconfig.node.json" + } + ] +} \ No newline at end of file