├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .github ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── FUNDING.yml ├── ISSUE_TEMPLATE.md ├── ISSUE_TEMPLATE │ ├── bug.md │ ├── config.yml │ ├── feature.md │ ├── question.md │ ├── regression.md │ └── security.md ├── PULL_REQUEST_TEMPLATE.md ├── autoapproval.yml ├── dependabot.yml ├── stale.yml └── workflows │ ├── ci.yml │ ├── codeql-analysis.yml │ ├── pr-updater.yml │ └── release.yml ├── .gitignore ├── .npmrc ├── .prettierrc ├── .releaserc ├── .storybook ├── main.js ├── manager.js ├── preview.js └── theme.js ├── .vscode └── settings.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── SECURITY.md ├── __mocks__ ├── matchMediaMock.js └── styleMock.js ├── _redirects ├── buildDocs.sh ├── commitlint.config.js ├── package.json ├── scss ├── _variables.scss └── main.scss ├── src ├── components │ ├── Alert │ │ ├── Alert.tsx │ │ └── index.tsx │ ├── Badge │ │ ├── Badge.tsx │ │ └── index.tsx │ ├── Breadcrumb │ │ ├── Breadcrumb.tsx │ │ ├── BreadcrumbItem.tsx │ │ └── index.tsx │ ├── Button │ │ ├── Button.tsx │ │ ├── index.tsx │ │ └── interfaces.ts │ ├── Calendar │ │ ├── Calendar.tsx │ │ ├── index.ts │ │ └── interfaces.ts │ ├── Callout │ │ ├── Callout.tsx │ │ ├── callout.scss │ │ └── index.tsx │ ├── Checkbox │ │ ├── Checkbox.tsx │ │ └── index.tsx │ ├── DateTimePicker │ │ ├── DateTimePicker.tsx │ │ └── index.tsx │ ├── Dropdown │ │ ├── Dropdown.tsx │ │ ├── index.tsx │ │ └── interfaces.tsx │ ├── Graph │ │ ├── BarGraph.tsx │ │ ├── LineGraph.tsx │ │ ├── PieGraph.tsx │ │ ├── index.tsx │ │ ├── interfaces.ts │ │ └── util.ts │ ├── Icon │ │ ├── Icon.tsx │ │ ├── index.tsx │ │ └── interfaces.ts │ ├── Image │ │ ├── Image.tsx │ │ └── index.tsx │ ├── Label │ │ ├── Label.tsx │ │ └── index.tsx │ ├── Layout │ │ ├── Column.tsx │ │ ├── Container.tsx │ │ ├── Row.tsx │ │ └── index.tsx │ ├── List │ │ ├── List.tsx │ │ ├── ListItem.tsx │ │ └── index.tsx │ ├── Modal │ │ ├── Modal.tsx │ │ ├── index.tsx │ │ └── interfaces.ts │ ├── Navbar │ │ ├── Navbar.tsx │ │ ├── index.tsx │ │ └── interfaces.tsx │ ├── Panel │ │ ├── Panel.tsx │ │ └── index.tsx │ ├── Pill │ │ ├── Pill.tsx │ │ └── index.tsx │ ├── Radio │ │ ├── Radio.tsx │ │ └── index.tsx │ ├── RichText │ │ ├── RichText.tsx │ │ └── index.tsx │ ├── Select │ │ ├── Select.tsx │ │ └── index.tsx │ ├── Spinner │ │ ├── Spinner.tsx │ │ ├── index.tsx │ │ ├── interfaces.ts │ │ └── strings.ts │ ├── Switch │ │ ├── Switch.tsx │ │ └── index.tsx │ ├── Tab │ │ ├── Tab.tsx │ │ ├── TabsHeader.tsx │ │ └── index.tsx │ ├── Table │ │ ├── Table.tsx │ │ └── index.tsx │ ├── TextField │ │ ├── TextField.tsx │ │ └── index.tsx │ ├── TextInput │ │ ├── TextInput.tsx │ │ └── index.tsx │ ├── Toaster │ │ ├── components.tsx │ │ ├── index.tsx │ │ ├── interfaces.ts │ │ └── toaster.scss │ ├── Typeahead │ │ ├── Typeahead.tsx │ │ └── index.tsx │ ├── Typography │ │ ├── Typography.tsx │ │ └── index.tsx │ └── VideoPlayer │ │ ├── VideoPlayer.tsx │ │ └── index.tsx ├── helpers │ └── controlSize.ts ├── index.tsx ├── interfaces │ └── index.tsx └── react-app-env.d.ts ├── stories ├── .eslintrc.js ├── CustomClasses.css ├── alerts.stories.tsx ├── badges.stories.tsx ├── breadcrumbs.stories.tsx ├── buttons.stories.tsx ├── calendar.stories.tsx ├── callout.stories.tsx ├── checkbox.stories.tsx ├── datetimepicker.stories.tsx ├── dropdown.stories.tsx ├── graphs.stories.tsx ├── icons.stories.tsx ├── image.stories.tsx ├── label.stories.tsx ├── layout.stories.css ├── layout.stories.tsx ├── lists.stories.tsx ├── modal.stories.tsx ├── navbar.stories.tsx ├── panel.stories.tsx ├── pills.stories.tsx ├── radio.stories.tsx ├── richtext.stories.tsx ├── select.stories.tsx ├── spinner.stories.tsx ├── switch.stories.tsx ├── tab.stories.tsx ├── table.stories.tsx ├── textfield.stories.tsx ├── textinput.stories.tsx ├── toaster.stories.tsx ├── tsconfig.json ├── typeahead.stories.tsx ├── typography.stories.tsx ├── videoplayer.stories.tsx └── welcome.stories.mdx ├── test-setup.ts ├── test ├── __snapshots__ │ └── toaster.test.tsx.snap ├── alert.test.tsx ├── badge.test.tsx ├── bargraph.test.tsx ├── breadcrumb.test.tsx ├── button.test.tsx ├── calendar.test.tsx ├── callout.test.tsx ├── checkbox.test.tsx ├── datetimepicker.test.tsx ├── dropdown.test.tsx ├── icon.test.tsx ├── image.test.tsx ├── label.test.tsx ├── layout.test.tsx ├── linegraph.test.tsx ├── list.test.tsx ├── listitem.test.tsx ├── modal.test.tsx ├── navbar.test.tsx ├── panel.test.tsx ├── piegraph.test.tsx ├── pill.test.tsx ├── radio.test.tsx ├── richtext.test.tsx ├── select.test.tsx ├── spinner.test.tsx ├── switch.test.tsx ├── tab.test.tsx ├── table.test.tsx ├── textfield.test.tsx ├── textinput.test.tsx ├── toaster.test.tsx ├── tsconfig.json ├── typeahead.test.tsx ├── typography.test.tsx └── videoplayer.test.tsx ├── tsconfig.json ├── tsdx.config.js └── types ├── react-table-config.d.ts └── videoplayer.d.ts /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | end_of_line = lf 10 | # editorconfig-tools is unable to ignore longs strings or urls 11 | max_line_length = 100 12 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | tsdx.config.js -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | ignorePatterns: ['commitlint.config.js', 'stories/.eslintrc.js', '.eslintrc.js'], 3 | env: { 4 | browser: true, 5 | es6: true, 6 | 'jest/globals': true, 7 | }, 8 | extends: [ 9 | 'airbnb', 10 | 'plugin:@typescript-eslint/recommended', 11 | 'prettier', 12 | 'prettier/react', 13 | 'prettier/@typescript-eslint', 14 | 'plugin:prettier/recommended', 15 | ], 16 | globals: { 17 | Atomics: 'readonly', 18 | SharedArrayBuffer: 'readonly', 19 | }, 20 | parser: '@typescript-eslint/parser', 21 | parserOptions: { 22 | project: ['./tsconfig.json', './stories/tsconfig.json', './test/tsconfig.json'], 23 | tsconfigRootDir: './', 24 | }, 25 | settings: { 26 | 'import/resolver': { 27 | node: { 28 | extensions: ['.js', '.jsx', '.ts', '.tsx'], 29 | }, 30 | }, 31 | }, 32 | plugins: ['react', '@typescript-eslint', 'prettier', 'jest'], 33 | rules: { 34 | '@typescript-eslint/member-delimiter-style': 'off', 35 | '@typescript-eslint/explicit-function-return-type': 'off', 36 | '@typescript-eslint/no-explicit-any': 'off', 37 | '@typescript-eslint/no-unused-vars': 'off', 38 | '@typescript-eslint/unified-signatures': 'error', 39 | '@typescript-eslint/no-inferrable-types': ['error', { ignoreParameters: true }], 40 | '@typescript-eslint/no-empty-function': ['error', { allow: ['arrowFunctions'] }], 41 | 'import/extensions': 'off', 42 | 'react/jsx-filename-extension': ['error', { extensions: ['.tsx'] }], 43 | 'react/jsx-one-expression-per-line': 'off', 44 | 'react/jsx-wrap-multilines': 'off', 45 | 'react/jsx-props-no-spreading': 'off', 46 | 'arrow-body-style': ['warn', 'as-needed'], 47 | 'no-param-reassign': ['error', { props: false }], 48 | 'import/prefer-default-export': 'off', 49 | 'no-console': 'off', 50 | 'eol-last': ['error', 'always'], 51 | 'no-debugger': 'error', 52 | 'no-nested-ternary': 'off', 53 | 'import/no-unresolved': 'off', 54 | 'import/extensions': ['error', 'never'], 55 | 'import/order': [ 56 | 'error', 57 | { 58 | groups: ['external', ['sibling', 'parent', 'internal'], 'builtin', 'unknown'], 59 | 'newlines-between': 'always', 60 | alphabetize: { 61 | order: 'asc', 62 | caseInsensitive: true, 63 | }, 64 | }, 65 | ], 66 | curly: ['error', 'all'], 67 | 'react/require-default-props': ['warn'], 68 | 'react/default-props-match-prop-types': ['warn'], 69 | 'react/prop-types': ['warn'] 70 | }, 71 | } 72 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: hospitalrun 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## 👉 [Please follow one of these issue templates](https://github.com/hospitalrun/components/issues/new/choose) 👈 2 | 3 | #### You have already researched for similar issues? 4 | 5 | It's not uncommon that somebody already opened an issue or in best case it's already fixed but not merged. That's the reason why you should [search](https://github.com/hospitalrun/components/issues) at first before submitting a new one. 6 | 7 | #### Are you sure this is an issue with HospitalRun or are you just looking for some help? 8 | 9 | Issues should only be posted in this repository after you have been able to reproduce them and confirm that they are a bug or incorrect/missing information in the [docs](https://github.com/HospitalRun/hospitalrun-docs). 10 | 11 | For all other questions, requests, help resolving an issue, or if you are not sure if this is 12 | the right place, please do not open an issue here. Instead, try to find a solution in our [Slack workspace](https://hospitalrun-slack.herokuapp.com/). 13 | 14 | If you have issues with the [hospitalrun.io](https://www.hospitalrun.io) site, please open an issue [here](https://github.com/HospitalRun/hospitalrun.github.io/issues). 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🐛 Bug report 3 | about: Create a report to help us improve 4 | --- 5 | 6 | Before you submit an issue we recommend you drop into our [Slack workspace](https://hospitalrun-slack.herokuapp.com/) and ask any questions you have or mention any problems you've had getting started with HospitalRun. 7 | 8 | **Please read this entire template before posting any issue. If you ignore these instructions 9 | and post an issue here that does not follow the instructions, your issue might be closed, 10 | locked, and assigned the `missing discussion` label.** 11 | 12 | ## 🐛 Bug Report 13 | 14 | A clear and concise description of what the bug is. 15 | 16 | ## To Reproduce 17 | 18 | Steps to reproduce the behavior: 19 | 20 | Paste your code here: 21 | 22 | ```js 23 | ``` 24 | 25 | ## Expected behavior 26 | 27 | A clear and concise description of what you expected to happen. 28 | 29 | Paste the results here: 30 | 31 | ```js 32 | ``` 33 | 34 | ## Your Environment 35 | 36 | - _node version_: 6,8,10 37 | - _os_: Mac, Windows, Linux 38 | - _any other relevant information_ 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Slack Workspace 4 | url: https://hospitalrun-slack.herokuapp.com/ 5 | about: Please ask and answer questions here 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🚀 Feature Proposal 3 | about: Submit a proposal for a new feature 4 | --- 5 | 6 | Before you submit an issue we recommend you drop into our [Slack workspace](https://hospitalrun-slack.herokuapp.com/) and ask any questions you have or mention any problems you've had getting started with HospitalRun. 7 | 8 | **Please read this entire template before posting any issue. If you ignore these instructions 9 | and post an issue here that does not follow the instructions, your issue might be closed, 10 | locked, and assigned the `missing discussion` label.** 11 | 12 | ## 🚀 Feature Proposal 13 | 14 | A clear and concise description of what the feature is. 15 | 16 | ## Motivation 17 | 18 | Please outline the motivation for the proposal. 19 | 20 | ## Example 21 | 22 | Please provide an example for how this feature would be used. 23 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 💬 Questions / Help 3 | about: If you have questions, please check our Slack workspace 4 | --- 5 | 6 | ## 💬 Questions and Help 7 | 8 | ### Please note that this issue tracker is not a help forum and this issue may be closed. 9 | 10 | Before you submit an issue we recommend you drop into our [Slack workspace](https://hospitalrun-slack.herokuapp.com/) and ask any questions you have or mention any problems you've had getting started with HospitalRun. 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/regression.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 💥 Regression Report 3 | about: Report unexpected behavior that worked in previous versions 4 | --- 5 | 6 | Before you submit an issue we recommend you drop into our [Slack workspace](https://hospitalrun-slack.herokuapp.com/) and ask any questions you have or mention any problems you've had getting started with HospitalRun. 7 | 8 | **Please read this entire template before posting any issue. If you ignore these instructions 9 | and post an issue here that does not follow the instructions, your issue might be closed, 10 | locked, and assigned the `missing discussion` label.** 11 | 12 | ## 💥 Regression Report 13 | 14 | A clear and concise description of what the regression is. 15 | 16 | ## Last working version 17 | 18 | Worked up to version: 19 | 20 | Stopped working in version: 21 | 22 | ## To Reproduce 23 | 24 | Steps to reproduce the behavior: 25 | 26 | Paste your code here: 27 | 28 | ```js 29 | ``` 30 | 31 | ## Expected behavior 32 | 33 | A clear and concise description of what you expected to happen. 34 | 35 | Paste the results here: 36 | 37 | ```js 38 | ``` 39 | 40 | ## Your Environment 41 | 42 | - _node version_: 6,8,10 43 | - _os_: Mac, Windows, Linux 44 | - _any other relevant information_ 45 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/security.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 👮 Security Issue 3 | about: Responsible Disclosure 4 | labels: 'security' 5 | 6 | --- 7 | 8 | ## 👮 [Responsible Disclosure](https://github.com/nodejs/security-wg/blob/master/processes/responsible_disclosure_template.md) 9 | 10 | Do not open issues that might have security implications. It is critical that security related issues 11 | are reported privately so we have time to address them before they become public knowledge. 12 | 13 | Vulnerabilities can also be reported by emailing security@hospitalrun.io 14 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Fixes #[replace brackets with the issue number that your pull request addresses]. 2 | 3 | **Changes proposed in this pull request:** 4 | - [list out summary of changes here] 5 | - [list out summary of changes here] 6 | - [list out summary of changes here] 7 | - [etc] 8 | 9 | **Newly added dependencies with [Bundlephobia](https://bundlephobia.com/) links:** 10 | - [Link of the new dependency] 11 | - [Link of the new dependency] 12 | - [etc] 13 | 14 | *Note: pull requests without proper descriptions may simply be closed without further discussion. We appreciate your contributions, but need to know what you are offering in clearly described format. Provide tests for all code that you add/modify. If you add/modify any components update the storybook. Thanks! (you can delete this text)* 15 | -------------------------------------------------------------------------------- /.github/autoapproval.yml: -------------------------------------------------------------------------------- 1 | from_owner: 2 | - dependabot[bot] 3 | required_labels: 4 | - dependencies 5 | apply_labels: 6 | - autoapproved 7 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "09:00" 8 | timezone: Europe/Rome 9 | open-pull-requests-limit: 10 10 | reviewers: 11 | - "autoapproval" 12 | labels: 13 | - dependencies 14 | ignore: 15 | - dependency-name: autoprefixer 16 | versions: 17 | - ">= 10.a, < 11" 18 | - dependency-name: babel-jest 19 | versions: 20 | - "> 26.1.0, < 27" 21 | - dependency-name: chart.js 22 | versions: 23 | - ">= 3.a, < 4" 24 | - dependency-name: eslint 25 | versions: 26 | - ">= 7.1.a, < 7.2" 27 | - dependency-name: eslint-config-prettier 28 | versions: 29 | - ">= 8.a, < 9" 30 | - dependency-name: "@fortawesome/free-regular-svg-icons" 31 | versions: 32 | - "> 5.14.0, < 6" 33 | - dependency-name: "@fortawesome/free-solid-svg-icons" 34 | versions: 35 | - "> 5.14.0, < 6" 36 | - dependency-name: "@fullcalendar/core" 37 | versions: 38 | - ">= 5.a, < 6" 39 | - dependency-name: "@fullcalendar/daygrid" 40 | versions: 41 | - ">= 5.a, < 6" 42 | - dependency-name: "@fullcalendar/interaction" 43 | versions: 44 | - ">= 5.a, < 6" 45 | - dependency-name: "@fullcalendar/react" 46 | versions: 47 | - ">= 5.a, < 6" 48 | - dependency-name: "@fullcalendar/timegrid" 49 | versions: 50 | - ">= 5.a, < 6" 51 | - dependency-name: node-sass 52 | versions: 53 | - ">= 5.a, < 6" 54 | - dependency-name: react-bootstrap 55 | versions: 56 | - "> 1.0.1, < 2" 57 | - dependency-name: react-bootstrap-typeahead 58 | versions: 59 | - ">= 5.a, < 6" 60 | - dependency-name: react-dom 61 | versions: 62 | - ">= 17.a, < 18" 63 | - dependency-name: react-toastify 64 | versions: 65 | - ">= 6.0.a, < 6.1" 66 | - dependency-name: react-toastify 67 | versions: 68 | - ">= 6.a, < 7" 69 | - dependency-name: react-toastify 70 | versions: 71 | - ">= 7.a, < 8" 72 | - dependency-name: sass-loader 73 | versions: 74 | - ">= 11.a, < 12" 75 | - dependency-name: "@tinymce/tinymce-react" 76 | versions: 77 | - "> 3.7.0, < 4" 78 | - dependency-name: "@tinymce/tinymce-react" 79 | versions: 80 | - ">= 3.8.a, < 3.9" 81 | - dependency-name: typescript 82 | versions: 83 | - ">= 3.9.a, < 3.10" 84 | - dependency-name: typescript 85 | versions: 86 | - ">= 4.a, < 5" 87 | - dependency-name: "@typescript-eslint/eslint-plugin" 88 | versions: 89 | - ">= 3.0.a, < 3.1" 90 | - dependency-name: "@typescript-eslint/eslint-plugin" 91 | versions: 92 | - ">= 3.1.a, < 3.2" 93 | - dependency-name: "@typescript-eslint/parser" 94 | versions: 95 | - ">= 3.0.a, < 3.1" 96 | - dependency-name: "@typescript-eslint/parser" 97 | versions: 98 | - ">= 4.a, < 5" 99 | - dependency-name: "@commitlint/cli" 100 | versions: 101 | - 12.0.0 102 | - 12.0.1 103 | - 12.1.0 104 | - dependency-name: "@commitlint/config-conventional" 105 | versions: 106 | - 12.1.0 107 | - dependency-name: "@storybook/addon-essentials" 108 | versions: 109 | - 6.2.0 110 | - dependency-name: "@babel/core" 111 | versions: 112 | - 7.13.0 113 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 30 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilStale: 60 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - pinned 8 | - security 9 | - core-team 10 | - in progress 11 | # Label to use when marking an issue as stale 12 | staleLabel: stale 13 | # Comment to post when marking an issue as stale. Set to `false` to disable 14 | markComment: > 15 | This issue has been automatically marked as stale because it has not had 16 | recent activity. It will be closed if no further activity occurs. Thank you 17 | for your contributions. 18 | # Comment to post when closing a stale issue. Set to `false` to disable 19 | closeComment: false 20 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: GitHub CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - "!master" 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | npm: 13 | runs-on: ${{ matrix.os }} 14 | strategy: 15 | matrix: 16 | node-version: [14.x] 17 | os: [macOS-latest, windows-latest, ubuntu-latest] 18 | steps: 19 | - run: git config --global core.autocrlf false # this is needed to prevent git changing EOL after cloning on Windows OS 20 | - uses: actions/checkout@v2 21 | - name: Use Node.js 22 | uses: actions/setup-node@v1 23 | with: 24 | node-version: ${{ matrix.node-version }} 25 | - name: Install with npm 26 | run: | 27 | npm install 28 | - name: Lint code 29 | run: | 30 | npm run lint 31 | - name: Build 32 | run: | 33 | npm run build 34 | - name: Storybook build 35 | run: | 36 | npm run build-storybook 37 | - name: Run tests 38 | run: | 39 | npm run coveralls 40 | - name: Coveralls 41 | uses: coverallsapp/github-action@master 42 | with: 43 | github-token: ${{ secrets.GITHUB_TOKEN }} 44 | path-to-lcov: ./lcov.info 45 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: "Code scanning" 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | # The branches below must be a subset of the branches above 8 | branches: [master] 9 | schedule: 10 | - cron: '0 19 * * 0' 11 | 12 | jobs: 13 | CodeQL-Build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: Checkout repository 19 | uses: actions/checkout@v2 20 | with: 21 | # We must fetch at least the immediate parents so that if this is 22 | # a pull request then we can checkout the head. 23 | fetch-depth: 2 24 | 25 | # If this run was triggered by a pull request event, then checkout 26 | # the head of the pull request instead of the merge commit. 27 | - run: git checkout HEAD^2 28 | if: ${{ github.event_name == 'pull_request' }} 29 | 30 | # Initializes the CodeQL tools for scanning. 31 | - name: Initialize CodeQL 32 | uses: github/codeql-action/init@v1 33 | # Override language selection by uncommenting this and choosing your languages 34 | # with: 35 | # languages: go, javascript, csharp, python, cpp, java 36 | 37 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 38 | # If this step fails, then you should remove it and run the build manually (see below) 39 | - name: Autobuild 40 | uses: github/codeql-action/autobuild@v1 41 | 42 | # ℹ️ Command-line programs to run using the OS shell. 43 | # 📚 https://git.io/JvXDl 44 | 45 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 46 | # and modify them (or add more) to build your code if your project 47 | # uses a compiled language 48 | 49 | #- run: | 50 | # make bootstrap 51 | # make release 52 | 53 | - name: Perform CodeQL Analysis 54 | uses: github/codeql-action/analyze@v1 55 | -------------------------------------------------------------------------------- /.github/workflows/pr-updater.yml: -------------------------------------------------------------------------------- 1 | name: PR update 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | autoupdate: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v1 13 | - name: PR updater 14 | uses: maxkomarychev/pr-updater-action@v1.0.0 15 | with: 16 | token: ${{ secrets.GH_TOKEN }} 17 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | push: 4 | branches: 5 | - master 6 | jobs: 7 | release: 8 | name: Release 9 | runs-on: ubuntu-18.04 10 | steps: 11 | - name: Checkout 12 | uses: actions/checkout@v1 13 | - name: Setup Node.js 14 | uses: actions/setup-node@v1 15 | with: 16 | node-version: 14 17 | - name: Install dependencies 18 | run: npm install 19 | - name: Release 20 | env: 21 | GITHUB_TOKEN: ${{ secrets.BOT_PAT }} 22 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 23 | run: npx semantic-release 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | lcov.info 17 | 18 | # nyc test coverage 19 | .nyc_output 20 | 21 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 22 | .grunt 23 | 24 | # node-waf configuration 25 | .lock-wscript 26 | 27 | dist 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | 39 | #misc 40 | .DS_Store 41 | .env 42 | .env.* 43 | .rts2* 44 | package-lock.json 45 | storybook-static 46 | yarn.lock 47 | pnpm-lock.yaml 48 | yarn-error.log 49 | .idea/ 50 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | save-prefix='~' -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100, 3 | "tabWidth": 2, 4 | "semi": false, 5 | "singleQuote": true, 6 | "trailingComma": "all", 7 | "bracketSpacing": true, 8 | "jsxBracketSameLine": false, 9 | "arrowParens": "always", 10 | "endOfLine": "auto" 11 | } 12 | -------------------------------------------------------------------------------- /.releaserc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "@semantic-release/commit-analyzer", 4 | "@semantic-release/release-notes-generator", 5 | "@semantic-release/changelog", 6 | "@semantic-release/npm", 7 | ["@semantic-release/git", { 8 | "assets": ["package.json", "CHANGELOG.md"], 9 | "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" 10 | }], 11 | "@semantic-release/github" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /.storybook/main.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | module.exports = { 3 | "stories": [ 4 | "../stories/**/*.stories.mdx", 5 | "../stories/**/*.stories.@(js|jsx|ts|tsx)" 6 | ], 7 | "addons": [ 8 | "@storybook/addon-links", 9 | "@storybook/addon-essentials" 10 | ], 11 | "typescript": { 12 | check: true, 13 | checkOptions: { 14 | tsconfig: path.resolve(__dirname, '../stories/tsconfig.json'),//We want to change this when https://github.com/storybookjs/storybook/issues/11804 is merged. 15 | compilerOptions : { 16 | 17 | } 18 | }, 19 | reactDocgen: 'react-docgen-typescript', 20 | reactDocgenTypescriptOptions: { 21 | shouldExtractLiteralValuesFromEnum: true, 22 | propFilter: (prop) => (prop.parent ? !/node_modules/.test(prop.parent.fileName) : true), 23 | }, 24 | }, 25 | "webpackFinal": async (config, { configType }) => { 26 | // `configType` has a value of 'DEVELOPMENT' or 'PRODUCTION' 27 | // You can change the configuration based on that. 28 | // 'PRODUCTION' is used when building the static version of storybook. 29 | 30 | // Make whatever fine-grained changes you need 31 | config.module.rules.push({ 32 | test: /\.s[ac]ss$/i, 33 | use: [ 34 | // Creates `style` nodes from JS strings 35 | 'style-loader', 36 | // Translates CSS into CommonJS 37 | 'css-loader', 38 | // Compiles Sass to CSS 39 | 'sass-loader', 40 | ], 41 | }); 42 | 43 | // Return the altered config 44 | return config; 45 | }, 46 | } -------------------------------------------------------------------------------- /.storybook/manager.js: -------------------------------------------------------------------------------- 1 | import { addons } from '@storybook/addons'; 2 | import theme from './theme'; 3 | 4 | addons.setConfig({ 5 | theme: theme, 6 | showPanel: false, 7 | isFullscreen: false, 8 | isToolshown: true, 9 | }); -------------------------------------------------------------------------------- /.storybook/preview.js: -------------------------------------------------------------------------------- 1 | import theme from './theme'; 2 | import { addDecorator } from '@storybook/react'; 3 | 4 | export const parameters = { 5 | actions: { argTypesRegex: "^on[A-Z].*" }, 6 | docs: { 7 | theme: theme 8 | }, 9 | options: { 10 | storySort: { 11 | method: 'alphabetical', 12 | order: ['Welcome', ], 13 | }, 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.storybook/theme.js: -------------------------------------------------------------------------------- 1 | import { create } from '@storybook/theming' 2 | export default create({ 3 | base: 'light', 4 | brandTitle: `HospitalRun Components`, 5 | brandUrl: 'https://github.com/HospitalRun/components', 6 | brandImage: 7 | 'https://raw.githubusercontent.com/HospitalRun/design/master/logo/horizontal/logo-on-transparent.png', 8 | fontBase: '"Open Sans", sans-serif', 9 | fontCode: 'monospace', 10 | }) -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib", 3 | "editor.codeActionsOnSave": { 4 | "source.fixAll.eslint": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 HospitalRun 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Responsible Disclosure Policy 2 | 3 | A responsible disclosure policy helps protect the project and its users from security vulnerabilities discovered in the project’s scope by employing a process where vulnerabilities are publicly disclosed after a reasonable time period to allow patching the vulnerability. 4 | 5 | All security bugs are taken seriously and are considered as top priority. 6 | Your efforts to responsibly disclose your findings are appreciated and will be taken into account to acknowledge your contributions. 7 | 8 | ## Supported Versions 9 | 10 | This versions of HospitalRun project are currently being supported with security updates. 11 | 12 | | Version | Supported | 13 | | ------- | ------------------ | 14 | | 2.0.0 | :white_check_mark: | 15 | | 1.0.0-beta | :x: | 16 | 17 | ## Reporting a Vulnerability 18 | 19 | Report security bugs by opening a new [Security Issue](https://github.com/HospitalRun/components/issues/new?template=security.md). You can also report a vulnerability by emailing security@hospitalrun.io. 20 | 21 | Report security bugs in third-party modules to the maintainer or team maintaining the module. 22 | -------------------------------------------------------------------------------- /__mocks__/matchMediaMock.js: -------------------------------------------------------------------------------- 1 | window.matchMedia = jest.fn().mockImplementation((query) => ({ 2 | matches: false, 3 | media: query, 4 | onchange: null, 5 | addListener: jest.fn(), // deprecated 6 | removeListener: jest.fn(), // deprecated 7 | addEventListener: jest.fn(), 8 | removeEventListener: jest.fn(), 9 | dispatchEvent: jest.fn(), 10 | })) 11 | -------------------------------------------------------------------------------- /__mocks__/styleMock.js: -------------------------------------------------------------------------------- 1 | module.exports = {} 2 | -------------------------------------------------------------------------------- /_redirects: -------------------------------------------------------------------------------- 1 | # Redirect default Netlify subdomain to primary domain 2 | https://hospitalrun-components.netlify.com/* https://components.hospitalrun.io/:splat 301! 3 | -------------------------------------------------------------------------------- /buildDocs.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * This example script expects a JSON blob generated by react-docgen as input, 5 | * e.g. react-docgen components/* | buildDocs.sh 6 | */ 7 | 8 | var fs = require('fs'); 9 | var generateMarkdown = require('./generateMarkdown'); 10 | var path = require('path'); 11 | 12 | var json = ''; 13 | process.stdin.setEncoding('utf8'); 14 | process.stdin.on('readable', function() { 15 | var chunk = process.stdin.read(); 16 | if (chunk !== null) { 17 | json += chunk; 18 | } 19 | }); 20 | 21 | process.stdin.on('end', function() { 22 | buildDocs(JSON.parse(json)); 23 | }); 24 | 25 | function buildDocs(api) { 26 | // api is an object keyed by filepath. We use the file name as component name. 27 | for (var filepath in api) { 28 | var name = getComponentName(filepath); 29 | var markdown = generateMarkdown(name, api[filepath]); 30 | const path = process.cwd() + '/docs/'; 31 | fs.mkdirSync(path, { recursive: true }); 32 | fs.writeFileSync(path + name + '.md', markdown); 33 | process.stdout.write(filepath + ' -> ' + path + name + '.md\n'); 34 | } 35 | } 36 | 37 | function getComponentName(filepath) { 38 | var name = path.basename(filepath); 39 | // check for index.js 40 | if (name === 'index.js') { 41 | const dirs = path.dirname(filepath).split('/'); 42 | name = dirs[dirs.length - 1]; 43 | } 44 | var ext; 45 | while ((ext = path.extname(name))) { 46 | name = name.substring(0, name.length - ext.length); 47 | } 48 | return name; 49 | } -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { extends: ['@commitlint/config-conventional'] } 2 | -------------------------------------------------------------------------------- /scss/_variables.scss: -------------------------------------------------------------------------------- 1 | $theme-colors: ( 2 | "primary": #1abc9c, 3 | "secondary": #009b9e, 4 | "success": #273647, 5 | "warning": #ffc107, 6 | "danger": #dc3545, 7 | "info": #177db8, 8 | "light": #fdfffc, 9 | "dark": #011627 10 | ); 11 | -------------------------------------------------------------------------------- /scss/main.scss: -------------------------------------------------------------------------------- 1 | /* Bootstrap overrides */ 2 | 3 | @import "./_variables"; 4 | 5 | /* Import bootstrap */ 6 | 7 | @import "~bootstrap/scss/bootstrap"; 8 | 9 | /* Other CSS Imports */ 10 | 11 | 12 | /* All component level SCSS files are imported @ component level */ -------------------------------------------------------------------------------- /src/components/Alert/Alert.tsx: -------------------------------------------------------------------------------- 1 | import React, { Component, ReactNode, CSSProperties } from 'react' 2 | import BootstrapAlert from 'react-bootstrap/Alert' 3 | 4 | import { ColorVariant } from '../../interfaces' 5 | import { Button } from '../Button' 6 | 7 | export interface AlertProps { 8 | /** 9 | * Defines the color of the alert. Defaults to primary. 10 | * @default "primary" 11 | */ 12 | color?: ColorVariant 13 | /** Defines the title of the alert. */ 14 | title?: string 15 | /** Defines the message of the alert. */ 16 | message?: ReactNode 17 | /** Defines if the alert should be dismissible. Defaults to false. */ 18 | dismissible?: boolean 19 | /** 20 | * Defines the label of the close button if the alert is dismissible. 21 | * @default "Dismiss" 22 | * */ 23 | closeLabel?: string 24 | /** 25 | * Defines the class of the alert 26 | */ 27 | className?: string 28 | /** 29 | * Defines the style of the alert 30 | */ 31 | style?: CSSProperties 32 | /** 33 | * Defines the class of the close button 34 | */ 35 | btnClassName?: string 36 | /** 37 | * Defines the style of the close button 38 | */ 39 | btnStyle?: CSSProperties 40 | } 41 | 42 | interface State { 43 | show: boolean 44 | } 45 | 46 | /** 47 | * Alerts can provide contextual feedback messages for typical user actions 48 | * with the handful of available and flexible alert messages. 49 | */ 50 | 51 | class Alert extends Component { 52 | constructor(props: AlertProps) { 53 | super(props) 54 | this.state = { 55 | show: true, 56 | } 57 | } 58 | 59 | render() { 60 | const { 61 | color = 'primary', 62 | title, 63 | message, 64 | dismissible = false, 65 | closeLabel = 'Dismiss', 66 | className, 67 | style, 68 | btnClassName, 69 | btnStyle, 70 | } = this.props 71 | 72 | const { show } = this.state 73 | 74 | if (show) { 75 | return ( 76 | this.setState({ show: false })} 79 | dismissible={dismissible} 80 | closeLabel={closeLabel} 81 | className={className} 82 | style={style} 83 | > 84 | {title && {title}} 85 | {message &&
{message}
} 86 | {dismissible && ( 87 | <> 88 |
89 |
90 | 99 |
{' '} 100 | 101 | )} 102 |
103 | ) 104 | } 105 | return <> 106 | } 107 | } 108 | 109 | export { Alert } 110 | -------------------------------------------------------------------------------- /src/components/Alert/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './Alert' 2 | -------------------------------------------------------------------------------- /src/components/Badge/Badge.tsx: -------------------------------------------------------------------------------- 1 | import React, { CSSProperties } from 'react' 2 | import BootstrapBadge from 'react-bootstrap/Badge' 3 | 4 | import { ColorVariant } from '../../interfaces' 5 | 6 | export interface BadgeProps { 7 | /** 8 | * Defines the color of the badge. Defaults to primary. 9 | * @default "primary" 10 | */ 11 | color?: ColorVariant 12 | /** The children to render */ 13 | children?: React.ReactNode 14 | /** Defines the class of the badge */ 15 | className?: string 16 | /** Defines the style of the badge */ 17 | style?: CSSProperties 18 | } 19 | 20 | /** 21 | * Badges can provide contextual clues to the user by differentiating color and styling 22 | * from the surrounding content. 23 | */ 24 | const Badge = (props: BadgeProps) => { 25 | const { color, children, className, style } = props 26 | return ( 27 | 28 | {children} 29 | 30 | ) 31 | } 32 | 33 | Badge.defaultProps = { 34 | color: 'primary', 35 | } 36 | 37 | export { Badge } 38 | -------------------------------------------------------------------------------- /src/components/Badge/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './Badge' 2 | -------------------------------------------------------------------------------- /src/components/Breadcrumb/Breadcrumb.tsx: -------------------------------------------------------------------------------- 1 | import React, { CSSProperties } from 'react' 2 | import BootstrapBreadcrumb from 'react-bootstrap/Breadcrumb' 3 | 4 | export interface BreadcrumbProps { 5 | /** The children to render */ 6 | children?: React.ReactNode 7 | /** 8 | * Defines the class of the breadcrumb. 9 | */ 10 | className?: string 11 | /** 12 | * Defines the style of the breadcrumb. 13 | */ 14 | style?: CSSProperties 15 | } 16 | 17 | const Breadcrumb = (props: BreadcrumbProps) => { 18 | const { children, className, style } = props 19 | 20 | return ( 21 | 22 | {children} 23 | 24 | ) 25 | } 26 | 27 | export { Breadcrumb } 28 | -------------------------------------------------------------------------------- /src/components/Breadcrumb/BreadcrumbItem.tsx: -------------------------------------------------------------------------------- 1 | import React, { CSSProperties } from 'react' 2 | import BootstrapBreadcrumbItem from 'react-bootstrap/BreadcrumbItem' 3 | 4 | interface Props { 5 | /** The children to render */ 6 | children?: React.ReactNode 7 | /** Adds active class and renders wraps children in span */ 8 | active?: boolean 9 | /** Adds custom event */ 10 | onClick?: (event: React.MouseEvent) => void 11 | /** 12 | * Defines the class of the Breadcrumb Item. 13 | */ 14 | className?: string 15 | /** 16 | * Defines the style of the Breadcrumb Item. 17 | */ 18 | style?: CSSProperties 19 | } 20 | 21 | const BreadcrumbItem = ({ children, active, onClick, className, style }: Props) => ( 22 | 23 | {children} 24 | 25 | ) 26 | 27 | export { BreadcrumbItem } 28 | -------------------------------------------------------------------------------- /src/components/Breadcrumb/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './Breadcrumb' 2 | export * from './BreadcrumbItem' 3 | -------------------------------------------------------------------------------- /src/components/Button/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './Button' 2 | -------------------------------------------------------------------------------- /src/components/Button/interfaces.ts: -------------------------------------------------------------------------------- 1 | export type ButtonType = 2 | | 'primary' 3 | | 'secondary' 4 | | 'success' 5 | | 'danger' 6 | | 'warning' 7 | | 'info' 8 | | 'dark' 9 | | 'light' 10 | | 'link' 11 | | 'outline-primary' 12 | | 'outline-secondary' 13 | | 'outline-success' 14 | | 'outline-danger' 15 | | 'outline-warning' 16 | | 'outline-info' 17 | | 'outline-dark' 18 | | 'outline-light' 19 | 20 | export type ButtonColor = 21 | | 'primary' 22 | | 'secondary' 23 | | 'success' 24 | | 'warning' 25 | | 'danger' 26 | | 'info' 27 | | 'light' 28 | | 'dark' 29 | -------------------------------------------------------------------------------- /src/components/Calendar/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Calendar' 2 | -------------------------------------------------------------------------------- /src/components/Calendar/interfaces.ts: -------------------------------------------------------------------------------- 1 | export default interface Event { 2 | id: string 3 | allDay: boolean 4 | start: Date | null 5 | end: Date | null 6 | title: string 7 | } 8 | -------------------------------------------------------------------------------- /src/components/Callout/Callout.tsx: -------------------------------------------------------------------------------- 1 | import classNames from 'classnames' 2 | import React, { CSSProperties, ReactElement } from 'react' 3 | 4 | import { ColorVariant } from '../../interfaces' 5 | 6 | import './callout.scss' 7 | 8 | export interface CalloutProps { 9 | /** 10 | * Defines the title of the callout. 11 | */ 12 | title?: string 13 | /** 14 | * Defines the color of the callout. 15 | */ 16 | color?: ColorVariant 17 | /** 18 | * Defines the body of the callout. 19 | */ 20 | children?: React.ReactElement 21 | /** 22 | * Styling through CSS classes 23 | */ 24 | className?: string 25 | /** 26 | * Styilng through inline CSSProperties 27 | */ 28 | style?: CSSProperties 29 | } 30 | 31 | export const Callout = ({ 32 | title, 33 | color, 34 | children, 35 | className, 36 | style, 37 | }: CalloutProps): ReactElement => { 38 | const calloutClass = classNames(className, 'callout', `callout-${color}`) 39 | return ( 40 |
41 | {title ?

{title}

: null} 42 | {children} 43 |
44 | ) 45 | } 46 | 47 | Callout.defaultProps = { 48 | color: 'primary', 49 | } 50 | -------------------------------------------------------------------------------- /src/components/Callout/callout.scss: -------------------------------------------------------------------------------- 1 | @import '../../../scss/_variables.scss'; 2 | 3 | .callout { 4 | padding: 20px; 5 | margin: 20px 0; 6 | border: 1px solid #eee; 7 | border-left-width: 5px; 8 | border-radius: 3px; 9 | h4 { 10 | margin-top: 0; 11 | margin-bottom: 5px; 12 | } 13 | p:last-child { 14 | margin-bottom: 0; 15 | } 16 | code { 17 | border-radius: 3px; 18 | } 19 | & + .bs-callout { 20 | margin-top: -5px; 21 | } 22 | } 23 | 24 | @each $name,$color in $theme-colors { 25 | .callout-#{$name} { 26 | border-left-color: $color; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/components/Callout/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './Callout' 2 | -------------------------------------------------------------------------------- /src/components/Checkbox/Checkbox.tsx: -------------------------------------------------------------------------------- 1 | import React, { CSSProperties } from 'react' 2 | import FormCheck from 'react-bootstrap/FormCheck' 3 | 4 | interface Props { 5 | /** The id for the checkbox */ 6 | id?: string 7 | /** The label to render next to the checkbox */ 8 | label: string 9 | /* Determines the side of the checkbox to render the label on. By default right */ 10 | labelSide?: 'right' | 'left' 11 | /** Gives the checkbox a name */ 12 | name?: string 13 | /** Determines if the checkbox should be disabled or not. By default false */ 14 | disabled?: boolean 15 | /** Determines if the checkbox should render inline or not. By default false. */ 16 | inline?: boolean 17 | /** The onChange listener */ 18 | onChange?: (event: React.ChangeEvent) => void 19 | /** 20 | * Defines the class of the checkbox. 21 | */ 22 | className?: string 23 | /** 24 | * Defines the style of the checkbox. 25 | */ 26 | style?: CSSProperties 27 | /** 28 | * Defines the class of the checkbox. 29 | */ 30 | labelClassName?: string 31 | /** 32 | * Defines the style of the checkbox. 33 | */ 34 | labelStyle?: CSSProperties 35 | /** Determines if the checkbox is checked or not. By default false */ 36 | checked?: boolean 37 | } 38 | 39 | /** 40 | * Checkbox is used to mark if something is true or not. Often times is used in a group where 41 | * multiple things can be true at one time. 42 | */ 43 | const Checkbox = (props: Props) => { 44 | const { 45 | id, 46 | label, 47 | name, 48 | inline, 49 | labelSide, 50 | disabled, 51 | onChange, 52 | className, 53 | style, 54 | labelClassName, 55 | labelStyle, 56 | checked, 57 | } = props 58 | 59 | const getLabel = () => ( 60 | 61 | {label} 62 | 63 | ) 64 | 65 | return ( 66 | 67 | {labelSide === 'left' && getLabel()} 68 | 75 | {labelSide === 'right' && getLabel()} 76 | 77 | ) 78 | } 79 | 80 | Checkbox.defaultProps = { 81 | labelSide: 'right', 82 | } 83 | 84 | export { Checkbox } 85 | -------------------------------------------------------------------------------- /src/components/Checkbox/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './Checkbox' 2 | -------------------------------------------------------------------------------- /src/components/DateTimePicker/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './DateTimePicker' 2 | -------------------------------------------------------------------------------- /src/components/Dropdown/Dropdown.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import DropdownRB from 'react-bootstrap/Dropdown' 3 | import DropdownButton from 'react-bootstrap/DropdownButton' 4 | 5 | import { ButtonVariant } from '../../interfaces' 6 | import { Item } from './interfaces' 7 | 8 | interface Props { 9 | /** Determines the dropdown toggle text */ 10 | text: string 11 | /** Determines the dropdown's items */ 12 | items: Item[] 13 | /** Determines the dropdown toggle button id */ 14 | id: string 15 | /** Determines the dropdown toggle variant color */ 16 | variant: ButtonVariant 17 | /** Determines the horizontal alignment of the dropdown items */ 18 | alignRight?: boolean 19 | /** Determines the dropdown toggle button size */ 20 | size?: 'sm' | 'md' | 'lg' 21 | /** Determines the dropdown's direction */ 22 | direction?: 'down' | 'up' | 'left' | 'right' 23 | /** Determines the dropdown's custom style */ 24 | style?: Record 25 | } 26 | 27 | /** 28 | * Customizable dropdown component based on React-Bootstrap dropdown 29 | * 30 | */ 31 | const Dropdown = (props: Props) => { 32 | const { text, size, id, items, direction, variant, style, alignRight } = props 33 | 34 | const getDropdownItem = (item: Item, i: number) => ( 35 | 41 | {item.text} 42 | 43 | ) 44 | 45 | return ( 46 | 55 | {items.map((item, i) => getDropdownItem(item, i))} 56 | 57 | ) 58 | } 59 | Dropdown.defaultProps = { 60 | id: `dropdown${Math.floor(Math.random() * 10000)}`, 61 | variant: 'light', 62 | size: 'sm', 63 | direction: 'down', 64 | } 65 | 66 | export { Dropdown } 67 | -------------------------------------------------------------------------------- /src/components/Dropdown/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './Dropdown' 2 | -------------------------------------------------------------------------------- /src/components/Dropdown/interfaces.tsx: -------------------------------------------------------------------------------- 1 | import { ButtonVariant } from '../../interfaces' 2 | 3 | export interface Item { 4 | /* Item text */ 5 | text: string 6 | /* Redirect or handle other click events */ 7 | onClick: (event: React.MouseEvent) => void 8 | /* Item event key */ 9 | eventKey?: string 10 | /* Item variant color */ 11 | variant?: ButtonVariant 12 | /* Item id */ 13 | id?: string 14 | /* Item key */ 15 | key?: string 16 | /* Custom style */ 17 | style?: Record 18 | } 19 | -------------------------------------------------------------------------------- /src/components/Graph/BarGraph.tsx: -------------------------------------------------------------------------------- 1 | import ChartJs from 'chart.js' 2 | import React, { Component } from 'react' 3 | 4 | import { Axis, Dataset } from './interfaces' 5 | import * as util from './util' 6 | 7 | interface Props { 8 | /** Determines if the bar graph should be displayed in a horizontal manner */ 9 | horizontal?: boolean 10 | /** Determines if the bar graph should be displayed as a stacked bar graph */ 11 | stacked?: boolean 12 | 13 | /** the title of the graph for the legend */ 14 | title?: string 15 | /** the font size for the title */ 16 | titleFontSize?: number 17 | /** the color for the title */ 18 | titleFontColor?: string 19 | /** the datasets for the graph */ 20 | datasets: Dataset[] 21 | /** The width of the graph */ 22 | width?: string 23 | /** The height of the graph */ 24 | height?: string 25 | 26 | /** the list of x axis information */ 27 | xAxes: Axis[] 28 | /** the list of y axis information */ 29 | yAxes: Axis[] 30 | } 31 | 32 | /** 33 | * A customizable Bar Graph component built on chart.js 34 | */ 35 | class BarGraph extends Component> { 36 | graph: ChartJs | null 37 | 38 | chart: HTMLCanvasElement | null 39 | 40 | constructor(props: Props) { 41 | super(props) 42 | this.graph = null // initalzied in componentDidMount 43 | this.chart = null // initalzied in componentDidMount 44 | } 45 | 46 | componentDidMount() { 47 | const { 48 | stacked, 49 | title, 50 | titleFontSize, 51 | titleFontColor, 52 | datasets, 53 | horizontal, 54 | xAxes, 55 | yAxes, 56 | } = this.props 57 | 58 | const isStacked = !!stacked 59 | const type = horizontal ? 'horizontalBar' : 'bar' 60 | const config = util.getCommonChartConfigurations( 61 | type, 62 | title, 63 | titleFontSize, 64 | titleFontColor, 65 | datasets, 66 | ) 67 | 68 | if (config && config.options) { 69 | let scales 70 | if (!horizontal) { 71 | scales = { 72 | xAxes: util.getAxes(xAxes, isStacked), 73 | yAxes: util.getAxes(yAxes, isStacked), 74 | } 75 | } else { 76 | scales = { 77 | xAxes: util.getAxes(yAxes, isStacked), 78 | yAxes: util.getAxes(xAxes, isStacked), 79 | } 80 | } 81 | 82 | config.options.scales = scales 83 | } 84 | 85 | this.graph = new ChartJs(this.chart as HTMLCanvasElement, config) 86 | } 87 | 88 | render() { 89 | return ( 90 | { 92 | this.chart = chart 93 | return this.chart 94 | }} 95 | /> 96 | ) 97 | } 98 | } 99 | 100 | export { BarGraph } 101 | -------------------------------------------------------------------------------- /src/components/Graph/LineGraph.tsx: -------------------------------------------------------------------------------- 1 | import ChartJs from 'chart.js' 2 | import React, { Component } from 'react' 3 | 4 | import { Axis, Dataset } from './interfaces' 5 | import * as util from './util' 6 | 7 | interface Props { 8 | /** Determines if the area under the line should be filled with the background color from the dataset */ 9 | fill?: boolean 10 | /** Determines if the the Line Graphs should be displayed in a stacked manner */ 11 | stacked?: boolean 12 | 13 | /** the title of the graph for the legend */ 14 | title?: string 15 | /** the font size for the title */ 16 | titleFontSize?: number 17 | /** the color for the title */ 18 | titleFontColor?: string 19 | /** the datasets for the graph */ 20 | datasets: Dataset[] 21 | /** The width of the graph */ 22 | width?: string 23 | /** The height of the graph */ 24 | height?: string 25 | 26 | /** the list of x axis information */ 27 | xAxes: Axis[] 28 | /** the list of y axis information */ 29 | yAxes: Axis[] 30 | } 31 | 32 | /** 33 | * A customizable Line Graph component built on chart.js 34 | */ 35 | class LineGraph extends Component> { 36 | graph: ChartJs | null 37 | 38 | chart: HTMLCanvasElement | null 39 | 40 | constructor(props: Props) { 41 | super(props) 42 | this.graph = null // initalzied in componentDidMount 43 | this.chart = null // initalzied in componentDidMount 44 | } 45 | 46 | componentDidMount() { 47 | const { 48 | title, 49 | titleFontSize, 50 | titleFontColor, 51 | datasets, 52 | stacked, 53 | fill, 54 | yAxes, 55 | xAxes, 56 | } = this.props 57 | 58 | const type = 'line' 59 | let isFill = false 60 | if (fill) { 61 | isFill = fill 62 | } 63 | 64 | const config = util.getCommonChartConfigurations( 65 | type, 66 | title, 67 | titleFontSize, 68 | titleFontColor, 69 | datasets, 70 | ) 71 | if (config && config.data && config.data.datasets) { 72 | for (let i = 0; i < datasets.length; i += 1) { 73 | config.data.datasets[i].fill = isFill 74 | config.data.datasets[i].backgroundColor = datasets[i].backgroundColor 75 | config.data.datasets[i].borderColor = datasets[i].borderColor 76 | } 77 | } 78 | 79 | if (config && config.options) { 80 | const isStacked = !!stacked 81 | const scales = { 82 | xAxes: util.getAxes(xAxes, false), 83 | yAxes: util.getAxes(yAxes, isStacked), 84 | } 85 | 86 | config.options.scales = scales 87 | } 88 | 89 | this.graph = new ChartJs(this.chart as HTMLCanvasElement, config) 90 | } 91 | 92 | render() { 93 | return ( 94 | { 96 | this.chart = chart 97 | return this.chart 98 | }} 99 | /> 100 | ) 101 | } 102 | } 103 | 104 | export { LineGraph } 105 | -------------------------------------------------------------------------------- /src/components/Graph/PieGraph.tsx: -------------------------------------------------------------------------------- 1 | import ChartJs from 'chart.js' 2 | import React, { Component } from 'react' 3 | 4 | import { Dataset } from './interfaces' 5 | import * as util from './util' 6 | 7 | interface Props { 8 | /** Determines if the pie graph should be displayed with a doughnut */ 9 | doughnut?: boolean 10 | 11 | /** the title of the graph for the legend */ 12 | title?: string 13 | /** the font size for the title */ 14 | titleFontSize?: number 15 | /** the color for the title */ 16 | titleFontColor?: string 17 | /** the datasets for the graph */ 18 | datasets: Dataset[] 19 | /** The width of the graph */ 20 | width?: string 21 | /** The height of the graph */ 22 | height?: string 23 | } 24 | 25 | /** 26 | * A customizable Pie Graph component built on chart.js 27 | */ 28 | class PieGraph extends Component> { 29 | graph: ChartJs | null 30 | 31 | chart: HTMLCanvasElement | null 32 | 33 | constructor(props: Props) { 34 | super(props) 35 | this.graph = null // initalzied in componentDidMount 36 | this.chart = null // initalzied in componentDidMount 37 | } 38 | 39 | componentDidMount() { 40 | const { doughnut, title, titleFontSize, titleFontColor, datasets } = this.props 41 | const type = doughnut ? 'doughnut' : 'pie' 42 | const config = util.getCommonChartConfigurations( 43 | type, 44 | title, 45 | titleFontSize, 46 | titleFontColor, 47 | datasets, 48 | ) 49 | this.graph = new ChartJs(this.chart as HTMLCanvasElement, config) 50 | } 51 | 52 | render() { 53 | return ( 54 | { 56 | this.chart = chart 57 | return this.chart 58 | }} 59 | /> 60 | ) 61 | } 62 | } 63 | 64 | export { PieGraph } 65 | -------------------------------------------------------------------------------- /src/components/Graph/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './BarGraph' 2 | export * from './LineGraph' 3 | export * from './PieGraph' 4 | -------------------------------------------------------------------------------- /src/components/Graph/interfaces.ts: -------------------------------------------------------------------------------- 1 | export interface Data { 2 | /** the x data point */ 3 | x: any 4 | /** the y data point */ 5 | y: any 6 | /** the background color for the piece of data. On Bar/Pie Graphs this is the bar or part of the pie. On Line Graphs this is the point */ 7 | backgroundColor?: string 8 | /** the border color for the piece of data. On Bar/Pie Graphs this is the bar or part of the pie. On Line Graphs this is the point */ 9 | borderColor?: string 10 | } 11 | 12 | export interface Dataset { 13 | /** the label of the dataset to show in the legend */ 14 | label: string 15 | /** the default background color of the dataset for Bar/Pie Graphs or the fill color for Line Graphs */ 16 | backgroundColor?: string 17 | /** the default border color of the dataset for Bar/Pie Graphs or the line color for Line Graphs */ 18 | borderColor?: string 19 | /** the list of data for the dataset */ 20 | data: Data[] 21 | } 22 | 23 | export interface HasAxes { 24 | /** the list of x axis information */ 25 | xAxes: Axis[] 26 | /** the list of y axis information */ 27 | yAxes: Axis[] 28 | } 29 | 30 | export interface Graph { 31 | /** the title of the graph for the legend */ 32 | title?: string 33 | /** the font size for the title */ 34 | titleFontSize?: number 35 | /** the color for the title */ 36 | titleFontColor?: string 37 | /** the datasets for the graph */ 38 | datasets: Dataset[] 39 | /** The width of the graph */ 40 | width?: string 41 | /** The height of the graph */ 42 | height?: string 43 | } 44 | 45 | export interface Axis { 46 | /** the type of data that is appearing for that axis */ 47 | type: 'category' | 'linear' | 'time' 48 | /** the label for the axis */ 49 | label: string 50 | /** The format to display the time in, if the axis type is not `time, this value will be ignored */ 51 | timeFormat?: 52 | | 'millisecond' 53 | | 'second' 54 | | 'minute' 55 | | 'hour' 56 | | 'day' 57 | | 'week' 58 | | 'month' 59 | | 'quarter' 60 | | 'year' 61 | 62 | /** The number of ticks between each grid line */ 63 | timeStepSize?: number 64 | } 65 | -------------------------------------------------------------------------------- /src/components/Icon/Icon.tsx: -------------------------------------------------------------------------------- 1 | import { IconPrefix, IconName, SizeProp } from '@fortawesome/fontawesome-svg-core' 2 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' 3 | import React, { CSSProperties } from 'react' 4 | 5 | import { IconType } from './interfaces' 6 | 7 | // maps between hospital run icon names and font awesome 8 | const iconMap = { 9 | add: 'plus', 10 | admin: 'user-shield', 11 | appointment: 'calendar', 12 | 'appointment-add': 'calendar-plus', 13 | 'appointment-remove': 'calendar-minus', 14 | calendar: 'calendar-alt', 15 | billing: 'file-invoice-dollar', 16 | dashboard: 'columns', 17 | 'down-arrow': 'chevron-down', 18 | edit: 'edit', 19 | image: 'camera', 20 | incident: 'file-alt', 21 | inventory: 'boxes', 22 | lab: 'microscope', 23 | 'left-arrow': 'chevron-left', 24 | logout: 'sign-out-alt', 25 | medication: 'pills', 26 | menu: 'bars', 27 | patient: 'user', 28 | 'patient-add': 'user-plus', 29 | 'patient-remove': 'user-minus', 30 | patients: 'users', 31 | remove: 'minus', 32 | 'right-arrow': 'chevron-right', 33 | save: 'save', 34 | setting: 'cog', 35 | 'up-arrow': 'chevron-up', 36 | } 37 | 38 | function getFontAwesomeIcon(icon: IconType): string { 39 | return iconMap[icon] 40 | } 41 | 42 | interface Props { 43 | /** The type of icon to display */ 44 | icon: IconType 45 | size?: SizeProp 46 | /** Outline version or filled-in version. Note some icons may be missing outline version. */ 47 | outline?: boolean 48 | /** 49 | * Defines the class of the icon. 50 | */ 51 | className?: string 52 | /** 53 | * Defines the style of the icon. 54 | */ 55 | style?: CSSProperties 56 | /** Function to execute when user clicks on icon */ 57 | onClick?: (event: React.MouseEvent) => void 58 | } 59 | 60 | /** 61 | * Icons provide contextual clues to users to make it easier to recognize functionality 62 | */ 63 | const Icon = (props: Props) => { 64 | const { icon, outline, className, style, onClick, size } = props 65 | const iconPrefix = (outline ? 'far' : 'fas') as IconPrefix 66 | const faIconName = getFontAwesomeIcon(icon) as IconName 67 | 68 | return ( 69 | 76 | ) 77 | } 78 | 79 | Icon.defaultProps = { 80 | outline: false, 81 | size: '1x', 82 | } 83 | 84 | export { Icon } 85 | -------------------------------------------------------------------------------- /src/components/Icon/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './Icon' 2 | -------------------------------------------------------------------------------- /src/components/Icon/interfaces.ts: -------------------------------------------------------------------------------- 1 | export type IconType = 2 | | 'add' 3 | | 'admin' 4 | | 'appointment' 5 | | 'appointment-add' 6 | | 'appointment-remove' 7 | | 'billing' 8 | | 'calendar' 9 | | 'dashboard' 10 | | 'down-arrow' 11 | | 'edit' 12 | | 'image' 13 | | 'incident' 14 | | 'inventory' 15 | | 'lab' 16 | | 'left-arrow' 17 | | 'logout' 18 | | 'medication' 19 | | 'menu' 20 | | 'patient' 21 | | 'patient-add' 22 | | 'patient-remove' 23 | | 'patients' 24 | | 'remove' 25 | | 'right-arrow' 26 | | 'save' 27 | | 'setting' 28 | | 'up-arrow' 29 | -------------------------------------------------------------------------------- /src/components/Image/Image.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import BootstrapImage from 'react-bootstrap/Image' 3 | 4 | interface Props extends React.ImgHTMLAttributes { 5 | /** Determines if the image should should shrink to fit the parent's width. By default false. */ 6 | fluid?: boolean 7 | /** Determines if the image should have rounded edges. By default false. */ 8 | rounded?: boolean 9 | /** Determines if the image should be circular. By default false. */ 10 | circle?: boolean 11 | /** The source of the image to display. */ 12 | src: string 13 | } 14 | 15 | /** 16 | * Image used to display imagery in various shapes 17 | */ 18 | const Image = (props: Props) => { 19 | const { circle, fluid, rounded, src, ...imgAttributes } = props 20 | return ( 21 | 28 | ) 29 | } 30 | 31 | export { Image } 32 | -------------------------------------------------------------------------------- /src/components/Image/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './Image' 2 | -------------------------------------------------------------------------------- /src/components/Label/Label.tsx: -------------------------------------------------------------------------------- 1 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' 2 | import React, { CSSProperties } from 'react' 3 | import FormLabel from 'react-bootstrap/FormLabel' 4 | 5 | interface Props { 6 | /** Text to display in label */ 7 | text: string 8 | /** Title of the label. */ 9 | title?: string // Use on required input labels to override default required title 10 | /** Ties label to input */ 11 | htmlFor?: string 12 | /** Defines whether input is required. */ 13 | isRequired?: boolean 14 | /** 15 | * Defines the class of the label. 16 | */ 17 | className?: string 18 | /** 19 | * Defines the style of the label. 20 | */ 21 | style?: CSSProperties 22 | } 23 | /** 24 | * Svg instead of asterisk to avoid asterisk being read by screenreaders 25 | * hidden text to be read explaing the input is required incase the title attribute 26 | * is not supported by the screen reader 27 | */ 28 | const asterisk = React.createElement('i', { style: { color: 'red' } }, [ 29 | , 34 | ]) 35 | /** 36 | * Labels are used to display text 37 | */ 38 | const Label = (props: Props) => { 39 | const { text, htmlFor, isRequired, title, className, style } = props 40 | /** Form label for required inputs */ 41 | if (isRequired) { 42 | return ( 43 |
44 | 50 | {text} 51 | {asterisk} 52 | 53 |
54 | ) 55 | } 56 | /** Default form label */ 57 | return ( 58 | 59 | {text} 60 | 61 | ) 62 | } 63 | Label.defaultProps = { 64 | title: undefined, 65 | htmlFor: undefined, 66 | } 67 | export { Label } 68 | -------------------------------------------------------------------------------- /src/components/Label/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './Label' 2 | -------------------------------------------------------------------------------- /src/components/Layout/Column.tsx: -------------------------------------------------------------------------------- 1 | import React, { CSSProperties } from 'react' 2 | import BoostrapColumn from 'react-bootstrap/Col' 3 | 4 | interface Props { 5 | /** 6 | * HTML element to be used for the component 7 | * @default
8 | */ 9 | as?: React.ElementType 10 | /** 11 | * The number of columns to span on extra large devices (≥ 1200px) 12 | */ 13 | xl?: true | 'auto' | number | { span?: true | 'auto' | number; offset?: number; order?: number } 14 | /** 15 | * The number of columns to span on large devices (≥ 992px) 16 | */ 17 | lg?: true | 'auto' | number | { span?: true | 'auto' | number; offset?: number; order?: number } 18 | /** 19 | * The number of columns to span on medium devices (≥ 768px) 20 | */ 21 | md?: true | 'auto' | number | { span?: true | 'auto' | number; offset?: number; order?: number } 22 | /** 23 | * The number of columns to span on small devices (≥ 576px) 24 | */ 25 | sm?: true | 'auto' | number | { span?: true | 'auto' | number; offset?: number; order?: number } 26 | /** 27 | * The number of columns to span on extra small devices (< 576px) 28 | */ 29 | xs?: true | 'auto' | number | { span?: true | 'auto' | number; offset?: number; order?: number } 30 | /** 31 | * `ReactNode` elements to be wrapped in the component 32 | */ 33 | children?: React.ReactNode 34 | /** 35 | * Defines the class of the column. 36 | */ 37 | className?: string 38 | /** 39 | * Defines the style of the column. 40 | */ 41 | style?: CSSProperties 42 | } 43 | 44 | const Column = (props: Props) => { 45 | const { as, lg, md, sm, xl, xs, children, className, style } = props 46 | 47 | return ( 48 | 58 | {children} 59 | 60 | ) 61 | } 62 | 63 | export { Column } 64 | -------------------------------------------------------------------------------- /src/components/Layout/Container.tsx: -------------------------------------------------------------------------------- 1 | import React, { CSSProperties } from 'react' 2 | import BootstrapContainer from 'react-bootstrap/Container' 3 | 4 | interface Props { 5 | /** 6 | * A custom element to be used for the component 7 | * @default
8 | */ 9 | as?: React.ElementType 10 | /** 11 | * Allow the Container to fill all of its available horizontal space 12 | * @default false 13 | */ 14 | fluid?: boolean 15 | /** 16 | * A custom class to be used for the component 17 | */ 18 | className?: string 19 | /** 20 | * `ReactNode` elements to be wrapped in the component 21 | */ 22 | children?: React.ReactNode 23 | /** 24 | * Defines the style of the container. 25 | */ 26 | style?: CSSProperties 27 | } 28 | 29 | const Container = (props: Props) => { 30 | const { as, fluid, className, children, style } = props 31 | 32 | return ( 33 | 34 | {children} 35 | 36 | ) 37 | } 38 | 39 | export { Container } 40 | -------------------------------------------------------------------------------- /src/components/Layout/Row.tsx: -------------------------------------------------------------------------------- 1 | import React, { CSSProperties } from 'react' 2 | import BootstrapRow from 'react-bootstrap/Row' 3 | 4 | interface Props { 5 | /** 6 | * HTML element to be used for the component 7 | * @default
8 | */ 9 | as?: React.ElementType 10 | /** 11 | * Removes the gutter spacing between `Columns` as well as any added negative margins. 12 | * @default false 13 | */ 14 | noGutters?: boolean 15 | /** 16 | * A custom CSS class to be used for the component 17 | */ 18 | className?: string 19 | /** 20 | * `ReactNode` elements to be wrapped in the component 21 | */ 22 | children?: React.ReactNode 23 | /** 24 | * Defines the style of the row. 25 | */ 26 | style?: CSSProperties 27 | } 28 | 29 | const Row = (props: Props) => { 30 | const { as, noGutters, className, children, style } = props 31 | 32 | return ( 33 | 34 | {children} 35 | 36 | ) 37 | } 38 | 39 | export { Row } 40 | -------------------------------------------------------------------------------- /src/components/Layout/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './Container' 2 | export * from './Row' 3 | export * from './Column' 4 | -------------------------------------------------------------------------------- /src/components/List/List.tsx: -------------------------------------------------------------------------------- 1 | import React, { CSSProperties } from 'react' 2 | import ListGroup from 'react-bootstrap/ListGroup' 3 | 4 | interface Props { 5 | /** Removes outer borders and rounded corners to render list group items edge-to-edge in a parent container. */ 6 | layout?: 'flush' 7 | /** The children to render */ 8 | children?: React.ReactNode 9 | /** 10 | * Defines the class of the list. 11 | */ 12 | className?: string 13 | /** 14 | * Defines the style of the list. 15 | */ 16 | style?: CSSProperties 17 | } 18 | 19 | /** 20 | * Lists are a flexible and powerful component for displaying a 21 | * series of content. Modify and extend them to support just about any 22 | * content within. 23 | */ 24 | const List = (props: Props) => { 25 | const { layout, children, className, style } = props 26 | 27 | return ( 28 | 29 | {children} 30 | 31 | ) 32 | } 33 | 34 | export { List } 35 | -------------------------------------------------------------------------------- /src/components/List/ListItem.tsx: -------------------------------------------------------------------------------- 1 | import React, { CSSProperties } from 'react' 2 | import ListGroupItem from 'react-bootstrap/ListGroupItem' 3 | 4 | import { ColorVariant } from '../../interfaces' 5 | 6 | interface Props { 7 | /** Defines the color of the list item. */ 8 | color?: ColorVariant 9 | /** Applies additional hover, active and disabled styles to the list item. */ 10 | action?: boolean 11 | /** Indicates the list group's current active selection. */ 12 | active?: boolean 13 | /** Prevents actions on a list item. */ 14 | disabled?: boolean 15 | /** The link value for a list item. */ 16 | href?: string 17 | /** Handles the on click event for a list item. */ 18 | onClick?: (event: React.MouseEvent) => void 19 | /** The children to render. */ 20 | children?: React.ReactNode 21 | /** 22 | * Defines the class of the listitem. 23 | */ 24 | className?: string 25 | /** 26 | * Defines the style of the listitem. 27 | */ 28 | style?: CSSProperties 29 | } 30 | 31 | /** 32 | * ListItem components display the child elements of List components and support flexible layout, style, and interactivity options. 33 | */ 34 | const ListItem = (props: Props) => { 35 | const { color, action, active, disabled, href, onClick, children, className, style } = props 36 | 37 | return ( 38 | 48 | {children} 49 | 50 | ) 51 | } 52 | 53 | export { ListItem } 54 | -------------------------------------------------------------------------------- /src/components/List/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './List' 2 | export * from './ListItem' 3 | -------------------------------------------------------------------------------- /src/components/Modal/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './Modal' 2 | -------------------------------------------------------------------------------- /src/components/Modal/interfaces.ts: -------------------------------------------------------------------------------- 1 | export type ButtonsAlignment = 'left' | 'right' | 'center' | 'edges' 2 | -------------------------------------------------------------------------------- /src/components/Navbar/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './Navbar' 2 | -------------------------------------------------------------------------------- /src/components/Navbar/interfaces.tsx: -------------------------------------------------------------------------------- 1 | export interface NavItem { 2 | type: string 3 | /** Defines the class of the list. */ 4 | className?: string 5 | } 6 | 7 | export interface NavImage extends NavItem { 8 | /** A path which contains the company icon/image */ 9 | src: string 10 | /** A click handle which will redirect the user to the respectable webpage/path */ 11 | onClick?: (event: React.MouseEvent) => void 12 | /** Alternative text attribute */ 13 | alt?: string 14 | } 15 | 16 | export interface NavHeader extends NavItem { 17 | /** Clinic/Hospital name */ 18 | label: string 19 | /** Label color */ 20 | color?: string 21 | /** A click handle which will redirect the user to the respectable webpage/path */ 22 | onClick?: (event: React.MouseEvent) => void 23 | } 24 | 25 | export interface NavLink extends NavItem { 26 | /** The link name */ 27 | label: string | React.ReactElement 28 | /** Adds a top border to the link as a list divider */ 29 | dividerAbove?: boolean 30 | /** Inserts an icon to the left of the link when a valid icon name is passed */ 31 | icon?: string 32 | /** A click handle which will redirect the user to whenever it is clicked */ 33 | onClick?: (event: React.MouseEvent) => void 34 | /** Determines the href */ 35 | href?: string 36 | } 37 | 38 | export interface NavIcon extends NavLink { 39 | /** Label color */ 40 | color?: string 41 | /** An icon name */ 42 | name: string 43 | /** Size of icon */ 44 | size?: string 45 | /** CSS class(es) for icon */ 46 | iconClassName?: string 47 | /** Outline or filled version */ 48 | outline?: boolean 49 | } 50 | 51 | export interface NavLinkList extends NavLink { 52 | /** An array to hold a dropdown Links */ 53 | children: Array 54 | /** Align menu to the right of the nav */ 55 | alignRight?: boolean 56 | } 57 | 58 | export interface NavLinkListIcon extends NavIcon, NavLinkList {} 59 | 60 | export interface NavSearch extends NavItem { 61 | /** Defines the placeholder text. */ 62 | placeholderText?: string 63 | /** Defines the button text. */ 64 | buttonText?: string 65 | /** Defines the button variant. */ 66 | buttonColor?: 67 | | 'primary' 68 | | 'secondary' 69 | | 'success' 70 | | 'warning' 71 | | 'danger' 72 | | 'info' 73 | | 'light' 74 | | 'dark' 75 | /** Handles the on click search button event */ 76 | onClickButton: (event: React.MouseEvent) => void 77 | /** Handles the on change search form event */ 78 | onChangeInput: (event: React.ChangeEvent) => void 79 | /** Invoked as the user types to get the search suggestions */ 80 | onSearch?: (query: string) => Promise 81 | } 82 | -------------------------------------------------------------------------------- /src/components/Panel/Panel.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { Card, Collapse } from 'react-bootstrap' 3 | 4 | import { ColorVariant } from '../../interfaces' 5 | import { Icon } from '../Icon' 6 | 7 | interface Props { 8 | /** Defines the color of the panel */ 9 | color?: ColorVariant 10 | /** The body for the panel */ 11 | children?: React.ReactNode 12 | /** The title for the panel */ 13 | title?: string 14 | /** The footer for the panel */ 15 | footer?: string 16 | /** Determines if the panel can be collapsible */ 17 | collapsible?: boolean 18 | /** Determines if the panel should be collapsed */ 19 | collapsed?: boolean 20 | /** The className for the panel card parent element */ 21 | className?: string 22 | } 23 | 24 | const Panel = (props: Props) => { 25 | const { color, children, footer, title, collapsible, collapsed, className } = props 26 | const [open, setOpen] = useState(!collapsed || !collapsible) 27 | 28 | const collapseIcon = ( 29 | 30 | setOpen(!open)} 33 | aria-controls="collapse-body" 34 | aria-expanded={open} 35 | /> 36 | 37 | ) 38 | 39 | return ( 40 | 41 | {title && ( 42 | collapsible && setOpen(!open)} 45 | > 46 | {title} 47 | {collapsible && collapseIcon} 48 | 49 | )} 50 | 51 | {collapsible && !title && collapseIcon} 52 | 53 |
{children}
54 |
55 |
56 | {footer && ( 57 | {footer} 58 | )} 59 |
60 | ) 61 | } 62 | 63 | export { Panel } 64 | -------------------------------------------------------------------------------- /src/components/Panel/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './Panel' 2 | -------------------------------------------------------------------------------- /src/components/Pill/Pill.tsx: -------------------------------------------------------------------------------- 1 | import React, { CSSProperties } from 'react' 2 | import Badge from 'react-bootstrap/Badge' 3 | 4 | import { ColorVariant } from '../../interfaces' 5 | 6 | interface Props { 7 | /** Defines the color of the pill. Defaults to primary. */ 8 | color?: ColorVariant 9 | /** The children to render */ 10 | children?: React.ReactNode 11 | /** 12 | * Defines the class of the pill. 13 | */ 14 | className?: string 15 | /** 16 | * Defines the style of the pill. 17 | */ 18 | style?: CSSProperties 19 | } 20 | 21 | /** 22 | * Pills can provide contextual clues to the user by differentiating color and styling 23 | * from the surrounding content. 24 | */ 25 | const Pill = (props: Props) => { 26 | const { color, children, className, style } = props 27 | return ( 28 | 29 | {children} 30 | 31 | ) 32 | } 33 | 34 | Pill.defaultProps = { 35 | color: 'primary', 36 | } 37 | 38 | export { Pill } 39 | -------------------------------------------------------------------------------- /src/components/Pill/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './Pill' 2 | -------------------------------------------------------------------------------- /src/components/Radio/Radio.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactNode, CSSProperties } from 'react' 2 | import Form from 'react-bootstrap/Form' 3 | 4 | interface Props { 5 | /** Label to display next to the Radio. */ 6 | label?: string | ReactNode 7 | /** Necessary to link the label with the input. */ 8 | id?: string 9 | /** Name to group Radios together. Two Radios with the same name can't be checked at the same time. */ 10 | name?: string 11 | /** Value associated with the Radio. */ 12 | value?: string 13 | /** When inline, Radio elements are stacked horizontally instead of vertically. Default is false. */ 14 | inline?: boolean 15 | /** When disabled, the Radio cannot be clicked or changed by the user. Default is false. */ 16 | disabled?: boolean 17 | /** When checked is true, the Radio button is selected. */ 18 | defaultChecked?: boolean 19 | /** Determines whether the Radio should be rendered as invalid or not. Default is false. */ 20 | isInvalid?: boolean 21 | /** Determines whether the Radio should be rendered as valid or not. Default is false. */ 22 | isValid?: boolean 23 | /** Message to display when the Radio is invalid. */ 24 | feedback?: string | ReactNode 25 | /** Listener will be called when the Radio is checked. */ 26 | onChange?: (event: React.ChangeEvent) => void 27 | /** 28 | * Defines the class of the radio. 29 | */ 30 | className?: string 31 | /** 32 | * Defines the style of the radio. 33 | */ 34 | style?: CSSProperties 35 | } 36 | 37 | const Radio = (props: Props) => { 38 | const { 39 | label, 40 | name, 41 | id, 42 | value, 43 | defaultChecked, 44 | disabled, 45 | inline, 46 | isInvalid, 47 | isValid, 48 | feedback, 49 | onChange, 50 | className, 51 | style, 52 | } = props 53 | 54 | return ( 55 | 69 | 75 | 78 | {label} 79 | 80 | 84 | {feedback} 85 | 86 | 87 | ) 88 | } 89 | 90 | export { Radio } 91 | -------------------------------------------------------------------------------- /src/components/Radio/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './Radio' 2 | -------------------------------------------------------------------------------- /src/components/RichText/RichText.tsx: -------------------------------------------------------------------------------- 1 | import { Editor } from '@tinymce/tinymce-react' 2 | import React from 'react' 3 | 4 | import 'tinymce/tinymce' 5 | 6 | // Basic tinyMCE theme & skins required for editor to display 7 | import 'tinymce/themes/silver/theme.min' 8 | import 'tinymce/skins/ui/oxide/skin.min.css' 9 | import 'tinymce/skins/ui/oxide/content.min.css' 10 | 11 | // Import required plugins 12 | import 'tinymce/plugins/autolink/plugin.min' 13 | import 'tinymce/plugins/lists/plugin.min' 14 | import 'tinymce/plugins/link/plugin.min' 15 | import 'tinymce/plugins/table/plugin.min' 16 | import 'tinymce/plugins/paste/plugin.min' 17 | import 'tinymce/plugins/charmap/plugin.min' 18 | 19 | interface Props { 20 | /** 21 | * id of the rich text editor component 22 | * @default "" 23 | */ 24 | id?: string 25 | /** 26 | * Initial value of the rich text editor 27 | * @default "" 28 | */ 29 | value?: string 30 | /** 31 | * Defines whether the rich text editor should be enabled/disabled (default = false) 32 | * @default false 33 | */ 34 | disabled?: boolean 35 | /** 36 | * Height of the rich text editor 37 | * @default 500 38 | */ 39 | height?: number 40 | /** 41 | * Method run on the editors onEditorChange event. Returns editor content as HTML. 42 | * @default null 43 | */ 44 | onChange?: (event: React.KeyboardEvent) => void 45 | } 46 | 47 | const RichText = (props: Props) => { 48 | const { id, value, disabled, height, onChange } = props 49 | 50 | return ( 51 | onChange && onChange(content)} 71 | /> 72 | ) 73 | } 74 | 75 | export { RichText } 76 | -------------------------------------------------------------------------------- /src/components/RichText/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './RichText' 2 | -------------------------------------------------------------------------------- /src/components/Select/Select.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Typeahead } from 'react-bootstrap-typeahead' 3 | import 'react-bootstrap-typeahead/css/Typeahead.css' 4 | import Form from 'react-bootstrap/Form' 5 | 6 | interface SelectOption { 7 | label: string 8 | value: T 9 | } 10 | 11 | interface Props { 12 | id: string 13 | options: SelectOption[] 14 | defaultSelected?: SelectOption[] 15 | onChange?: (values: T[]) => void 16 | placeholder?: string 17 | multiple?: boolean 18 | disabled?: boolean 19 | isValid?: boolean 20 | isInvalid?: boolean 21 | feedback?: string 22 | } 23 | 24 | function Select(props: Props) { 25 | const { 26 | id, 27 | options, 28 | defaultSelected, 29 | onChange, 30 | placeholder, 31 | multiple, 32 | disabled, 33 | isValid, 34 | isInvalid, 35 | feedback, 36 | } = props 37 | 38 | return ( 39 | <> 40 | > 41 | id={id} 42 | options={options as any} 43 | selected={defaultSelected} 44 | onChange={(selected: SelectOption[]) => { 45 | if (onChange !== undefined) { 46 | onChange(selected.map((option) => option.value)) 47 | } 48 | }} 49 | placeholder={placeholder} 50 | multiple={multiple} 51 | disabled={disabled} 52 | isInvalid={isInvalid} 53 | filterBy={(option: SelectOption, selectProps: any) => { 54 | // per https://github.com/HospitalRun/components/issues/517 55 | // change component default behavior 56 | // multiple - filter-out current selections 57 | const isMatch = option.label.toLowerCase().indexOf(selectProps.text.toLowerCase()) !== -1 58 | if (selectProps.selected.length && selectProps.multiple) { 59 | return selectProps.selected.every( 60 | (selected: any) => selected.label !== option.label && isMatch, 61 | ) 62 | } 63 | // single (custom)- display all options 64 | if (selectProps.selected.length) { 65 | return true 66 | } 67 | // default filter as normal 68 | 69 | return isMatch 70 | }} 71 | /> 72 |
73 | 79 | {feedback} 80 | 81 | 82 | ) 83 | } 84 | 85 | Select.defaultProps = { 86 | defaultSelected: [], 87 | onChange: undefined, 88 | placeholder: '-- Choose --', 89 | multiple: false, 90 | disabled: false, 91 | isInvalid: false, 92 | } 93 | 94 | export { Select } 95 | -------------------------------------------------------------------------------- /src/components/Select/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './Select' 2 | -------------------------------------------------------------------------------- /src/components/Spinner/Spinner.tsx: -------------------------------------------------------------------------------- 1 | /* eslint react/jsx-props-no-spreading: "off" */ 2 | import React from 'react' 3 | import * as Spinners from 'react-spinners' 4 | 5 | import { SpinnerType, SpinnerSizeUnit } from './interfaces' 6 | import * as strings from './strings' 7 | 8 | interface Props { 9 | /** The type of spinner to render */ 10 | type: SpinnerType 11 | /** This prop tells Spinner when to show itself. It should be linked to something dynamic, like a property in the parent component's state. */ 12 | loading: boolean 13 | /** Choose the spinner color. */ 14 | color?: string 15 | /** Adds a margin to the Spinner component. */ 16 | margin?: string 17 | /** 18 | * Declares the size of the spinner. If you choose to declare it, in the following spinners it must be an array of two numbers (for width & height): 19 | * BarLoader, FadeLoader, ScaleLoader 20 | */ 21 | size?: number | [number, number] 22 | /** 23 | * Declares the unit of measure for the previous size parameter. If you choose to declare it, in the following spinners it must be an array of two strings (respectively for width & height): 24 | * BarLoader, FadeLoader, ScaleLoader 25 | */ 26 | sizeUnit?: SpinnerSizeUnit | [SpinnerSizeUnit, SpinnerSizeUnit] 27 | } 28 | 29 | /** 30 | * A customizable spinner component. It's a wrapper component built upon react-spinners. 31 | */ 32 | const Spinner = (props: Props) => { 33 | const { loading, color, margin, size, sizeUnit, type } = props 34 | 35 | const commonStyles = { 36 | loading, 37 | color: color || 'grey', 38 | margin: margin || '2px', 39 | } 40 | 41 | const loaderStyles1 = { 42 | ...commonStyles, 43 | size: size ? (size as number) : 15, 44 | sizeUnit: sizeUnit ? (sizeUnit as SpinnerSizeUnit) : 'px', 45 | } 46 | 47 | const loaderStyles2 = { 48 | ...commonStyles, 49 | width: size ? (size as [number, number])[0] : 5, 50 | height: size ? (size as [number, number])[1] : 15, 51 | widthUnit: sizeUnit ? (sizeUnit as [SpinnerSizeUnit, SpinnerSizeUnit])[0] : 'px', 52 | heightUnit: sizeUnit ? (sizeUnit as [SpinnerSizeUnit, SpinnerSizeUnit])[1] : 'px', 53 | } 54 | 55 | switch (type) { 56 | case 'BarLoader': 57 | return 58 | case 'BeatLoader': 59 | return 60 | case 'BounceLoader': 61 | return 62 | case 'ClimbingBoxLoader': 63 | return 64 | case 'ClipLoader': 65 | return 66 | case 'DotLoader': 67 | return 68 | case 'FadeLoader': 69 | return 70 | case 'PulseLoader': 71 | return 72 | case 'RotateLoader': 73 | return 74 | case 'ScaleLoader': 75 | return 76 | case 'SyncLoader': 77 | return 78 | default: 79 | return
{strings.invalidSpinner}
80 | } 81 | } 82 | 83 | export { Spinner } 84 | -------------------------------------------------------------------------------- /src/components/Spinner/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './Spinner' 2 | -------------------------------------------------------------------------------- /src/components/Spinner/interfaces.ts: -------------------------------------------------------------------------------- 1 | export type SpinnerType = 2 | | 'BarLoader' 3 | | 'BeatLoader' 4 | | 'BounceLoader' 5 | | 'ClimbingBoxLoader' 6 | | 'ClipLoader' 7 | | 'DotLoader' 8 | | 'FadeLoader' 9 | | 'PulseLoader' 10 | | 'RotateLoader' 11 | | 'ScaleLoader' 12 | | 'SyncLoader' 13 | 14 | export type SpinnerSizeUnit = 'px' | '%' | 'em' 15 | -------------------------------------------------------------------------------- /src/components/Spinner/strings.ts: -------------------------------------------------------------------------------- 1 | export const invalidSpinner = 'Invalid spinner' 2 | -------------------------------------------------------------------------------- /src/components/Switch/Switch.tsx: -------------------------------------------------------------------------------- 1 | import React, { CSSProperties } from 'react' 2 | import FormCheck from 'react-bootstrap/FormCheck' 3 | 4 | interface Props { 5 | /** The id for the switch element */ 6 | id: string 7 | /** The label to render next to the switch */ 8 | label: string 9 | /** Determines if the switch should be disabled or not. By default false */ 10 | disabled?: boolean 11 | /** The onChange listener */ 12 | onChange?: (event: React.ChangeEvent) => void 13 | /** 14 | * Defines the class of the switch. 15 | */ 16 | className?: string 17 | /** 18 | * Defines the style of the switch. 19 | */ 20 | style?: CSSProperties 21 | } 22 | 23 | /** 24 | * Switches are used to choose if something is true or false 25 | */ 26 | const Switch = (props: Props) => { 27 | const { id, label, disabled, onChange, className, style } = props 28 | return ( 29 |
30 | 39 |
40 | ) 41 | } 42 | 43 | export { Switch } 44 | -------------------------------------------------------------------------------- /src/components/Switch/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './Switch' 2 | -------------------------------------------------------------------------------- /src/components/Tab/Tab.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import { Button } from '../Button' 4 | import { IconType } from '../Icon/interfaces' 5 | 6 | interface Props { 7 | /** Tab label */ 8 | label: string 9 | /** Whether tab is currently selected */ 10 | active?: boolean 11 | /** Whether the tab is disabled */ 12 | disabled?: boolean 13 | /** Handles the on click event for a tab */ 14 | onClick?: (event: React.MouseEvent) => void 15 | 16 | /** The icon to display */ 17 | icon?: IconType 18 | /** Determines whether or not the icon should display on the left side or right side of the tab. By default is left */ 19 | iconLocation?: 'left' | 'right' 20 | } 21 | 22 | const Tab = (props: Props) => { 23 | const { label, onClick, active, icon, disabled, iconLocation } = props 24 | const className = `nav-link btn-link ${active ? ' active' : ''} ${disabled ? ' disabled' : ''}` 25 | 26 | return ( 27 |
  • 28 | 38 |
  • 39 | ) 40 | } 41 | 42 | Tab.defaultProps = { 43 | iconLocation: 'left', 44 | } 45 | 46 | export { Tab } 47 | -------------------------------------------------------------------------------- /src/components/Tab/TabsHeader.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | interface Props { 4 | /** The children to render */ 5 | children?: React.ReactNode 6 | } 7 | 8 | const TabsHeader = (props: Props) => { 9 | const { children } = props 10 | 11 | return ( 12 |
      13 | {children} 14 |
    15 | ) 16 | } 17 | 18 | export { TabsHeader } 19 | -------------------------------------------------------------------------------- /src/components/Tab/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './Tab' 2 | export * from './TabsHeader' 3 | -------------------------------------------------------------------------------- /src/components/Table/Table.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { ButtonVariant } from 'src/interfaces' 3 | 4 | import { Button } from '../Button' 5 | 6 | interface Props { 7 | tableClassName: string 8 | headerClassName: string 9 | columns: { key: string; label: string; formatter?: (row: T) => React.ReactNode }[] 10 | data: T[] 11 | actionsHeaderText: string 12 | actions?: { label: string; action: (row: T) => void; buttonColor?: ButtonVariant }[] 13 | getID: (row: T) => string 14 | onRowClick?: (row: T) => void 15 | } 16 | 17 | function Table(props: Props) { 18 | const { 19 | tableClassName, 20 | headerClassName, 21 | columns, 22 | data, 23 | actionsHeaderText, 24 | actions, 25 | getID, 26 | onRowClick, 27 | } = props 28 | 29 | const table = ( 30 | 31 | 32 | 33 | {columns.map((column) => ( 34 | 35 | ))} 36 | {actions ? : null} 37 | 38 | 39 | 40 | 41 | {data.map((row: T) => ( 42 | { 45 | if (onRowClick) { 46 | onRowClick(row) 47 | } 48 | }} 49 | > 50 | {columns.map((column) => { 51 | const content = !column.formatter ? row[column.key as keyof T] : column.formatter(row) 52 | return 53 | })} 54 | 55 | {actions ? ( 56 | 71 | ) : null} 72 | 73 | ))} 74 | 75 |
    {column.label}{actionsHeaderText}
    {content} 57 | {actions.map(({ label, action, buttonColor }, i) => ( 58 | 69 | ))} 70 |
    76 | ) 77 | 78 | return table 79 | } 80 | 81 | Table.defaultProps = { 82 | tableClassName: 'table table-hover', 83 | headerClassName: 'thead-light', 84 | actionsHeaderText: 'Actions', 85 | } 86 | 87 | export { Table } 88 | -------------------------------------------------------------------------------- /src/components/Table/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './Table' 2 | -------------------------------------------------------------------------------- /src/components/TextField/TextField.tsx: -------------------------------------------------------------------------------- 1 | import React, { CSSProperties } from 'react' 2 | import Form from 'react-bootstrap/Form' 3 | 4 | import { getControlSize } from '../../helpers/controlSize' 5 | 6 | interface Props { 7 | /** A unique identifier for the componentchrome 8 | * 9 | */ 10 | id?: string 11 | /** Determines whether the TextField should be disabled or not. By default, it is false. */ 12 | disabled?: boolean 13 | /** Determines whether the TextField should be rendered as invalid or not. By default, it is false. */ 14 | isInvalid?: boolean 15 | /** Determines whether the Select should be rendered as valid or not. Default is false. */ 16 | isValid?: boolean 17 | /** The name of the text field */ 18 | name?: string 19 | /** The number of rows to render */ 20 | rows?: number 21 | /** Determines whether to render a small or large TextField. By default, it is undefined. */ 22 | size?: 'small' | 'large' 23 | /** The value of the text field */ 24 | value?: string 25 | /** Defines the default value */ 26 | defaultValue?: string | Array 27 | /** Handles the onChange event for the TextField */ 28 | onChange?: (event: React.ChangeEvent) => void 29 | /** 30 | * Defines the class of the textfield. 31 | */ 32 | className?: string 33 | /** 34 | * Defines the style of the textfield. 35 | */ 36 | style?: CSSProperties 37 | /** Defines the custom feedback of the input. */ 38 | feedback?: string 39 | } 40 | 41 | /** 42 | * A customizable text field component. It's a wrapper component built upon react's form controls. 43 | */ 44 | const TextField = (props: Props) => { 45 | const { 46 | id, 47 | disabled, 48 | isValid, 49 | isInvalid, 50 | feedback, 51 | name, 52 | rows, 53 | size, 54 | value, 55 | onChange, 56 | className, 57 | style, 58 | defaultValue, 59 | } = props 60 | 61 | return ( 62 | 63 | 78 | 82 | {feedback} 83 | 84 | 85 | ) 86 | } 87 | 88 | export { TextField } 89 | -------------------------------------------------------------------------------- /src/components/TextField/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './TextField' 2 | -------------------------------------------------------------------------------- /src/components/TextInput/TextInput.tsx: -------------------------------------------------------------------------------- 1 | import React, { CSSProperties } from 'react' 2 | import Form from 'react-bootstrap/Form' 3 | 4 | interface Props { 5 | /** Defines the type of the input. Defaults to 'text' if not specified. */ 6 | type?: 'text' | 'number' | 'email' | 'password' | 'search' | 'tel' | 'url' 7 | /** Defines the size of the input. Defaults to 'lg' */ 8 | size?: 'sm' | 'lg' 9 | /** The value of the input */ 10 | value?: string 11 | /** Handles the onChange event for the input */ 12 | onChange?: (e: React.ChangeEvent) => void 13 | /** The name of the input */ 14 | name?: string 15 | /** The id value of the input */ 16 | id?: string 17 | /** The placeholder inside of the text input */ 18 | placeholder?: string 19 | /** Defines whether the input should be disabled or not. Defaults to false. */ 20 | disabled?: boolean 21 | /** Defines the custom error message of the input. */ 22 | feedback?: string 23 | /** Defines whether the input should display as invalid. Defaults to false. */ 24 | isInvalid?: boolean 25 | /** Defines whether the input should display as valid. Defaults to false */ 26 | isValid?: boolean 27 | /** Defines the class of the input. */ 28 | className?: string 29 | /** Defines the style of the input. */ 30 | style?: CSSProperties 31 | /** Defines the custom style of the input. */ 32 | inputDefaultStyle?: Record 33 | } 34 | 35 | /** 36 | * A flexible text input as a wrapper around the React Bootstrap Form Control. 37 | */ 38 | 39 | const TextInput = (props: Props) => { 40 | const { 41 | type, 42 | name, 43 | id, 44 | placeholder, 45 | onChange, 46 | disabled, 47 | feedback, 48 | isValid, 49 | isInvalid, 50 | value, 51 | size, 52 | className, 53 | style, 54 | inputDefaultStyle, 55 | } = props 56 | 57 | return ( 58 | 59 | 74 | 80 | {feedback} 81 | 82 | 83 | ) 84 | } 85 | 86 | TextInput.defaultProps = { 87 | type: 'text', 88 | } 89 | 90 | export { TextInput } 91 | -------------------------------------------------------------------------------- /src/components/TextInput/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './TextInput' 2 | -------------------------------------------------------------------------------- /src/components/Toaster/components.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | 3 | import { ToastProps } from './interfaces' 4 | 5 | export const titleWithMessage = (title: ToastProps['title'], message: ToastProps['message']) => ( 6 | <> 7 |
    8 | {title} 9 |
    10 |
    {message}
    11 | 12 | ) 13 | 14 | export const titleWithoutMessage = (title: ToastProps['title']) =>
    {title}
    15 | -------------------------------------------------------------------------------- /src/components/Toaster/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import { ToastContainer, toast, Slide } from 'react-toastify' 3 | 4 | import { titleWithMessage, titleWithoutMessage } from './components' 5 | import { ToastProps, ToasterProps } from './interfaces' 6 | 7 | import 'react-toastify/dist/ReactToastify.min.css' 8 | import './toaster.scss' 9 | 10 | export const Toast: any = ( 11 | type: ToastProps['type'], 12 | title: ToastProps['title'], 13 | message?: ToastProps['message'], 14 | position?: ToastProps['position'], 15 | ) => { 16 | const messageToShow = message ? titleWithMessage(title, message) : titleWithoutMessage(title) 17 | let toastToShow 18 | const actualPosition = position || toast.POSITION.TOP_RIGHT 19 | 20 | switch (type) { 21 | case 'error': 22 | toast.error(messageToShow, { position: actualPosition }) 23 | break 24 | case 'info': 25 | toast.info(messageToShow, { position: actualPosition }) 26 | break 27 | case 'success': 28 | toast.success(messageToShow, { position: actualPosition }) 29 | break 30 | case 'warning': 31 | toast.warn(messageToShow, { position: actualPosition }) 32 | break 33 | default: 34 | toast.error(messageToShow, { position: actualPosition }) 35 | } 36 | 37 | return toastToShow 38 | } 39 | 40 | export const Toaster = (props: ToasterProps) => { 41 | const { autoClose, hideProgressBar, draggable } = props 42 | 43 | return ( 44 | 51 | ) 52 | } 53 | -------------------------------------------------------------------------------- /src/components/Toaster/interfaces.ts: -------------------------------------------------------------------------------- 1 | import { PositionOptions } from 'react-toastify' 2 | 3 | export interface ToasterProps { 4 | autoClose?: number 5 | hideProgressBar?: boolean 6 | draggable?: boolean 7 | } 8 | 9 | export interface ToastProps { 10 | type: 'success' | 'info' | 'warning' | 'error' 11 | title: string 12 | message?: string 13 | position?: PositionOptions 14 | } 15 | -------------------------------------------------------------------------------- /src/components/Toaster/toaster.scss: -------------------------------------------------------------------------------- 1 | @import "../../../scss/_variables"; 2 | 3 | div.Toastify__toast.Toastify__toast--error { 4 | background: map-get($theme-colors, error); 5 | } 6 | 7 | div.Toastify__toast.Toastify__toast--info { 8 | background: map-get($theme-colors, info); 9 | } 10 | 11 | div.Toastify__toast.Toastify__toast--success { 12 | background: map-get($theme-colors, success); 13 | } 14 | 15 | div.Toastify__toast.Toastify__toast--warning { 16 | background: map-get($theme-colors, warning); 17 | } 18 | -------------------------------------------------------------------------------- /src/components/Typeahead/Typeahead.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { AsyncTypeahead } from 'react-bootstrap-typeahead' 3 | 4 | interface Props { 5 | /** The id value of the input */ 6 | id: string 7 | /** Specify which option key to use for display or a render function. 8 | By default, the selector will use the 'label' key. */ 9 | searchAccessor: string 10 | /** Provides a hook for customized rendering of menu item contents. */ 11 | renderMenuItemChildren: (option: any) => React.ReactNode 12 | /** Invoked whenever items are added or removed. Receives an array of the selected options. */ 13 | onChange: (selected: any) => void 14 | /** Callback to perform when the search is executed. */ 15 | onSearch: (query: string) => Promise 16 | /** Number of input characters that must be entered before showing results. */ 17 | minLength?: number 18 | /** The placeholder inside of the text input */ 19 | placeholder?: string 20 | /** The value of the input */ 21 | value?: any 22 | /** Defines whether the input should be disabled or not. Defaults to false. */ 23 | disabled?: boolean 24 | /** Defines whether the input should display as invalid. Defaults to false. */ 25 | isValid?: boolean 26 | /** Defines the custom error message of the input. */ 27 | isInvalid?: boolean 28 | /** Defines whether the input should display as valid. Defaults to false */ 29 | feedback?: string 30 | } 31 | 32 | const Typeahead = (props: Props) => { 33 | const [options, setOptions] = useState([]) 34 | const [isLoading, setIsLoading] = useState(false) 35 | const { 36 | id, 37 | searchAccessor, 38 | placeholder, 39 | onSearch, 40 | onChange, 41 | renderMenuItemChildren, 42 | minLength, 43 | value, 44 | disabled, 45 | isValid, 46 | isInvalid, 47 | feedback, 48 | } = props 49 | 50 | const search = async (query: string) => { 51 | setIsLoading(true) 52 | const results = await onSearch(query) 53 | setOptions(results) 54 | setIsLoading(false) 55 | } 56 | 57 | const selectedValues = [] 58 | if (value) { 59 | selectedValues.push(value) 60 | } 61 | 62 | return ( 63 | <> 64 | 79 | {feedback !== undefined && ( 80 |
    85 | {feedback} 86 |
    87 | )} 88 | 89 | ) 90 | } 91 | 92 | Typeahead.defaultProps = { 93 | minLength: 3, 94 | } 95 | 96 | export { Typeahead } 97 | -------------------------------------------------------------------------------- /src/components/Typeahead/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './Typeahead' 2 | -------------------------------------------------------------------------------- /src/components/Typography/Typography.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | interface Props { 4 | /** 5 | * Defines the HTML variant of typography 6 | * @default 'p' 7 | */ 8 | variant?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' 9 | /** 10 | * Defines the CSS styling of typography 11 | * @default 'p' 12 | */ 13 | styleAs?: 14 | | 'h1' 15 | | 'h2' 16 | | 'h3' 17 | | 'h4' 18 | | 'h5' 19 | | 'h6' 20 | | 'display-1' 21 | | 'display-2' 22 | | 'display-3' 23 | | 'display-4' 24 | /** 25 | * Children to be rendered between Typography tags. 26 | */ 27 | children?: React.ReactNode 28 | } 29 | 30 | const Typography = (props: Props) => { 31 | const { variant, styleAs, children } = props 32 | 33 | return ( 34 | <> 35 | {variant === 'h1' ? ( 36 |

    {children}

    37 | ) : variant === 'h2' ? ( 38 |

    {children}

    39 | ) : variant === 'h3' ? ( 40 |

    {children}

    41 | ) : variant === 'h4' ? ( 42 |

    {children}

    43 | ) : variant === 'h5' ? ( 44 |
    {children}
    45 | ) : variant === 'h6' ? ( 46 |
    {children}
    47 | ) : ( 48 |

    {children}

    49 | )} 50 | 51 | ) 52 | } 53 | 54 | export { Typography } 55 | -------------------------------------------------------------------------------- /src/components/Typography/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './Typography' 2 | -------------------------------------------------------------------------------- /src/components/VideoPlayer/VideoPlayer.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Player } from 'video-react' 3 | import 'video-react/dist/video-react.css' 4 | 5 | interface Props { 6 | /** 7 | * The URL of the video to embed. This is optional. 8 | * You may instead use the element within to specify the video to embed. 9 | */ 10 | src?: string 11 | /** 12 | * A URL indicating a poster frame to show until the user plays or seeks. 13 | * If this attribute isn't specified, nothing is displayed until the first frame is available; then the first frame is shown as the poster frame. 14 | */ 15 | poster?: string 16 | /** 17 | * This enumerated attribute is intended to provide a hint to the browser about what the author thinks will lead to the best user experience. 18 | * It may have one of the following values: 19 | * - auto: indicates that the whole video file could be downloaded, even if the user is not expected to use it. 20 | * - empty string: synonym of the auto value. 21 | * - none: indicates that the video should not be preloaded. 22 | * - metadata: indicates that only video metadata (e.g. length) is fetched. 23 | */ 24 | preload?: 'auto' | '' | 'none' | 'metadata' 25 | /** 26 | * In `fluid` mode, player is 100% wide all the time, the height will be calculated by the video's ratio. 27 | * @default true 28 | */ 29 | fluid?: boolean 30 | /** 31 | * The width of element, could be an number or percent or auto. 32 | * (This attribute is effective only if you set `fluid` as false) 33 | */ 34 | width?: number 35 | /** 36 | * The height of element, could be an number or percent or auto. 37 | * (This attribute is effective only if you set `fluid` as false) 38 | */ 39 | height?: number 40 | /** 41 | * Indicates the default setting of the audio contained in the video. If set, the audio will be initially silenced. 42 | * Its default value is false, meaning that the audio will be played when the video is played. 43 | * @default false 44 | */ 45 | muted?: boolean 46 | /** 47 | * [iOS only] Determines whether HTML5 videos play inline or use the native full-screen controller. 48 | * @default false 49 | */ 50 | playsInline?: boolean 51 | /** 52 | * The aspect ratio is the width of the video divided by its height. 53 | * Posible values: "auto", "16:9", "4:3", etc. 54 | * @default "auto" 55 | */ 56 | aspectRatio?: string 57 | /** 58 | * If specified, the video automatically begins to play back as soon as it can do so without stopping to finish loading the data. 59 | * @default false 60 | */ 61 | autoPlay?: boolean 62 | /** 63 | * Seek the Video at A Specific Time On Load 64 | */ 65 | startTime?: number 66 | children?: React.ReactNode 67 | } 68 | 69 | const VideoPlayer = (props: Props) => { 70 | const { 71 | src, 72 | poster, 73 | preload, 74 | fluid, 75 | width, 76 | height, 77 | muted, 78 | playsInline, 79 | aspectRatio, 80 | autoPlay, 81 | startTime, 82 | children, 83 | } = props 84 | 85 | return ( 86 | 99 | {children} 100 | 101 | ) 102 | } 103 | 104 | export { VideoPlayer } 105 | -------------------------------------------------------------------------------- /src/components/VideoPlayer/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './VideoPlayer' 2 | -------------------------------------------------------------------------------- /src/helpers/controlSize.ts: -------------------------------------------------------------------------------- 1 | interface KeyValuePair { 2 | key: string 3 | value: any 4 | } 5 | 6 | const sizes: KeyValuePair[] = [ 7 | { key: 'small', value: 'sm' }, 8 | { key: 'large', value: 'lg' }, 9 | ] 10 | 11 | export function getControlSize(size: string | undefined): 'sm' | 'lg' | undefined { 12 | const controlSize = sizes.find((s) => s.key === size) 13 | return controlSize ? controlSize.value : undefined 14 | } 15 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import { library } from '@fortawesome/fontawesome-svg-core' 2 | import { far } from '@fortawesome/free-regular-svg-icons' 3 | import { fas } from '@fortawesome/free-solid-svg-icons' 4 | 5 | import '../scss/main.scss' 6 | 7 | library.add(fas) 8 | library.add(far) 9 | export * from './components/Toaster' 10 | export * from './components/Spinner' 11 | export * from './components/Graph' 12 | export * from './components/Button' 13 | export * from './components/Dropdown' 14 | export * from './components/Icon' 15 | export * from './components/Image' 16 | export * from './components/Badge' 17 | export * from './components/Panel' 18 | export * from './components/Pill' 19 | export * from './components/Checkbox' 20 | export * from './components/Navbar' 21 | export * from './components/TextField' 22 | export * from './components/Switch' 23 | export * from './components/Radio' 24 | export * from './components/Alert' 25 | export * from './components/Modal' 26 | export * from './components/TextInput' 27 | export * from './components/Select' 28 | export * from './components/RichText' 29 | export * from './components/List' 30 | export * from './components/DateTimePicker' 31 | export * from './components/VideoPlayer' 32 | export * from './components/Layout' 33 | export * from './components/Label' 34 | export * from './components/Breadcrumb' 35 | export * from './components/Tab' 36 | export * from './components/Typeahead' 37 | export * from './components/Typography' 38 | export * from './components/Calendar' 39 | export * from './components/Table' 40 | export * from './components/Callout' 41 | -------------------------------------------------------------------------------- /src/interfaces/index.tsx: -------------------------------------------------------------------------------- 1 | export type ColorVariant = 2 | | 'primary' 3 | | 'secondary' 4 | | 'success' 5 | | 'danger' 6 | | 'warning' 7 | | 'dark' 8 | | 'light' 9 | | 'info' 10 | 11 | export type ButtonVariant = 12 | | 'light' 13 | | 'dark' 14 | | 'primary' 15 | | 'secondary' 16 | | 'success' 17 | | 'danger' 18 | | 'warning' 19 | | 'info' 20 | | 'link' 21 | | 'outline-primary' 22 | | 'outline-secondary' 23 | | 'outline-success' 24 | | 'outline-danger' 25 | | 'outline-warning' 26 | | 'outline-info' 27 | | 'outline-dark' 28 | | 'outline-light' 29 | -------------------------------------------------------------------------------- /src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | // / 2 | -------------------------------------------------------------------------------- /stories/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | rules: { 3 | 'import/no-extraneous-dependencies': 0, 4 | 'no-alert': 'off' 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /stories/CustomClasses.css: -------------------------------------------------------------------------------- 1 | .customClass { 2 | border: 5px solid green; 3 | background-color: rgb(46, 52, 70); 4 | font-style: italic; 5 | border-radius: 20px; 6 | padding: 5px; 7 | color: rgb(207, 207, 207); 8 | } 9 | .customClass input { 10 | margin-left: -0.8rem; 11 | } 12 | .customClass label { 13 | margin-left: 10px; 14 | } 15 | .customClass2 { 16 | background-color: violet; 17 | color: black; 18 | border: 2px solid purple; 19 | font-weight: bold; 20 | text-transform: uppercase; 21 | } 22 | 23 | .customClass:hover { 24 | background-color: cornflowerblue; 25 | } 26 | -------------------------------------------------------------------------------- /stories/alerts.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story, Meta } from '@storybook/react/types-6-0' 2 | import React from 'react' 3 | 4 | import { Alert, AlertProps } from '../src' 5 | 6 | export default { 7 | title: 'Alert', 8 | component: Alert, 9 | argTypes: { 10 | color: { 11 | control: { 12 | type: 'select', 13 | options: ['primary', 'secondary', 'success', 'warning', 'danger', 'info', 'light', 'dark'], 14 | }, 15 | }, 16 | dismissible: { 17 | type: 'boolean', 18 | }, 19 | }, 20 | decorators: [], 21 | } as Meta 22 | // your templates and stories 23 | 24 | // We create a “template” of how args map to rendering 25 | const Template: Story = (args) => 26 | 27 | // main story that's editable and has the docs for the props 28 | export const Main = Template.bind({}) 29 | Main.args = { 30 | color: 'primary', 31 | title: 'This is the title of the alert', 32 | message: 'And this is the message of the alert', 33 | dismissible: true, 34 | closeLabel: 'Close', 35 | } 36 | 37 | // Rest of the stories 38 | export const Primary = Template.bind({}) 39 | Primary.args = { 40 | color: 'primary', 41 | title: 'This is the title of the alert', 42 | message: 'And this is the message of the alert', 43 | } 44 | 45 | export const Secondary = Template.bind({}) 46 | Secondary.args = { 47 | color: 'secondary', 48 | title: 'This is an alert with only the title', 49 | } 50 | 51 | export const Success = Template.bind({}) 52 | Success.args = { 53 | color: 'success', 54 | title: 'This is an alert with only the title', 55 | } 56 | 57 | export const Danger = Template.bind({}) 58 | Danger.args = { 59 | color: 'danger', 60 | title: 'This alert is dismissible', 61 | dismissible: true, 62 | } 63 | 64 | export const Warning = Template.bind({}) 65 | Warning.args = { 66 | color: 'warning', 67 | title: 'This alert is dismissible with a custom label', 68 | dismissible: true, 69 | closeLabel: 'Close me', 70 | } 71 | 72 | export const Info = Template.bind({}) 73 | Info.args = { 74 | color: 'info', 75 | title: 'This is an info alert', 76 | } 77 | 78 | export const Light = Template.bind({}) 79 | Light.args = { 80 | color: 'light', 81 | title: 'This is an light alert', 82 | } 83 | 84 | export const Dark = Template.bind({}) 85 | Dark.args = { 86 | color: 'dark', 87 | title: 'This is an dark alert', 88 | message: With a strong message, 89 | } 90 | 91 | export const CustomClass = Template.bind({}) 92 | CustomClass.args = { 93 | title: 'This is an alert with a custom class', 94 | className: 'customClass', 95 | message: 'And it has a button with a custom class', 96 | btnClassName: 'customClass2', 97 | dismissible: true, 98 | } 99 | 100 | export const CustomStyle = Template.bind({}) 101 | CustomStyle.args = { 102 | title: 'This is an alert with a custom style', 103 | style: { height: '50%', width: '50%', border: '2px solid red' }, 104 | message: 'And it has a button with a custom style', 105 | btnStyle: { background: 'red', color: 'white' }, 106 | dismissible: true, 107 | } 108 | -------------------------------------------------------------------------------- /stories/badges.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story, Meta } from '@storybook/react/types-6-0' 2 | import React from 'react' 3 | 4 | import { Badge, BadgeProps } from '../src' 5 | 6 | export default { 7 | title: 'Badge', 8 | component: Badge, 9 | argTypes: { 10 | color: { 11 | control: { 12 | type: 'select', 13 | options: ['primary', 'secondary', 'success', 'warning', 'danger', 'info', 'light', 'dark'], 14 | }, 15 | }, 16 | children: { 17 | control: 'text', 18 | }, 19 | }, 20 | decorators: [], 21 | } as Meta 22 | // your templates and stories 23 | 24 | // We create a “template” of how args map to rendering 25 | const Template: Story = (args) => 26 | 27 | // main story that's editable and has the docs for the props 28 | export const Main = Template.bind({}) 29 | Main.args = { 30 | color: 'primary', 31 | children: 'main example', 32 | } 33 | 34 | // Rest of the stories 35 | export const Primary = Template.bind({}) 36 | Primary.args = { 37 | color: 'primary', 38 | children: 'Primary', 39 | } 40 | 41 | export const Secondary = Template.bind({}) 42 | Secondary.args = { 43 | color: 'secondary', 44 | children: 'Secondary', 45 | } 46 | 47 | export const Success = Template.bind({}) 48 | Success.args = { 49 | color: 'success', 50 | children: 'Success', 51 | } 52 | 53 | export const Warning = Template.bind({}) 54 | Warning.args = { 55 | color: 'warning', 56 | children: 'Warning', 57 | } 58 | 59 | export const Danger = Template.bind({}) 60 | Danger.args = { 61 | color: 'danger', 62 | children: 'Danger', 63 | } 64 | 65 | export const Info = Template.bind({}) 66 | Info.args = { 67 | color: 'info', 68 | children: 'Info', 69 | } 70 | export const Light = Template.bind({}) 71 | Light.args = { 72 | color: 'light', 73 | children: 'Light', 74 | } 75 | 76 | export const Dark = Template.bind({}) 77 | Dark.args = { 78 | color: 'dark', 79 | children: 'Dark', 80 | } 81 | 82 | export const CustomStyle = Template.bind({}) 83 | CustomStyle.args = { 84 | style: { padding: 10 }, 85 | children: 'Custom Style', 86 | } 87 | 88 | export const CustomClass = Template.bind({}) 89 | CustomClass.args = { 90 | className: 'customClass', 91 | children: 'Custom Class', 92 | } 93 | -------------------------------------------------------------------------------- /stories/breadcrumbs.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story, Meta } from '@storybook/react/types-6-0' 2 | import React from 'react' 3 | 4 | import { Breadcrumb, BreadcrumbItem, BreadcrumbProps } from '../src' 5 | 6 | export default { 7 | title: 'Breadcrumb', 8 | component: Breadcrumb, 9 | argTypes: { 10 | children: { 11 | control: 'text', 12 | }, 13 | }, 14 | decorators: [], 15 | } as Meta 16 | // your templates and stories 17 | 18 | /** 19 | * it displays a simple breadcrum component 20 | * with two items and the first one is active 21 | */ 22 | const MultiItem: Story = (args) => ( 23 | 24 | Item 1 25 | Item 2 26 | 27 | ) 28 | export const Main = MultiItem.bind({}) 29 | 30 | /** 31 | * simple breadcrumb story with a single item active 32 | */ 33 | const SingleActive: Story = (args) => ( 34 | 35 | Item 1 36 | 37 | ) 38 | export const SingleActiveStory = SingleActive.bind({}) 39 | 40 | /** 41 | * story with the custom link on the single active item 42 | */ 43 | const SingleActiveCustomLink: Story = (args) => ( 44 | 45 | 46 | Custom link item 47 | 48 | 49 | ) 50 | export const SingleActiveCustomLinkSrory = SingleActiveCustomLink.bind({}) 51 | 52 | /** 53 | * story with the custom style on both the items and the parent 54 | */ 55 | const SingleActiveCustomStyle: Story = (args) => ( 56 | 57 | 58 | Item 1 59 | 60 | Item 2 61 | 62 | ) 63 | export const SingleActiveCustomStyleStory = SingleActiveCustomStyle.bind({}) 64 | SingleActiveCustomStyleStory.args = { 65 | style: { padding: 10, background: 'cyan' }, 66 | } 67 | 68 | /** 69 | * story with the custom style on the single item and the parent 70 | */ 71 | const SingleActiveCustomClass: Story = (args) => ( 72 | 73 | 74 | Custom Class 75 | 76 | 77 | ) 78 | export const SingleActiveCustomClassStory = SingleActiveCustomClass.bind({}) 79 | SingleActiveCustomClassStory.args = { 80 | className: 'customClass', 81 | } 82 | -------------------------------------------------------------------------------- /stories/calendar.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story, Meta } from '@storybook/react/types-6-0' 2 | import moment from 'moment' 3 | import React from 'react' 4 | 5 | import { Calendar, CalendarProps, Toast, Toaster } from '../src' 6 | 7 | export default { 8 | title: 'Calendar', 9 | component: Calendar, 10 | argTypes: { 11 | children: { 12 | control: 'text', 13 | }, 14 | }, 15 | decorators: [], 16 | } as Meta 17 | 18 | const start = moment() 19 | const hours = 1 20 | const end = moment().add(hours, 'hours') 21 | 22 | const Template: Story = (args) => ( 23 |
    24 | 25 | 26 |
    27 | ) 28 | 29 | // main story that's editable and has the docs for the props 30 | export const Main = Template.bind({}) 31 | Main.args = { 32 | onDateClick: (date, allDay) => { 33 | console.log('from story') 34 | Toast('success', 'Date Click', `${date.toISOString()} all day is ${allDay}`) 35 | }, 36 | onEventClick: (event) => { 37 | Toast('success', 'Event Click', event.title) 38 | }, 39 | onDateRangeSelected: (startDate: Date, endDate: Date) => { 40 | Toast('success', 'Range Selected', `${startDate.toISOString()} to ${endDate.toISOString()}`) 41 | }, 42 | onPrevClick: () => { 43 | Toast('success', 'previous clicked') 44 | }, 45 | onNextClick: () => { 46 | Toast('success', 'next clicked') 47 | }, 48 | onTodayClick: () => { 49 | console.log('on today click') 50 | Toast('success', 'today clicked') 51 | }, 52 | events: [ 53 | { 54 | start: start.toDate(), 55 | end: end.toDate(), 56 | title: 'Some Title', 57 | id: 'Some Id', 58 | allDay: false, 59 | }, 60 | ], 61 | } 62 | -------------------------------------------------------------------------------- /stories/callout.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Story, Meta } from '@storybook/react/types-6-0' 2 | import React from 'react' 3 | 4 | import { Callout, CalloutProps } from '../src' 5 | 6 | export default { 7 | title: 'Callout', 8 | component: Callout, 9 | argTypes: { 10 | color: { 11 | control: { 12 | type: 'select', 13 | options: ['primary', 'secondary', 'success', 'warning', 'danger', 'info', 'light', 'dark'], 14 | }, 15 | }, 16 | children: { 17 | control: 'text', 18 | }, 19 | }, 20 | decorators: [], 21 | } as Meta 22 | // your templates and stories 23 | 24 | // We create a “template” of how args map to rendering 25 | const Template: Story = (args) => 26 | 27 | // main story that's editable and has the docs for the props 28 | export const Main = Template.bind({}) 29 | Main.args = { 30 | title: 'Main Story Title', 31 | color: 'primary', 32 | children:
    Body of the Callout
    , 33 | } 34 | 35 | // Rest of the stories 36 | export const Primary = Template.bind({}) 37 | Primary.args = { 38 | title: 'Primary Callout', 39 | color: 'primary', 40 | children:
    Body of the Callout
    , 41 | } 42 | 43 | export const Secondary = Template.bind({}) 44 | Secondary.args = { 45 | title: 'Secondary Callout', 46 | color: 'secondary', 47 | children:
    Body of the Callout
    , 48 | } 49 | 50 | export const Success = Template.bind({}) 51 | Success.args = { 52 | title: 'Secondary Callout', 53 | color: 'success', 54 | children:
    Body of the Success
    , 55 | } 56 | 57 | export const Info = Template.bind({}) 58 | Info.args = { 59 | title: 'Info Callout', 60 | color: 'info', 61 | children:
    Body of the Callout
    , 62 | } 63 | export const Warning = Template.bind({}) 64 | Warning.args = { 65 | title: 'Warning Callout', 66 | color: 'warning', 67 | children:
    Body of the Callout
    , 68 | } 69 | 70 | export const Danger = Template.bind({}) 71 | Danger.args = { 72 | title: 'Danger Callout', 73 | color: 'danger', 74 | children:
    Body of the Callout
    , 75 | } 76 | 77 | export const Dark = Template.bind({}) 78 | Dark.args = { 79 | title: 'Dark Callout', 80 | color: 'dark', 81 | children:
    Body of the Callout
    , 82 | } 83 | 84 | export const Light = Template.bind({}) 85 | Light.args = { 86 | title: 'Light Callout', 87 | color: 'light', 88 | children:
    Body of the Callout
    , 89 | } 90 | 91 | export const NoTitle = Template.bind({}) 92 | NoTitle.args = { 93 | children:
    Body of the Callout without title
    , 94 | } 95 | 96 | export const NoBody = Template.bind({}) 97 | NoBody.args = { 98 | title: 'Callout without a body', 99 | } 100 | 101 | export const CustomClass = Template.bind({}) 102 | CustomClass.args = { 103 | title: 'Custom Class', 104 | 105 | className: 'customClass', 106 | children:
    Callout with a custom class
    , 107 | } 108 | 109 | export const CustomStyle = Template.bind({}) 110 | CustomStyle.args = { 111 | title: 'Custom Style', 112 | style: { border: '2px solid red' }, 113 | children:
    Callout with custom styles
    , 114 | } 115 | -------------------------------------------------------------------------------- /stories/checkbox.stories.tsx: -------------------------------------------------------------------------------- 1 | import { storiesOf } from '@storybook/react' 2 | import React from 'react' 3 | 4 | import { Checkbox } from '../src' 5 | 6 | storiesOf('Checkbox', module) 7 | .addParameters({ 8 | info: { 9 | inline: true, 10 | }, 11 | }) 12 | .addDecorator((storyFn) =>
    {storyFn()}
    ) 13 | .add('Checkbox', () => ( 14 |
    15 |

    Vertical

    16 | 17 | 18 | 19 |
    20 |

    Horizontal

    21 | 22 | 23 | 24 |

    Custom Style

    25 | 32 |

    Custom class

    33 | 40 |
    41 | )) 42 | -------------------------------------------------------------------------------- /stories/dropdown.stories.tsx: -------------------------------------------------------------------------------- 1 | import { storiesOf } from '@storybook/react' 2 | import React from 'react' 3 | import ButtonToolbar from 'react-bootstrap/ButtonToolbar' 4 | 5 | import { Dropdown } from '../src' 6 | 7 | storiesOf('Dropdown', module) 8 | .addParameters({ 9 | info: { 10 | inline: true, 11 | }, 12 | }) 13 | .addDecorator((storyFn) => ( 14 |
    {storyFn()}
    15 | )) 16 | .add('Dropdown', () => ( 17 |
    18 | 19 | {} }, 23 | { text: 'Link 2', onClick: () => {} }, 24 | { text: 'Link 3', onClick: () => {} }, 25 | ]} 26 | /> 27 | {} }, 32 | { text: 'Link 2', onClick: () => {} }, 33 | { text: 'Link 3', onClick: () => {} }, 34 | ]} 35 | variant="primary" 36 | style={{ marginLeft: '20px', marginRight: '20px' }} 37 | /> 38 | {} }, 44 | { text: 'Link 2', onClick: () => {} }, 45 | ]} 46 | /> 47 | {} }, 53 | { text: 'true', onClick: () => {} }, 54 | ]} 55 | style={{ marginLeft: '20px' }} 56 | /> 57 | 58 |
    Utilities
    59 |
    60 | 61 | {} }, 66 | { text: 'Link 2', onClick: () => {}, style: { fontSize: '20px', color: 'green' } }, 67 | { text: 'Link 3', onClick: () => {}, style: { fontSize: '30px' } }, 68 | ]} 69 | variant="primary" 70 | style={{ marginLeft: '20px', marginRight: '20px' }} 71 | /> 72 | {} }, 76 | { text: 'True', onClick: () => {} }, 77 | ]} 78 | alignRight 79 | /> 80 | {} }, 86 | { text: 'up', onClick: () => {} }, 87 | { text: 'down', onClick: () => {} }, 88 | { text: 'right', onClick: () => {} }, 89 | { text: 'left', onClick: () => {} }, 90 | ]} 91 | style={{ marginLeft: '20px' }} 92 | /> 93 | {} }, 99 | { text: 'true', onClick: () => {} }, 100 | ]} 101 | style={{ marginLeft: '20px' }} 102 | /> 103 | 104 |
    105 |
    106 | )) 107 | -------------------------------------------------------------------------------- /stories/graphs.stories.tsx: -------------------------------------------------------------------------------- 1 | import { storiesOf } from '@storybook/react' 2 | import React from 'react' 3 | 4 | import { LineGraph, PieGraph, BarGraph } from '../src' 5 | 6 | storiesOf('Graphs', module) 7 | .addParameters({ 8 | info: { 9 | inline: true, 10 | }, 11 | }) 12 | .add('Bar Graph', () => ( 13 | 29 | )) 30 | .add('Bar Graph - Horizontal', () => ( 31 | 48 | )) 49 | .add('Line Graph', () => ( 50 | 67 | )) 68 | .add('Pie Graph', () => ( 69 | 83 | )) 84 | .add('Pie Graph - Doughnut', () => ( 85 | 100 | )) 101 | -------------------------------------------------------------------------------- /stories/icons.stories.tsx: -------------------------------------------------------------------------------- 1 | import { storiesOf } from '@storybook/react' 2 | import React from 'react' 3 | 4 | import { Icon } from '../src' 5 | 6 | storiesOf('Icons', module) 7 | .addParameters({ 8 | info: { 9 | inline: true, 10 | }, 11 | }) 12 | .addDecorator((storyFn) =>
    {storyFn()}
    ) 13 | .add('Icon Set', () => ( 14 |
    15 | Add: 16 | 17 |
    18 | Admin: 19 | 20 |
    21 | Apointment: 22 | 23 |
    24 | Add Apointment: 25 | 26 |
    27 | Remove Apointment: 28 | 29 |
    30 | Billing: 31 | 32 |
    33 | Calendar: 34 | 35 |
    36 | Dashboard: 37 | 38 |
    39 | Down Arrow: 40 | 41 |
    42 | Edit: 43 | 44 |
    45 | Image: 46 | 47 |
    48 | Incident: 49 | 50 |
    51 | Inventory: 52 | 53 |
    54 | Lab: 55 | 56 |
    57 | Left Arrow: 58 | 59 |
    60 | Log Out: 61 | 62 |
    63 | Medication: 64 | 65 |
    66 | Menu: 67 | 68 |
    69 | Patient: 70 | 71 |
    72 | Patient (Outline version): 73 | 74 |
    75 | Add Patient: 76 | 77 |
    78 | Remove Patient: 79 | 80 |
    81 | Patients: 82 | 83 |
    84 | Remove: 85 | 86 |
    87 | Right Arrow: 88 | 89 |
    90 | Save: 91 | 92 |
    93 | Setting: 94 | 95 |
    96 | Up Arrow: 97 | 98 |
    99 |
    100 | )) 101 | .add('Icon with custom class & style', () => ( 102 |
    103 | Custom class: 104 | 105 |
    106 | Custom style: 107 | 108 |
    109 | )) 110 | -------------------------------------------------------------------------------- /stories/image.stories.tsx: -------------------------------------------------------------------------------- 1 | import { storiesOf } from '@storybook/react' 2 | import React from 'react' 3 | 4 | import { Image } from '../src' 5 | 6 | storiesOf('Image', module) 7 | .addParameters({ 8 | info: { 9 | inline: true, 10 | }, 11 | }) 12 | .addDecorator((storyFn) => ( 13 |
    {storyFn()}
    14 | )) 15 | .add('Image', () => ( 16 |
    17 | 18 |
    19 | )) 20 | .add('Circular Image', () => ( 21 |
    22 | 23 |
    24 | )) 25 | .add('Rounded Image', () => ( 26 |
    27 | 28 |
    29 | )) 30 | .add('Fluid Image', () => ( 31 |
    32 | 33 |
    34 | )) 35 | .add('Image with standard img attributes', () => ( 36 |
    37 | Test alt 38 |
    39 | )) 40 | -------------------------------------------------------------------------------- /stories/label.stories.tsx: -------------------------------------------------------------------------------- 1 | import { storiesOf } from '@storybook/react' 2 | import React from 'react' 3 | 4 | import { Label, TextInput } from '../src' 5 | 6 | storiesOf('Label', module) 7 | .addParameters({ 8 | info: { 9 | inline: true, 10 | }, 11 | }) 12 | .addDecorator((storyFn) => ( 13 |
    22 | {storyFn()} 23 |
    24 | )) 25 | .add('Label', () => ( 26 | <> 27 |