From 80f86e76b323552b2163ba6b96b4b6f09f036f17 Mon Sep 17 00:00:00 2001 From: mpavlovicbb Date: Wed, 18 Sep 2024 09:34:59 +0200 Subject: [PATCH 1/5] add pipeline --- .github/workflows/merge.yaml | 175 ++++++++++++++++++++++++++++++++++ .github/workflows/pr.yaml | 177 +++++++++++++++++++++++++++++++++++ 2 files changed, 352 insertions(+) create mode 100644 .github/workflows/merge.yaml create mode 100644 .github/workflows/pr.yaml diff --git a/.github/workflows/merge.yaml b/.github/workflows/merge.yaml new file mode 100644 index 0000000..df24d79 --- /dev/null +++ b/.github/workflows/merge.yaml @@ -0,0 +1,175 @@ +name: Check,Build,Deploy + +on: + push: + branches: + - dev + - qa + - pre-prod + - main + +permissions: + contents: write + pull-requests: write + packages: write + +env: + ENVIRONMENT: ${{ (github.ref_name == 'main' && 'prod-pdf') || (github.ref_name == 'pre-prod' && 'pre-prod-pdf') || (github.ref_name == 'qa' && 'qa-pdf') || (github.ref_name == 'dev' && 'dev-pdf') }} + +jobs: + check-build-deploy: + strategy: + fail-fast: false + matrix: + include: + - workdir: ./backend + name: backend + dockerfile: ./backend/Dockerfile + image: ghcr.io/${{ github.repository }}-backend + qovery_container_name: backend + + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set TAG Environment Variable + id: set_tag + run: | + if [ "${{ github.ref_name }}" = "main" ]; then + echo "TAG=${{ github.sha }}" >> $GITHUB_ENV + else + echo "TAG=${{ github.ref_name }}-${{ github.sha }}" >> $GITHUB_ENV + fi + + - name: Lint Dockerfile + id: hadolint + uses: hadolint/hadolint-action@v3.1.0 + with: + failure-threshold: error + format: json + dockerfile: ${{ matrix.dockerfile }} + # output-file: hadolint_output.json + + - name: Save Hadolint output + id: save_hadolint_output + if: always() + run: cd ${{ matrix.workdir }} && echo "$HADOLINT_RESULTS" | jq '.' > hadolint_output.json + + - name: Print Dockerfile lint output + run: | + cd ${{ matrix.workdir }} + echo "-----HADOLINT RESULT-----" + echo "Outcome: ${{ steps.hadolint.outcome }}" + echo "-----DETAILS--------" + cat hadolint_output.json + echo "--------------------" + + - name: Code lint + id: code_lint + run: | + cd ${{ matrix.workdir }} + if [ ! -f lint.sh ]; then + echo "lint skipped" | tee code_lint_output.txt + exit 0 + fi + set -o pipefail + sudo chmod +x lint.sh && ./lint.sh 2>&1 | tee code_lint_output.txt + + + - name: Unit tests + id: unit_tests + run: | + cd ${{ matrix.workdir }} + if [ ! -f unit-test.sh ]; then + echo "unit tests skipped" | tee code_lint_output.txt + exit 0 + fi + set -o pipefail + sudo chmod +x unit-test.sh && ./unit-test.sh 2>&1 | tee unit_test_output.txt + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Cache Docker layers + uses: actions/cache@v3 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx- + + - id: image_lowercase + uses: ASzc/change-string-case-action@v6 + with: + string: ${{ matrix.image }} + + - name: Build Docker image + uses: docker/build-push-action@v5 + with: + context: ${{ matrix.workdir }} + file: ${{ matrix.dockerfile }} + tags: ${{ steps.image_lowercase.outputs.lowercase }}:${{ env.TAG }} + load: false + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: type=local,dest=/tmp/.buildx-cache + outputs: type=docker,dest=/tmp/image-${{ matrix.name }}-${{ env.ENVIRONMENT }}.tar + + - name: Login to GHCR + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Scan Docker image with Dockle + id: dockle + run: | + wget -q https://github.com/goodwithtech/dockle/releases/download/v0.4.14/dockle_0.4.14_Linux-64bit.tar.gz + tar zxf dockle_0.4.14_Linux-64bit.tar.gz + sudo mv dockle /usr/local/bin + + dockle --exit-code 1 --exit-level fatal --format json --input '/tmp/image-${{ matrix.name }}-${{ env.ENVIRONMENT }}.tar' --output ${{ matrix.workdir }}/dockle_scan_output.json + cat ${{ matrix.workdir }}/dockle_scan_output.json + + echo "outcome=success" >> $GITHUB_OUTPUT + + - name: Push Docker image to GHCR + run: | + docker load -i '/tmp/image-${{ matrix.name }}-${{ env.ENVIRONMENT }}.tar' + rm -rf '/tmp/image-${{ matrix.name }}-${{ env.ENVIRONMENT }}.tar' + docker push ${{ steps.image_lowercase.outputs.lowercase }}:${{ env.TAG }} + + - name: Add tag as a PR comment + uses: ubie-oss/comment-to-merged-pr-action@v0.3.3 + id: comment-to-merged-pr + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + message: |- + This PR is in the tag: ${{ env.TAG }} , for ${{ matrix.name }} service + + - name: Deploy with Qovery + if: github.ref == 'refs/heads/dev' + env: + QOVERY_CLI_ACCESS_TOKEN: ${{secrets.QOVERY_CLI_ACCESS_TOKEN }} + run: | + + echo "Deploying on $ENVIRONMENT" + echo "Organization - ${{ vars.ORGANIZATION }}" + echo "Project - ${{ vars.PROJECT }}" + + # Download and install Qovery CLI + curl -s https://get.qovery.com | bash + + qovery container list \ + --organization ${{ vars.ORGANIZATION }} \ + --project ${{ vars.PROJECT }} \ + --environment $ENVIRONMENT + + qovery container deploy \ + --organization ${{ vars.ORGANIZATION }} \ + --project ${{ vars.PROJECT }} \ + --environment $ENVIRONMENT \ + --container ${{ matrix.qovery_container_name }} \ + --tag ${{ env.TAG }} \ + --watch diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml new file mode 100644 index 0000000..89b7f2c --- /dev/null +++ b/.github/workflows/pr.yaml @@ -0,0 +1,177 @@ +name: Lint,Test,Security Checks + +on: + pull_request: + branches: + - dev + - qa + - pre-prod + - main + +permissions: + contents: read + pull-requests: write + packages: write + +jobs: + static-checks: + strategy: + fail-fast: false + matrix: + include: + - workdir: ./backend + name: backend + dockerfile: ./backend/Dockerfile + image: ghcr.io/${{ github.repository }}-backend + + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Lint Dockerfile + id: hadolint + uses: hadolint/hadolint-action@v3.1.0 + with: + failure-threshold: error + format: json + dockerfile: ${{ matrix.dockerfile }} + # output-file: hadolint_output.json + + - name: Save Hadolint output + id: save_hadolint_output + if: always() + run: cd ${{ matrix.workdir }} && echo "$HADOLINT_RESULTS" | jq '.' > hadolint_output.json + + - name: Print Dockerfile lint output + run: | + cd ${{ matrix.workdir }} + echo "-----HADOLINT RESULT-----" + echo "Outcome: ${{ steps.hadolint.outcome }}" + echo "-----DETAILS--------" + cat hadolint_output.json + echo "--------------------" + + - name: Code lint + id: code_lint + run: | + cd ${{ matrix.workdir }} + if [ ! -f lint.sh ]; then + echo "lint skipped" | tee code_lint_output.txt + exit 0 + fi + set -o pipefail + sudo chmod +x lint.sh && ./lint.sh 2>&1 | tee code_lint_output.txt + + + - name: Unit tests + id: unit_tests + run: | + cd ${{ matrix.workdir }} + if [ ! -f unit-test.sh ]; then + echo "unit tests skipped" | tee code_lint_output.txt + exit 0 + fi + set -o pipefail + sudo chmod +x unit-test.sh && ./unit-test.sh 2>&1 | tee unit_test_output.txt + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Cache Docker layers + uses: actions/cache@v3 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx- + + - id: image_lowercase + uses: ASzc/change-string-case-action@v6 + with: + string: ${{ matrix.image }} + + - name: Build Docker image + uses: docker/build-push-action@v5 + with: + context: ${{ matrix.workdir }} + file: ${{ matrix.dockerfile }} + tags: ${{ steps.image_lowercase.outputs.lowercase }}:${{ github.sha }} + load: false + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: type=local,dest=/tmp/.buildx-cache + outputs: type=docker,dest=/tmp/image-${{ matrix.name }}-${{ github.sha }}-pr.tar + + - name: Scan Docker image with Dockle + id: dockle + run: | + wget -q https://github.com/goodwithtech/dockle/releases/download/v0.4.14/dockle_0.4.14_Linux-64bit.tar.gz + tar zxf dockle_0.4.14_Linux-64bit.tar.gz + sudo mv dockle /usr/local/bin + + dockle --exit-code 1 --exit-level fatal --format json --input '/tmp/image-${{ matrix.name }}-${{ github.sha }}-pr.tar' --output ${{ matrix.workdir }}/dockle_scan_output.json + rm -rf '/tmp/image-${{ matrix.name }}-${{ github.sha }}-pr.tar' + cat ${{ matrix.workdir }}/dockle_scan_output.json + + echo "outcome=success" >> $GITHUB_OUTPUT + + - name: Create PR comment + if: always() + uses: actions/github-script@v6 + env: + HADOLINT_RESULT: ${{ steps.hadolint.outcome }} + CODE_LINT_RESULT: ${{ steps.code_lint.outcome }} + UNIT_TEST_RESULT: ${{ steps.unit_tests.outcome }} + DOCKLE_RESULT: ${{ steps.dockle.outcome }} + with: + script: | + const fs = require('fs'); + + const hadolintResult = fs.existsSync('${{ matrix.workdir }}/hadolint_output.json') ? fs.readFileSync('${{ matrix.workdir }}/hadolint_output.json', 'utf8') : 'No output'; + const codeLintResult = fs.existsSync('${{ matrix.workdir }}/code_lint_output.txt') ? fs.readFileSync('${{ matrix.workdir }}/code_lint_output.txt', 'utf8') : 'No output'; + const unitTestResult = fs.existsSync('${{ matrix.workdir }}/unit_test_output.txt') ? fs.readFileSync('${{ matrix.workdir }}/unit_test_output.txt', 'utf8') : 'No output'; + const dockleScanResult = fs.existsSync('${{ matrix.workdir }}/dockle_scan_output.json') ? fs.readFileSync('${{ matrix.workdir }}/dockle_scan_output.json', 'utf8') : 'No output'; + + let commentBody = ''; + + if (process.env.HADOLINT_RESULT !== 'success') { + commentBody = ` + :x: Dockerfile Lint (Hadolint) failed + \`\`\`json + ${hadolintResult} + \`\`\` + `; + } else if (process.env.CODE_LINT_RESULT !== 'success') { + commentBody = ` + :x: Code Lint failed + \`\`\` + ${codeLintResult} + \`\`\` + `; + } else if (process.env.UNIT_TEST_RESULT !== 'success') { + commentBody = ` + :x: Unit Tests failed + \`\`\` + ${unitTestResult} + \`\`\` + `; + } else if (process.env.DOCKLE_RESULT !== 'success') { + commentBody = ` + :x: Docker Image Scan (Dockle) failed + \`\`\`json + ${dockleScanResult} + \`\`\` + `; + } else { + commentBody = ':white_check_mark: All checks succeeded'; + } + + const { owner, repo } = context.repo; + const issue_number = context.issue.number; + + await github.rest.issues.createComment({ + owner, + repo, + issue_number, + body: commentBody + }); From 106fea1b3516e29e2568f93303cdeb070403614d Mon Sep 17 00:00:00 2001 From: Ryan Williams Date: Wed, 18 Sep 2024 11:54:01 +0100 Subject: [PATCH 2/5] docs: refactor --- .github/ISSUE_TEMPLATE/config.yml | 6 + .github/pull_request_template.md | 12 + CODEOWNERS | 4 +- CONTRIBUTING.md | 193 +------ README.md | 35 +- SECURITY.md | 2 +- SUPPORT.md | 6 +- docs/README.md | 2 + docs/style-guides/css-in-js/README.md | 432 --------------- docs/style-guides/css-sass/README.md | 315 ----------- docs/style-guides/react/README.md | 757 -------------------------- 11 files changed, 48 insertions(+), 1716 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/pull_request_template.md create mode 100644 docs/README.md delete mode 100644 docs/style-guides/css-in-js/README.md delete mode 100644 docs/style-guides/css-sass/README.md delete mode 100644 docs/style-guides/react/README.md diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..54a65cd --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,6 @@ +blank_issues_enabled: true +contact_links: + + - name: Consider creating issue via GovTool repository + url: https://github.com/IntersectMBO/govtool/issues/new/choose + about: To ensure issues are all in one place please make your issue via the GovTool wrapper repository. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..c648139 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,12 @@ +## List of changes + +- Add / Fix / Change / Remove + +## Checklist + +- [related issue](https://github.com/IntersectMBO/govtool/issues/) +- [ ] My changes generate no new warnings +- [ ] My code follows the [style guidelines](https://github.com/IntersectMBO/govtool/tree/main/docs/style-guides) of this project +- [ ] I have commented my code, particularly in hard-to-understand areas +- [ ] I have made corresponding changes to the [changelog](https://github.com/IntersectMBO/govtool-voting-pillar/blob/main/CHANGELOG.md) +- [ ] I have added tests that prove my fix is effective or that my feature works diff --git a/CODEOWNERS b/CODEOWNERS index 21ac68b..fc57e93 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1,4 +1,4 @@ -# GovTool Proposal Discussion Backend Project Codeowners +# GovTool Proposal Pillar Project Codeowners # These owners will be the default owners for everything in the repository. - +* @vpaunovic @teske00 \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 04cc0f5..3412ad7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,192 +1,5 @@ -# Contributing to the `GovTool Proposal Discussion Backend` project +# Contributing to the `GovTool` project -Thanks for considering contributing and helping us on creating GovTool Proposal Discussion Backend! 😎 +Thanks for considering contributing and helping us on creating GovTool! 😎 -The best way to contribute right now is to try things out and provide feedback, but we also accept contributions to the documentation and the obviously to the code itself. - -This document contains guidelines to help you get started and how to make sure your contribution gets accepted, making you our newest GovTool Proposal Discussion Backend contributor! - -## Table of Contents - -- [Contributing to the `GovTool Proposal Discussion Backend` project](#contributing-to-the-govtool-proposal-discussion-backend-project) - - [Table of Contents](#table-of-contents) - - [Code of Conduct](#code-of-conduct) - - [Ask for Help](#ask-for-help) - - [Roles and Responsibilities](#roles-and-responsibilities) - - [I Want To Contribute](#i-want-to-contribute) - - [Before Submitting a Bug Report](#before-submitting-a-bug-report) - - [How Do I Submit a Good Bug Report?](#how-do-i-submit-a-good-bug-report) - - [Your First Code Contribution](#your-first-code-contribution) - - [Working Conventions](#working-conventions) - - [Pull Requests](#pull-requests) - - [Branch Naming](#branch-naming) - - [Commit Messages](#commit-messages) - - [Rationale](#rationale) - - [Merge Commit PRs and Rebase Branches on top of Main](#merge-commit-prs-and-rebase-branches-on-top-of-main) - - [Rationale](#rationale-1) - - [Versioning](#versioning) - - [Changelog](#changelog) - - [Style Guides](#style-guides) - - [React](#react) - - [CSS in Javascript](#css-in-javascript) - - [CSS / SASS](#css--sass) - -## Code of Conduct - -This project and everyone participating in it is governed by the [Code of Conduct](./CODE-OF-CONDUCT.md). -By participating, you are expected to uphold this code. - -## Ask for Help - -See [`SUPPORT.md`](./SUPPORT.md) should you have any questions or need some help in getting set up. - -## Roles and Responsibilities - -We maintain a [CODEOWNERS file](./CODEOWNERS) which provides information who should review a contributing PR. -Note that you might need to get approvals from all code owners (even though GitHub doesn't give a way to enforce it). - -## I Want To Contribute - -#### Before Submitting a Bug Report - -A good bug report shouldn't leave others needing to chase you up for more information. -Therefore, we ask you to investigate carefully, collect information and describe the issue in detail in your report. -Please complete the following steps in advance to help us fix any potential bug as fast as possible. - -- Make sure that you are using the latest version. -- Determine if your bug is really a bug and not an error on your side. - e.g. using incompatible environment components/versions. - If you are looking for support, you might want to check [this section](#i-have-a-question). -- To see if other users have experienced (and potentially already solved) the same issue you are having. -- Also make sure to search the internet (including Stack Overflow) - to see if users outside of the GitHub community have discussed the issue. -- Collect information about the bug: - - Stack trace (Traceback) - - OS, Platform and Version (Windows, Linux, macOS, x86, ARM) - - Version of the interpreter, compiler, SDK, runtime environment, package manager, depending on what seems relevant. - - Possibly your input and the output - - Can you reliably reproduce the issue? And can you also reproduce it with older versions? - -#### How Do I Submit a Good Bug Report? - -We use GitHub issues to track bugs and errors. If you run into an issue with the project: - -- Open an [Issue](https://github.com/IntersectMBO/GovTool Proposal Discussion Backend/issues/new). - (Since we can't be sure at this point whether it is a bug or not, we ask you not to talk about a bug yet and not to label the issue.) -- Explain the behavior you would expect and the actual behavior. -- Please provide as much context as possible. - Describe the *reproduction steps* that someone else can follow to recreate the issue on their own. - This usually includes your code. - For good bug reports you should isolate the problem and create a reduced test case. -- Provide the information you collected in the previous section. - -Once it's filed: - -- The project team will label the issue accordingly. -- A team member will try to reproduce the issue with your provided steps. - If there are no reproduction steps or no obvious way to reproduce the issue, the team will ask you for those steps. - The issue would then be marked as `needs-repro`. - Bugs with the `needs-repro` tag will not be addressed until they are reproduced. -- If the team is able to reproduce the issue, it will be marked `needs-fix`. - It may possibly be marked with other tags (such as `critical`). - The issue will then be left to be [implemented by someone](#your-first-code-contribution). - -#### Your First Code Contribution - -TODO - -## Working Conventions - -### Pull Requests - -Thank you for contributing your changes by opening a pull requests! - -To get something merged we usually require: -- Follow the Pull Request template -- Description of the changes - if your commit messages are great, this is less important -- Quality of changes is ensured - through new or updated automated tests -- Change is related to an issue, feature (idea) or bug report - ideally discussed beforehand -- Well-scoped - we prefer multiple PRs, rather than a big one -- Add changes to changelog, see [Changelog](#changelog). - -Please reuse the branch naming for the pull request naming. - -### Branch Naming - -- When creating your branches please create informative names. -- Using prefixes such as `feat/`, `fix/`, `chore/`, `docs/` for branch names are a good start. -- Using the related issue number after the prefix is required. - -Examples: -- `feat/123-added-ability-for-dreps-to-change-drep-id` -- `fix/312-fixed-drep-ids-being-reversed` -- `chore/567-bumped-cardano-node-version-to-9` -- `docs/88-tweak-contributing-pr-template-codeowners` - -### Commit Messages - -Please make informative commit messages! -It makes it much easier to work out why things are the way they are when you’re debugging things later. - -A commit message is communication, so as usual, put yourself in the position of the reader: what does a reviewer, or someone reading the commit message later need to do their job? -Write it down! -It is even better to include this information in the code itself, but sometimes it doesn’t belong there (e.g. ticket info). - -Also, include any relevant meta-information, such as issue numbers. -If a commit completely addresses a issue, you can put that in the headline if you want, but it’s fine to just put it in the body. - -Here are seven rules for great git commit messages: -1. Separate subject from body with a blank line -2. Limit the subject line to 50 characters (soft limit) -3. Capitalize the subject line -4. Do not end the subject line with a period -5. Use the imperative mood in the subject line and suffix with ticket number if applicable -6. Wrap the body at 72 characters (hard limit) -7. Use the body to explain what and why vs. how - -There is plenty to say on this topic, but broadly the guidelines in [this post](https://cbea.ms/git-commit/) are good. - -#### Rationale - -Git commit messages are our only source of why something was changed the way it was changed. -So we better make the readable, concise and detailed (when required). - -### Merge Commit PRs and Rebase Branches on top of Main - -When closing branches / PRs use merge commits, so we have a history of PRs also in the git history. -Do not merge main into side branches, instead rebase them on top of main. -Try to keep branches up-to-date with main (not strict requirement though). -Once merged to main, please delete the branch. - -**Tip:** Use Github's merge button in PRs to merge with commit. -If a branch is outdated, use the rebase button in PRs to rebase feature branches (NOT update via merge). - -#### Rationale - -Keeping branches ahead of main not only make the git history a lot nicer to process, it also makes conflict resolutions easier. -Merging main into a branch repeatedly is a good recipe to introduce invalid conflict resolutions and loose track of the actual changes brought by a the branch. - -### Versioning - -Not all releases are declared stable. -Releases that aren't stable will be released as pre-releases and will append a -pre tag indicating it is not ready for running on production networks. - -### Changelog - -During development, on every PR; -- Make sure `CHANGELOG.md` is kept up-to-date with high-level, technical, but user-focused list of changes according to [keepachangelog](https://keepachangelog.com/en/1.0.0/). -- Bump `UNRELEASED` version in `CHANGELOG.md` according to [semver](https://semver.org/). - -### Style Guides - -#### React - -Please see [React Style Guide](./docs/style-guides/react/). - -#### CSS in Javascript - -Please see [CSS in Javascript Style Guide](./docs/style-guides/css-in-js/). - -#### CSS / SASS - -Please see [CSS / SASS Style Guide](./docs/style-guides/css-sass/). +Please see [Contributing](https://github.com/IntersectMBO/govtool/blob/main/CONTRIBUTING.md) in GovTool wrapper repository. \ No newline at end of file diff --git a/README.md b/README.md index cab9779..2e69908 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,26 @@ -# 🚀 PDF (Proposal Discussion Forum) +# GovTool Proposal Pillar -Welcome to the official repository for the PDF backend. +This is the repository which holds GovTool's Voting Pillar packages. + +For further information and documentation see [GovTool](https://github.com/IntersectMBO/govtool). ## Table of content: -- [Table of content:](#table-of-content) -- [Introduction](#introduction) -- [Prerequisites](#prerequisites) -- [Tech stack:](#tech-stack) - - [pdf-ui package](#pdf-ui-package) - - [Backend](#backend) - - [Database](#database) -- [Getting started](#getting-started) -- [Running locally](#running-locally) - - [Backend setup](#backend-setup) - - [Database configuration](#database-configuration) -- [Running using docker compose](#running-using-docker-compose) -- [Overview of services in docker compose:](#overview-of-services-in-docker-compose) -- [Additional Information](#additional-information) +- [GovTool Proposal Pillar](#govtool-proposal-pillar) + - [Table of content:](#table-of-content) + - [Introduction](#introduction) + - [Prerequisites](#prerequisites) + - [Tech stack:](#tech-stack) + - [pdf-ui package](#pdf-ui-package) + - [Backend](#backend) + - [Database](#database) + - [Getting started](#getting-started) + - [Running locally](#running-locally) + - [Backend setup](#backend-setup) + - [Database configuration](#database-configuration) + - [Running using docker compose](#running-using-docker-compose) + - [Overview of services in docker compose:](#overview-of-services-in-docker-compose) + - [Additional Information](#additional-information) ## Introduction diff --git a/SECURITY.md b/SECURITY.md index 2e7800b..5ff6a22 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -12,4 +12,4 @@ Please provide a clear and concise description of the vulnerability, including: * steps that can be followed to exercise the vulnerability, * any workarounds or mitigations -If you have developed any code or utilities that can help demonstrate the suspected vulnerability, please mention them in your email but ***DO NOT*** attempt to include them as attachments as this may cause your Email to be blocked by spam filters. \ No newline at end of file +If you have developed any code or utilities that can help demonstrate the suspected vulnerability, please mention them in your email but ***DO NOT*** attempt to include them as attachments as this may cause your Email to be blocked by spam filters. diff --git a/SUPPORT.md b/SUPPORT.md index 45ef5c4..39f3878 100644 --- a/SUPPORT.md +++ b/SUPPORT.md @@ -1,8 +1,8 @@ # Ask for help -Should you have any questions or need some help in getting set up, you can use these communication channels to reach the GovTool Proposal Discussion Backend team and get answers in a way where others can benefit from it as well: +Should you have any questions or need some help in getting set up, you can use these communication channels to reach the GovTool team and get answers in a way where others can benefit from it as well: -- Github [Discussions](https://github.com/IntersectMBO/govtool-proposal-dicussion) +- Github [Discussions](https://github.com/IntersectMBO/govtool/discussions) # Reporting a Vulnerability @@ -10,4 +10,4 @@ See [`SECURITY.md`](SECURITY.md) on how to report a security vulnerability. # Contributions -See [`CONTRIBUTING.md`](CONTRIBUTING.md) on how to contribute. \ No newline at end of file +See [`CONTRIBUTING.md`](CONTRIBUTING.md) on how to contribute. diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..b3f3f8f --- /dev/null +++ b/docs/README.md @@ -0,0 +1,2 @@ + +### For docs see [GovTool/docs](https://github.com/IntersectMBO/govtool/tree/main/docs) \ No newline at end of file diff --git a/docs/style-guides/css-in-js/README.md b/docs/style-guides/css-in-js/README.md deleted file mode 100644 index 0618309..0000000 --- a/docs/style-guides/css-in-js/README.md +++ /dev/null @@ -1,432 +0,0 @@ -# CSS-in-JavaScript Style Guide - -*A mostly reasonable approach to CSS-in-JavaScript, adopted from Airbnb* - -## Table of Contents - -1. [Naming](#naming) -1. [Ordering](#ordering) -1. [Nesting](#nesting) -1. [Inline](#inline) -1. [Themes](#themes) - -## Naming - - - Use camelCase for object keys (i.e. "selectors"). - - > Why? We access these keys as properties on the `styles` object in the component, so it is most convenient to use camelCase. - - ```js - // bad - { - 'bermuda-triangle': { - display: 'none', - }, - } - - // good - { - bermudaTriangle: { - display: 'none', - }, - } - ``` - - - Use an underscore for modifiers to other styles. - - > Why? Similar to BEM, this naming convention makes it clear that the styles are intended to modify the element preceded by the underscore. Underscores do not need to be quoted, so they are preferred over other characters, such as dashes. - - ```js - // bad - { - bruceBanner: { - color: 'pink', - transition: 'color 10s', - }, - - bruceBannerTheHulk: { - color: 'green', - }, - } - - // good - { - bruceBanner: { - color: 'pink', - transition: 'color 10s', - }, - - bruceBanner_theHulk: { - color: 'green', - }, - } - ``` - - - Use `selectorName_fallback` for sets of fallback styles. - - > Why? Similar to modifiers, keeping the naming consistent helps reveal the relationship of these styles to the styles that override them in more adequate browsers. - - ```js - // bad - { - muscles: { - display: 'flex', - }, - - muscles_sadBears: { - width: '100%', - }, - } - - // good - { - muscles: { - display: 'flex', - }, - - muscles_fallback: { - width: '100%', - }, - } - ``` - - - Use a separate selector for sets of fallback styles. - - > Why? Keeping fallback styles contained in a separate object clarifies their purpose, which improves readability. - - ```js - // bad - { - muscles: { - display: 'flex', - }, - - left: { - flexGrow: 1, - display: 'inline-block', - }, - - right: { - display: 'inline-block', - }, - } - - // good - { - muscles: { - display: 'flex', - }, - - left: { - flexGrow: 1, - }, - - left_fallback: { - display: 'inline-block', - }, - - right_fallback: { - display: 'inline-block', - }, - } - ``` - - - Use device-agnostic names (e.g. "small", "medium", and "large") to name media query breakpoints. - - > Why? Commonly used names like "phone", "tablet", and "desktop" do not match the characteristics of the devices in the real world. Using these names sets the wrong expectations. - - ```js - // bad - const breakpoints = { - mobile: '@media (max-width: 639px)', - tablet: '@media (max-width: 1047px)', - desktop: '@media (min-width: 1048px)', - }; - - // good - const breakpoints = { - small: '@media (max-width: 639px)', - medium: '@media (max-width: 1047px)', - large: '@media (min-width: 1048px)', - }; - ``` - -## Ordering - - - Define styles after the component. - - > Why? We use a higher-order component to theme our styles, which is naturally used after the component definition. Passing the styles object directly to this function reduces indirection. - - ```jsx - // bad - const styles = { - container: { - display: 'inline-block', - }, - }; - - function MyComponent({ styles }) { - return ( -
- Never doubt that a small group of thoughtful, committed citizens can - change the world. Indeed, it’s the only thing that ever has. -
- ); - } - - export default withStyles(() => styles)(MyComponent); - - // good - function MyComponent({ styles }) { - return ( -
- Never doubt that a small group of thoughtful, committed citizens can - change the world. Indeed, it’s the only thing that ever has. -
- ); - } - - export default withStyles(() => ({ - container: { - display: 'inline-block', - }, - }))(MyComponent); - ``` - -## Nesting - - - Leave a blank line between adjacent blocks at the same indentation level. - - > Why? The whitespace improves readability and reduces the likelihood of merge conflicts. - - ```js - // bad - { - bigBang: { - display: 'inline-block', - '::before': { - content: "''", - }, - }, - universe: { - border: 'none', - }, - } - - // good - { - bigBang: { - display: 'inline-block', - - '::before': { - content: "''", - }, - }, - - universe: { - border: 'none', - }, - } - ``` - -## Inline - - - Use inline styles for styles that have a high cardinality (e.g. uses the value of a prop) and not for styles that have a low cardinality. - - > Why? Generating themed stylesheets can be expensive, so they are best for discrete sets of styles. - - ```jsx - // bad - export default function MyComponent({ spacing }) { - return ( -
- ); - } - - // good - function MyComponent({ styles, spacing }) { - return ( -
- ); - } - export default withStyles(() => ({ - periodic: { - display: 'table', - }, - }))(MyComponent); - ``` - -## Themes - - - Use an abstraction layer such as [react-with-styles](https://github.com/airbnb/react-with-styles) that enables theming. *react-with-styles gives us things like `withStyles()`, `ThemedStyleSheet`, and `css()` which are used in some of the examples in this document.* - - > Why? It is useful to have a set of shared variables for styling your components. Using an abstraction layer makes this more convenient. Additionally, this can help prevent your components from being tightly coupled to any particular underlying implementation, which gives you more freedom. - - - Define colors only in themes. - - ```js - // bad - export default withStyles(() => ({ - chuckNorris: { - color: '#bada55', - }, - }))(MyComponent); - - // good - export default withStyles(({ color }) => ({ - chuckNorris: { - color: color.badass, - }, - }))(MyComponent); - ``` - - - Define fonts only in themes. - - ```js - // bad - export default withStyles(() => ({ - towerOfPisa: { - fontStyle: 'italic', - }, - }))(MyComponent); - - // good - export default withStyles(({ font }) => ({ - towerOfPisa: { - fontStyle: font.italic, - }, - }))(MyComponent); - ``` - - - Define fonts as sets of related styles. - - ```js - // bad - export default withStyles(() => ({ - towerOfPisa: { - fontFamily: 'Italiana, "Times New Roman", serif', - fontSize: '2em', - fontStyle: 'italic', - lineHeight: 1.5, - }, - }))(MyComponent); - - // good - export default withStyles(({ font }) => ({ - towerOfPisa: { - ...font.italian, - }, - }))(MyComponent); - ``` - - - Define base grid units in theme (either as a value or a function that takes a multiplier). - - ```js - // bad - export default withStyles(() => ({ - rip: { - bottom: '-6912px', // 6 feet - }, - }))(MyComponent); - - // good - export default withStyles(({ units }) => ({ - rip: { - bottom: units(864), // 6 feet, assuming our unit is 8px - }, - }))(MyComponent); - - // good - export default withStyles(({ unit }) => ({ - rip: { - bottom: 864 * unit, // 6 feet, assuming our unit is 8px - }, - }))(MyComponent); - ``` - - - Define media queries only in themes. - - ```js - // bad - export default withStyles(() => ({ - container: { - width: '100%', - - '@media (max-width: 1047px)': { - width: '50%', - }, - }, - }))(MyComponent); - - // good - export default withStyles(({ breakpoint }) => ({ - container: { - width: '100%', - - [breakpoint.medium]: { - width: '50%', - }, - }, - }))(MyComponent); - ``` - - - Define tricky fallback properties in themes. - - > Why? Many CSS-in-JavaScript implementations merge style objects together which makes specifying fallbacks for the same property (e.g. `display`) a little tricky. To keep the approach unified, put these fallbacks in the theme. - - ```js - // bad - export default withStyles(() => ({ - .muscles { - display: 'flex', - }, - - .muscles_fallback { - 'display ': 'table', - }, - }))(MyComponent); - - // good - export default withStyles(({ fallbacks }) => ({ - .muscles { - display: 'flex', - }, - - .muscles_fallback { - [fallbacks.display]: 'table', - }, - }))(MyComponent); - - // good - export default withStyles(({ fallback }) => ({ - .muscles { - display: 'flex', - }, - - .muscles_fallback { - [fallback('display')]: 'table', - }, - }))(MyComponent); - ``` - - - Create as few custom themes as possible. Many applications may only have one theme. - - - Namespace custom theme settings under a nested object with a unique and descriptive key. - - ```js - // bad - ThemedStyleSheet.registerTheme('mySection', { - mySectionPrimaryColor: 'green', - }); - - // good - ThemedStyleSheet.registerTheme('mySection', { - mySection: { - primaryColor: 'green', - }, - }); - ``` - ---- - -CSS puns adapted from [Saijo George](https://saijogeorge.com/css-puns/). diff --git a/docs/style-guides/css-sass/README.md b/docs/style-guides/css-sass/README.md deleted file mode 100644 index 836fc7e..0000000 --- a/docs/style-guides/css-sass/README.md +++ /dev/null @@ -1,315 +0,0 @@ -# CSS / Sass Styleguide - -*A mostly reasonable approach to CSS and Sass, adopted from Airbnb* - -## Table of Contents - -1. [Terminology](#terminology) - - [Rule Declaration](#rule-declaration) - - [Selectors](#selectors) - - [Properties](#properties) -1. [CSS](#css) - - [Formatting](#formatting) - - [Comments](#comments) - - [OOCSS and BEM](#oocss-and-bem) - - [ID Selectors](#id-selectors) - - [JavaScript hooks](#javascript-hooks) - - [Border](#border) -1. [Sass](#sass) - - [Syntax](#syntax) - - [Ordering](#ordering-of-property-declarations) - - [Variables](#variables) - - [Mixins](#mixins) - - [Extend directive](#extend-directive) - - [Nested selectors](#nested-selectors) -1. [Translation](#translation) - -## Terminology - -### Rule declaration - -A “rule declaration” is the name given to a selector (or a group of selectors) with an accompanying group of properties. Here's an example: - -```css -.listing { - font-size: 18px; - line-height: 1.2; -} -``` - -### Selectors - -In a rule declaration, “selectors” are the bits that determine which elements in the DOM tree will be styled by the defined properties. Selectors can match HTML elements, as well as an element's class, ID, or any of its attributes. Here are some examples of selectors: - -```css -.my-element-class { - /* ... */ -} - -[aria-hidden] { - /* ... */ -} -``` - -### Properties - -Finally, properties are what give the selected elements of a rule declaration their style. Properties are key-value pairs, and a rule declaration can contain one or more property declarations. Property declarations look like this: - -```css -/* some selector */ { - background: #f1f1f1; - color: #333; -} -``` - -**[⬆ back to top](#table-of-contents)** - -## CSS - -### Formatting - -* Use soft tabs (2 spaces) for indentation. -* Prefer dashes over camelCasing in class names. - - Underscores and PascalCasing are okay if you are using BEM (see [OOCSS and BEM](#oocss-and-bem) below). -* Do not use ID selectors. -* When using multiple selectors in a rule declaration, give each selector its own line. -* Put a space before the opening brace `{` in rule declarations. -* In properties, put a space after, but not before, the `:` character. -* Put closing braces `}` of rule declarations on a new line. -* Put blank lines between rule declarations. - -**Bad** - -```css -.avatar{ - border-radius:50%; - border:2px solid white; } -.no, .nope, .not_good { - // ... -} -#lol-no { - // ... -} -``` - -**Good** - -```css -.avatar { - border-radius: 50%; - border: 2px solid white; -} - -.one, -.selector, -.per-line { - // ... -} -``` - -### Comments - -* Prefer line comments (`//` in Sass-land) to block comments. -* Prefer comments on their own line. Avoid end-of-line comments. -* Write detailed comments for code that isn't self-documenting: - - Uses of z-index - - Compatibility or browser-specific hacks - -### OOCSS and BEM - -We encourage some combination of OOCSS and BEM for these reasons: - - * It helps create clear, strict relationships between CSS and HTML - * It helps us create reusable, composable components - * It allows for less nesting and lower specificity - * It helps in building scalable stylesheets - -**OOCSS**, or “Object Oriented CSS”, is an approach for writing CSS that encourages you to think about your stylesheets as a collection of “objects”: reusable, repeatable snippets that can be used independently throughout a website. - - * Nicole Sullivan's [OOCSS wiki](https://github.com/stubbornella/oocss/wiki) - * Smashing Magazine's [Introduction to OOCSS](http://www.smashingmagazine.com/2011/12/12/an-introduction-to-object-oriented-css-oocss/) - -**BEM**, or “Block-Element-Modifier”, is a _naming convention_ for classes in HTML and CSS. It was originally developed by Yandex with large codebases and scalability in mind, and can serve as a solid set of guidelines for implementing OOCSS. - - * CSS Trick's [BEM 101](https://css-tricks.com/bem-101/) - * Harry Roberts' [introduction to BEM](http://csswizardry.com/2013/01/mindbemding-getting-your-head-round-bem-syntax/) - -We recommend a variant of BEM with PascalCased “blocks”, which works particularly well when combined with components (e.g. React). Underscores and dashes are still used for modifiers and children. - -**Example** - -```jsx -// ListingCard.jsx -function ListingCard() { - return ( - - ); -} -``` - -```css -/* ListingCard.css */ -.ListingCard { } -.ListingCard--featured { } -.ListingCard__title { } -.ListingCard__content { } -``` - - * `.ListingCard` is the “block” and represents the higher-level component - * `.ListingCard__title` is an “element” and represents a descendant of `.ListingCard` that helps compose the block as a whole. - * `.ListingCard--featured` is a “modifier” and represents a different state or variation on the `.ListingCard` block. - -### ID selectors - -While it is possible to select elements by ID in CSS, it should generally be considered an anti-pattern. ID selectors introduce an unnecessarily high level of [specificity](https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity) to your rule declarations, and they are not reusable. - -For more on this subject, read [CSS Wizardry's article](http://csswizardry.com/2014/07/hacks-for-dealing-with-specificity/) on dealing with specificity. - -### JavaScript hooks - -Avoid binding to the same class in both your CSS and JavaScript. Conflating the two often leads to, at a minimum, time wasted during refactoring when a developer must cross-reference each class they are changing, and at its worst, developers being afraid to make changes for fear of breaking functionality. - -We recommend creating JavaScript-specific classes to bind to, prefixed with `.js-`: - -```html - -``` - -### Border - -Use `0` instead of `none` to specify that a style has no border. - -**Bad** - -```css -.foo { - border: none; -} -``` - -**Good** - -```css -.foo { - border: 0; -} -``` -**[⬆ back to top](#table-of-contents)** - -## Sass - -### Syntax - -* Use the `.scss` syntax, never the original `.sass` syntax -* Order your regular CSS and `@include` declarations logically (see below) - -### Ordering of property declarations - -1. Property declarations - - List all standard property declarations, anything that isn't an `@include` or a nested selector. - - ```scss - .btn-green { - background: green; - font-weight: bold; - // ... - } - ``` - -2. `@include` declarations - - Grouping `@include`s at the end makes it easier to read the entire selector. - - ```scss - .btn-green { - background: green; - font-weight: bold; - @include transition(background 0.5s ease); - // ... - } - ``` - -3. Nested selectors - - Nested selectors, _if necessary_, go last, and nothing goes after them. Add whitespace between your rule declarations and nested selectors, as well as between adjacent nested selectors. Apply the same guidelines as above to your nested selectors. - - ```scss - .btn { - background: green; - font-weight: bold; - @include transition(background 0.5s ease); - - .icon { - margin-right: 10px; - } - } - ``` - -### Variables - -Prefer dash-cased variable names (e.g. `$my-variable`) over camelCased or snake_cased variable names. It is acceptable to prefix variable names that are intended to be used only within the same file with an underscore (e.g. `$_my-variable`). - -### Mixins - -Mixins should be used to DRY up your code, add clarity, or abstract complexity--in much the same way as well-named functions. Mixins that accept no arguments can be useful for this, but note that if you are not compressing your payload (e.g. gzip), this may contribute to unnecessary code duplication in the resulting styles. - -### Extend directive - -`@extend` should be avoided because it has unintuitive and potentially dangerous behavior, especially when used with nested selectors. Even extending top-level placeholder selectors can cause problems if the order of selectors ends up changing later (e.g. if they are in other files and the order the files are loaded shifts). Gzipping should handle most of the savings you would have gained by using `@extend`, and you can DRY up your stylesheets nicely with mixins. - -### Nested selectors - -**Do not nest selectors more than three levels deep!** - -```scss -.page-container { - .content { - .profile { - // STOP! - } - } -} -``` - -When selectors become this long, you're likely writing CSS that is: - -* Strongly coupled to the HTML (fragile) *—OR—* -* Overly specific (powerful) *—OR—* -* Not reusable - - -Again: **never nest ID selectors!** - -If you must use an ID selector in the first place (and you should really try not to), they should never be nested. If you find yourself doing this, you need to revisit your markup, or figure out why such strong specificity is needed. If you are writing well formed HTML and CSS, you should **never** need to do this. - -**[⬆ back to top](#table-of-contents)** - -## Translation - - This style guide is also available in other languages: - - - ![id](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Indonesia.png) **Bahasa Indonesia**: [mazipan/css-style-guide](https://github.com/mazipan/css-style-guide) - - ![tw](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Taiwan.png) **Chinese (Traditional)**: [ArvinH/css-style-guide](https://github.com/ArvinH/css-style-guide) - - ![cn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/China.png) **Chinese (Simplified)**: [Zhangjd/css-style-guide](https://github.com/Zhangjd/css-style-guide) - - ![fr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/France.png) **French**: [mat-u/css-style-guide](https://github.com/mat-u/css-style-guide) - - ![ka](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Georgia.png) **Georgian**: [DavidKadaria/css-style-guide](https://github.com/davidkadaria/css-style-guide) - - ![ja](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Japan.png) **Japanese**: [nao215/css-style-guide](https://github.com/nao215/css-style-guide) - - ![ko](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/South-Korea.png) **Korean**: [CodeMakeBros/css-style-guide](https://github.com/CodeMakeBros/css-style-guide) - - ![PT-BR](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Brazil.png) **Portuguese (Brazil)**: [felipevolpatto/css-style-guide](https://github.com/felipevolpatto/css-style-guide) - - ![pt-PT](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Portugal.png) **Portuguese (Portugal)**: [SandroMiguel/airbnb-css-style-guide](https://github.com/SandroMiguel/airbnb-css-style-guide) - - ![ru](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Russia.png) **Russian**: [rtplv/airbnb-css-ru](https://github.com/rtplv/airbnb-css-ru) - - ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Spain.png) **Spanish**: [ismamz/guia-de-estilo-css](https://github.com/ismamz/guia-de-estilo-css) - - ![vn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Vietnam.png) **Vietnamese**: [trungk18/css-style-guide](https://github.com/trungk18/css-style-guide) - - ![vn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Italy.png) **Italian**: [antoniofull/linee-guida-css](https://github.com/antoniofull/linee-guida-css) - - ![de](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Germany.png) **German**: [tderflinger/css-styleguide](https://github.com/tderflinger/css-styleguide) - -**[⬆ back to top](#table-of-contents)** diff --git a/docs/style-guides/react/README.md b/docs/style-guides/react/README.md deleted file mode 100644 index 7b82997..0000000 --- a/docs/style-guides/react/README.md +++ /dev/null @@ -1,757 +0,0 @@ -# React/JSX Style Guide - -*A mostly reasonable approach to React and JSX, adopted from Airbnb* - -This style guide is mostly based on the standards that are currently prevalent in JavaScript, although some conventions (i.e async/await or static class fields) may still be included or prohibited on a case-by-case basis. Currently, anything prior to stage 3 is not included nor recommended in this guide. - -## Table of Contents - - 1. [Basic Rules](#basic-rules) - 1. [Class vs `React.createClass` vs stateless](#class-vs-reactcreateclass-vs-stateless) - 1. [Mixins](#mixins) - 1. [Naming](#naming) - 1. [Declaration](#declaration) - 1. [Alignment](#alignment) - 1. [Quotes](#quotes) - 1. [Spacing](#spacing) - 1. [Props](#props) - 1. [Refs](#refs) - 1. [Parentheses](#parentheses) - 1. [Tags](#tags) - 1. [Methods](#methods) - 1. [Ordering](#ordering) - 1. [`isMounted`](#ismounted) - -## Basic Rules - - - Only include one React component per file. - - However, multiple [Stateless, or Pure, Components](https://facebook.github.io/react/docs/reusable-components.html#stateless-functions) are allowed per file. eslint: [`react/no-multi-comp`](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/no-multi-comp.md#ignorestateless). - - Always use JSX syntax. - - Do not use `React.createElement` unless you’re initializing the app from a file that is not JSX. - - [`react/forbid-prop-types`](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/forbid-prop-types.md) will allow `arrays` and `objects` only if it is explicitly noted what `array` and `object` contains, using `arrayOf`, `objectOf`, or `shape`. - -## Class vs `React.createClass` vs stateless - - - If you have internal state and/or refs, prefer `class extends React.Component` over `React.createClass`. eslint: [`react/prefer-es6-class`](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/prefer-es6-class.md) [`react/prefer-stateless-function`](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/prefer-stateless-function.md) - - ```jsx - // bad - const Listing = React.createClass({ - // ... - render() { - return
{this.state.hello}
; - } - }); - - // good - class Listing extends React.Component { - // ... - render() { - return
{this.state.hello}
; - } - } - ``` - - And if you don’t have state or refs, prefer normal functions (not arrow functions) over classes: - - ```jsx - // bad - class Listing extends React.Component { - render() { - return
{this.props.hello}
; - } - } - - // bad (relying on function name inference is discouraged) - const Listing = ({ hello }) => ( -
{hello}
- ); - - // good - function Listing({ hello }) { - return
{hello}
; - } - ``` - -## Mixins - - - [Do not use mixins](https://facebook.github.io/react/blog/2016/07/13/mixins-considered-harmful.html). - - > Why? Mixins introduce implicit dependencies, cause name clashes, and cause snowballing complexity. Most use cases for mixins can be accomplished in better ways via components, higher-order components, or utility modules. - -## Naming - - - **Extensions**: Use `.jsx` extension for React components. eslint: [`react/jsx-filename-extension`](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-filename-extension.md) - - **Filename**: Use PascalCase for filenames. E.g., `ReservationCard.jsx`. - - **Reference Naming**: Use PascalCase for React components and camelCase for their instances. eslint: [`react/jsx-pascal-case`](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-pascal-case.md) - - ```jsx - // bad - import reservationCard from './ReservationCard'; - - // good - import ReservationCard from './ReservationCard'; - - // bad - const ReservationItem = ; - - // good - const reservationItem = ; - ``` - - - **Component Naming**: Use the filename as the component name. For example, `ReservationCard.jsx` should have a reference name of `ReservationCard`. However, for root components of a directory, use `index.jsx` as the filename and use the directory name as the component name: - - ```jsx - // bad - import Footer from './Footer/Footer'; - - // bad - import Footer from './Footer/index'; - - // good - import Footer from './Footer'; - ``` - - - **Higher-order Component Naming**: Use a composite of the higher-order component’s name and the passed-in component’s name as the `displayName` on the generated component. For example, the higher-order component `withFoo()`, when passed a component `Bar` should produce a component with a `displayName` of `withFoo(Bar)`. - - > Why? A component’s `displayName` may be used by developer tools or in error messages, and having a value that clearly expresses this relationship helps people understand what is happening. - - ```jsx - // bad - export default function withFoo(WrappedComponent) { - return function WithFoo(props) { - return ; - } - } - - // good - export default function withFoo(WrappedComponent) { - function WithFoo(props) { - return ; - } - - const wrappedComponentName = WrappedComponent.displayName - || WrappedComponent.name - || 'Component'; - - WithFoo.displayName = `withFoo(${wrappedComponentName})`; - return WithFoo; - } - ``` - - - **Props Naming**: Avoid using DOM component prop names for different purposes. - - > Why? People expect props like `style` and `className` to mean one specific thing. Varying this API for a subset of your app makes the code less readable and less maintainable, and may cause bugs. - - ```jsx - // bad - - - // bad - - - // good - - ``` - -## Declaration - - - Do not use `displayName` for naming components. Instead, name the component by reference. - - ```jsx - // bad - export default React.createClass({ - displayName: 'ReservationCard', - // stuff goes here - }); - - // good - export default class ReservationCard extends React.Component { - } - ``` - -## Alignment - - - Follow these alignment styles for JSX syntax. eslint: [`react/jsx-closing-bracket-location`](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-closing-bracket-location.md) [`react/jsx-closing-tag-location`](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-closing-tag-location.md) - - ```jsx - // bad - - - // good - - - // if props fit in one line then keep it on the same line - - - // children get indented normally - - - - - // bad - {showButton && -