Storybook Integration for Component testing. (#142)
Co-authored-by: Serinus1 <junkmayle670@yahoo.com>
This commit is contained in:
parent
2e53ce79c7
commit
2eced4fe91
|
@ -25,6 +25,8 @@ jobs:
|
|||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
- name: "Populate frontend version information"
|
||||
run: "cd frontend && ./git.version.sh"
|
||||
- name: Set up depot.dev multi-arch runner
|
||||
uses: depot/setup-action@v1
|
||||
# Login against a Docker registry except on PR
|
||||
|
|
|
@ -60,3 +60,4 @@ test.go
|
|||
/.couchdb
|
||||
|
||||
config.dev.yaml
|
||||
.cache/
|
||||
|
|
|
@ -227,3 +227,9 @@ curl -X POST http://localhost:9090/api/auth/signin -H 'Content-Type: application
|
|||
curl -H "Authorization: Bearer ${JWT_TOKEN_HERE}" http://localhost:5984/_session
|
||||
|
||||
```
|
||||
|
||||
|
||||
# Run Component Storybook
|
||||
```bash
|
||||
ng run fastenhealth:storybook
|
||||
```
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
# Fasten - On Premise/Self-Hosted
|
||||
|
||||
[![CI](https://github.com/fastenhealth/fasten-onprem/actions/workflows/ci.yaml/badge.svg)](https://github.com/fastenhealth/fasten-onprem/actions/workflows/ci.yaml)
|
||||
[![codecov](https://codecov.io/gh/fastenhealth/fasten-onprem/branch/main/graph/badge.svg?token=6O0ZUABEHT&style=flat-square)](https://codecov.io/gh/fastenhealth/fasten-onprem)
|
||||
[![GitHub license](https://img.shields.io/github/license/fastenhealth/fasten-onprem?style=flat-square)](https://github.com/fastenhealth/fasten-onprem/blob/main/LICENSE.md)
|
||||
[![GitHub release (latest by date)](https://img.shields.io/github/v/release/fastenhealth/fasten-onprem?style=flat-square)](https://github.com/fastenhealth/fasten-onprem/releases/latest)
|
||||
[![Discord Join](https://img.shields.io/badge/discord-join-blueviolet?style=flat-square&logo=discord)](https://discord.gg/Bykz6BAN8p)
|
||||
|
|
|
@ -301,10 +301,10 @@ func getSourcesAndSinksForGraphType(graphType pkg.ResourceGraphType) ([][]string
|
|||
case pkg.ResourceGraphTypeMedicalHistory:
|
||||
sources = [][]string{
|
||||
{"condition", "composition"},
|
||||
{"encounter"},
|
||||
{"encounter", "explanationofbenefit"},
|
||||
}
|
||||
sinks = [][]string{
|
||||
{"location", "device", "organization", "practitioner", "medication", "patient"}, //resources that are shared across multiple conditions
|
||||
{"location", "device", "organization", "practitioner", "medication", "patient", "coverage"}, //resources that are shared across multiple conditions
|
||||
{"binary"},
|
||||
}
|
||||
sourceFlattenRelated = map[string]bool{
|
||||
|
|
|
@ -39,7 +39,7 @@ func FindCodeSystem(codeSystem string) (string, error) {
|
|||
if codeSystemId, ok := codeSystemIds[codeSystem]; ok {
|
||||
return codeSystemId, nil
|
||||
} else {
|
||||
return "", fmt.Errorf("Code System not found")
|
||||
return "", fmt.Errorf("Code System not found: %s", codeSystem)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -80,7 +80,7 @@ func (ae *AppEngine) Setup(logger *logrus.Entry) *gin.Engine {
|
|||
ae.Logger.Warningln("***UNSAFE***")
|
||||
unsafe := api.Group("/unsafe")
|
||||
{
|
||||
//http://localhost:9090/api/raw/test@test.com/436d7277-ad56-41ce-9823-44e353d1b3f6/Patient/smart-1288992
|
||||
//http://localhost:9090/api/unsafe/testuser1/3508f8cf-6eb9-4e4b-8174-dd69a493a2b4/Patient/smart-1288992
|
||||
unsafe.GET("/:username/:sourceId/*path", handler.UnsafeRequestSource)
|
||||
unsafe.GET("/:username/graph/:graphType", handler.UnsafeResourceGraph)
|
||||
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
import type { StorybookConfig } from "@storybook/angular";
|
||||
const config: StorybookConfig = {
|
||||
stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
|
||||
addons: [
|
||||
"@storybook/addon-links",
|
||||
"@storybook/addon-essentials",
|
||||
"@storybook/addon-interactions",
|
||||
],
|
||||
framework: {
|
||||
name: "@storybook/angular",
|
||||
options: {},
|
||||
},
|
||||
docs: {
|
||||
autodocs: "tag",
|
||||
},
|
||||
};
|
||||
export default config;
|
|
@ -0,0 +1,29 @@
|
|||
import type { Preview } from "@storybook/angular";
|
||||
import { setCompodocJson } from "@storybook/addon-docs/angular";
|
||||
import docJson from "../documentation.json";
|
||||
import {applicationConfig} from "@storybook/angular";
|
||||
import {importProvidersFrom} from "@angular/core";
|
||||
import {HttpClientModule} from "@angular/common/http";
|
||||
setCompodocJson(docJson);
|
||||
|
||||
// see: https://github.com/storybookjs/storybook/issues/21942#issuecomment-1516177565
|
||||
const decorators = [
|
||||
// applicationConfig({
|
||||
// providers: [importProvidersFrom(HttpClientModule)]
|
||||
// })
|
||||
];
|
||||
|
||||
const preview: Preview = {
|
||||
parameters: {
|
||||
actions: { argTypesRegex: "^on[A-Z].*" },
|
||||
controls: {
|
||||
matchers: {
|
||||
color: /(background|color)$/i,
|
||||
date: /Date$/,
|
||||
},
|
||||
},
|
||||
},
|
||||
decorators: decorators
|
||||
};
|
||||
|
||||
export default preview;
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"extends": "../tsconfig.app.json",
|
||||
"compilerOptions": {
|
||||
"types": ["node"],
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"resolveJsonModule": true
|
||||
},
|
||||
"exclude": ["../src/test.ts", "../src/**/*.spec.ts"],
|
||||
"include": ["../src/**/*", "./preview.ts"],
|
||||
"files": ["./typings.d.ts"]
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
declare module '*.md' {
|
||||
const content: string;
|
||||
export default content;
|
||||
}
|
|
@ -23,7 +23,10 @@
|
|||
"main": "src/main.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"allowedCommonJsDependencies": ["chart.js", "highlight.js"],
|
||||
"allowedCommonJsDependencies": [
|
||||
"chart.js",
|
||||
"highlight.js"
|
||||
],
|
||||
"aot": true,
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
|
@ -32,14 +35,13 @@
|
|||
"glob": "**/*",
|
||||
"input": "./node_modules/dwv/decoders/",
|
||||
"output": "/assets/dwv/decoders/"
|
||||
},
|
||||
}
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.scss"
|
||||
],
|
||||
"scripts": [
|
||||
"node_modules/@panva/oauth4webapi/build/index.js",
|
||||
|
||||
"node_modules/@panva/oauth4webapi/build/index.js"
|
||||
]
|
||||
},
|
||||
"configurations": {
|
||||
|
@ -125,7 +127,6 @@
|
|||
}
|
||||
]
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
"serve": {
|
||||
|
@ -193,6 +194,43 @@
|
|||
"devServerTarget": "fastenhealth:serve:prod"
|
||||
}
|
||||
}
|
||||
},
|
||||
"storybook": {
|
||||
"builder": "@storybook/angular:start-storybook",
|
||||
"options": {
|
||||
"configDir": ".storybook",
|
||||
"browserTarget": "fastenhealth:build",
|
||||
"compodoc": true,
|
||||
"compodocArgs": [
|
||||
"-e",
|
||||
"json",
|
||||
"-d",
|
||||
"."
|
||||
],
|
||||
"assets": [
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "./node_modules/dwv/decoders/",
|
||||
"output": "/assets/dwv/decoders/"
|
||||
}
|
||||
],
|
||||
"port": 6006
|
||||
}
|
||||
},
|
||||
"build-storybook": {
|
||||
"builder": "@storybook/angular:build-storybook",
|
||||
"options": {
|
||||
"configDir": ".storybook",
|
||||
"browserTarget": "fastenhealth:build",
|
||||
"compodoc": true,
|
||||
"compodocArgs": [
|
||||
"-e",
|
||||
"json",
|
||||
"-d",
|
||||
"."
|
||||
],
|
||||
"outputDir": "storybook-static"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
if [[ -z "${CI}" ]]; then
|
||||
echo "running locally (not in Github Actions). generating version file from git client"
|
||||
GIT_TAG=`git describe --tags`
|
||||
GIT_BRANCH=`git rev-parse --abbrev-ref HEAD`
|
||||
|
||||
if [[ "$GIT_BRANCH" == "main" ]]; then
|
||||
VERSION_INFO="${GIT_TAG}"
|
||||
else
|
||||
VERSION_INFO="${GIT_BRANCH}#${GIT_TAG}"
|
||||
fi
|
||||
else
|
||||
echo "running in Github Actions, generating version file from environmental variables"
|
||||
# https://docs.github.com/en/actions/learn-github-actions/environment-variables
|
||||
VERSION_INFO="${GITHUB_REF_NAME}"
|
||||
|
||||
if [[ "$GITHUB_REF_TYPE" == "branch" ]]; then
|
||||
VERSION_INFO="${VERSION_INFO}#${GITHUB_SHA::7}"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "writing version file (version: ${VERSION_INFO})"
|
||||
cat <<EOT > src/environments/versions.ts
|
||||
// this file is automatically generated by git.version.ts script
|
||||
export const versionInfo = {
|
||||
version: '${VERSION_INFO}',
|
||||
};
|
||||
EOT
|
|
@ -9,7 +9,9 @@
|
|||
"test": "ng test",
|
||||
"lint": "ng lint",
|
||||
"e2e": "ng e2e",
|
||||
"dist": "ng build --watch --output-path=../dist"
|
||||
"dist": "ng build --watch --output-path=../dist",
|
||||
"storybook": "ng run fastenhealth:storybook",
|
||||
"build-storybook": "ng run fastenhealth:build-storybook"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
|
@ -38,6 +40,7 @@
|
|||
"chart.js": "2.9.4",
|
||||
"dwv": "^0.31.0",
|
||||
"fhirpath": "^3.3.0",
|
||||
"gridstack": "7.3.0",
|
||||
"humanize-duration": "^3.27.3",
|
||||
"idb": "^7.1.0",
|
||||
"jose": "^4.10.4",
|
||||
|
@ -58,6 +61,13 @@
|
|||
"@angular/cli": "^14.1.3",
|
||||
"@angular/compiler-cli": "^14.1.3",
|
||||
"@angular/language-service": "^14.1.3",
|
||||
"@compodoc/compodoc": "^1.1.19",
|
||||
"@storybook/addon-essentials": "^7.0.7",
|
||||
"@storybook/addon-interactions": "^7.0.7",
|
||||
"@storybook/addon-links": "^7.0.7",
|
||||
"@storybook/angular": "^7.0.7",
|
||||
"@storybook/blocks": "^7.0.7",
|
||||
"@storybook/testing-library": "^0.0.14-next.2",
|
||||
"@types/jasmine": "~3.5.0",
|
||||
"@types/jasminewd2": "~2.0.3",
|
||||
"codelyzer": "^5.1.2",
|
||||
|
@ -70,8 +80,14 @@
|
|||
"karma-jasmine": "~4.0.0",
|
||||
"karma-jasmine-html-reporter": "^1.5.0",
|
||||
"protractor": "~7.0.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"storybook": "^7.0.7",
|
||||
"ts-node": "~8.3.0",
|
||||
"tslint": "~6.1.0",
|
||||
"typescript": "~4.6.4"
|
||||
},
|
||||
"resolutions": {
|
||||
"webpack": "5.74.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import { BrowserModule } from '@angular/platform-browser';
|
|||
import { NgModule } from '@angular/core';
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { AppComponent } from './app.component';
|
||||
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
|
||||
import {HttpClientModule, HTTP_INTERCEPTORS, HttpClient} from '@angular/common/http';
|
||||
import { NgbModule } from "@ng-bootstrap/ng-bootstrap";
|
||||
import { HeaderComponent } from './components/header/header.component';
|
||||
import { FooterComponent } from './components/footer/footer.component';
|
||||
|
@ -32,6 +32,8 @@ import {PipesModule} from './pipes/pipes.module';
|
|||
import { ResourceCreatorComponent } from './pages/resource-creator/resource-creator.component';
|
||||
import { InfiniteScrollModule } from 'ngx-infinite-scroll';
|
||||
import { NgSelectModule } from '@ng-select/ng-select';
|
||||
import {HTTP_CLIENT_TOKEN} from "./dependency-injection";
|
||||
import {WidgetsModule} from './widgets/widgets.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
|
@ -64,9 +66,14 @@ import { NgSelectModule } from '@ng-select/ng-select';
|
|||
MomentModule,
|
||||
PipesModule,
|
||||
InfiniteScrollModule,
|
||||
NgSelectModule
|
||||
NgSelectModule,
|
||||
WidgetsModule
|
||||
],
|
||||
providers: [
|
||||
{
|
||||
provide: HTTP_CLIENT_TOKEN,
|
||||
useClass: HttpClient,
|
||||
},
|
||||
{
|
||||
provide: HTTP_INTERCEPTORS,
|
||||
useClass: AuthInterceptorService,
|
||||
|
|
|
@ -8,7 +8,7 @@ describe('BadgeComponent', () => {
|
|||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ BadgeComponent ]
|
||||
imports: [ BadgeComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import {Component, Input, OnInit} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
selector: 'fhir-ui-badge',
|
||||
templateUrl: './badge.component.html',
|
||||
styleUrls: ['./badge.component.scss']
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
import type { Meta, StoryObj } from '@storybook/angular';
|
||||
import {BadgeComponent} from "./badge.component";
|
||||
|
||||
// More on how to set up stories at: https://storybook.js.org/docs/angular/writing-stories/introduction
|
||||
const meta: Meta<BadgeComponent> = {
|
||||
title: 'Fhir/Common/Badge',
|
||||
component: BadgeComponent,
|
||||
decorators: [
|
||||
// moduleMetadata({
|
||||
// imports: [AppModule]
|
||||
// })
|
||||
// applicationConfig({
|
||||
// providers: [importProvidersFrom(AppModule)],
|
||||
// }),
|
||||
],
|
||||
tags: ['autodocs'],
|
||||
render: (args: BadgeComponent) => ({
|
||||
props: {
|
||||
backgroundColor: null,
|
||||
...args,
|
||||
},
|
||||
}),
|
||||
argTypes: {
|
||||
status: {
|
||||
control: 'text',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<BadgeComponent>;
|
||||
|
||||
// More on writing stories with args: https://storybook.js.org/docs/angular/writing-stories/args
|
||||
export const Primary: Story = {
|
||||
args: {
|
||||
status: "active",
|
||||
}
|
||||
};
|
||||
|
||||
export const Secondary: Story = {
|
||||
args: {
|
||||
status: 'refuted'
|
||||
},
|
||||
};
|
||||
|
|
@ -8,7 +8,7 @@ describe('TableComponent', () => {
|
|||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ TableComponent ]
|
||||
imports: [ TableComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
import {Component, Input, OnInit} from '@angular/core';
|
||||
import {TableRowItem} from './table-row-item';
|
||||
import {BinaryModel} from '../../../../../lib/models/resources/binary-model';
|
||||
import {FastenDisplayModel} from '../../../../../lib/models/fasten/fasten-display-model';
|
||||
import {CommonModule} from "@angular/common";
|
||||
import {CodingComponent} from "../../datatypes/coding/coding.component";
|
||||
import {Router, RouterModule} from "@angular/router";
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
imports: [CommonModule, CodingComponent, RouterModule],
|
||||
providers: [RouterModule],
|
||||
selector: 'fhir-ui-table',
|
||||
templateUrl: './table.component.html',
|
||||
styleUrls: ['./table.component.scss']
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
import type { Meta, StoryObj } from '@storybook/angular';
|
||||
import {TableComponent} from "./table.component";
|
||||
import {TableRowItem} from "./table-row-item";
|
||||
import {FastenDisplayModel} from "../../../../../lib/models/fasten/fasten-display-model";
|
||||
|
||||
// More on how to set up stories at: https://storybook.js.org/docs/angular/writing-stories/introduction
|
||||
const meta: Meta<TableComponent> = {
|
||||
title: 'Fhir/Common/Table',
|
||||
component: TableComponent,
|
||||
decorators: [
|
||||
// moduleMetadata({
|
||||
// imports: [AppModule]
|
||||
// })
|
||||
// applicationConfig({
|
||||
// providers: [importProvidersFrom(AppModule)],
|
||||
// }),
|
||||
],
|
||||
tags: ['autodocs'],
|
||||
render: (args: TableComponent) => ({
|
||||
props: {
|
||||
backgroundColor: null,
|
||||
...args,
|
||||
},
|
||||
}),
|
||||
argTypes: {
|
||||
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<TableComponent>;
|
||||
|
||||
// More on writing stories with args: https://storybook.js.org/docs/angular/writing-stories/args
|
||||
export const String: Story = {
|
||||
args: {
|
||||
tableData: [
|
||||
{
|
||||
enabled: true,
|
||||
label: 'hello',
|
||||
data: 'world'
|
||||
},
|
||||
{
|
||||
enabled: true,
|
||||
label: 'hello',
|
||||
data: 'world'
|
||||
},
|
||||
{
|
||||
enabled: true,
|
||||
label: 'hello',
|
||||
data: 'world'
|
||||
},
|
||||
],
|
||||
}
|
||||
};
|
||||
|
||||
export const Ref: Story = {
|
||||
args: {
|
||||
displayModel: {
|
||||
source_id: '123-456-789',
|
||||
} as FastenDisplayModel,
|
||||
tableData: [
|
||||
{
|
||||
enabled: true,
|
||||
label: 'hello',
|
||||
data_type: 'reference',
|
||||
data: {
|
||||
reference: 'Patient/123',
|
||||
display: 'John Doe'
|
||||
}
|
||||
},
|
||||
{
|
||||
enabled: true,
|
||||
label: 'hello',
|
||||
data_type: 'reference',
|
||||
data: {
|
||||
reference: 'Patient/123',
|
||||
display: 'John Doe'
|
||||
}
|
||||
},
|
||||
{
|
||||
enabled: true,
|
||||
label: 'hello',
|
||||
data_type: 'reference',
|
||||
data: {
|
||||
reference: 'Patient/123',
|
||||
display: 'John Doe'
|
||||
}
|
||||
}
|
||||
] as TableRowItem[]
|
||||
},
|
||||
};
|
||||
|
|
@ -8,7 +8,7 @@ describe('BinaryTextComponent', () => {
|
|||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ BinaryTextComponent ]
|
||||
imports: [ BinaryTextComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
import {Component, Input, OnInit} from '@angular/core';
|
||||
import {BinaryModel} from '../../../../../lib/models/resources/binary-model';
|
||||
import {CommonModule} from "@angular/common";
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
imports: [CommonModule],
|
||||
selector: 'fhir-binary-text',
|
||||
templateUrl: './binary-text.component.html',
|
||||
styleUrls: ['./binary-text.component.scss']
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
import type { Meta, StoryObj } from '@storybook/angular';
|
||||
import {fhirVersions} from "../../../../../lib/models/constants";
|
||||
import R4Example1Json from "../../../../../lib/fixtures/r4/resources/binary/exampleText.json";
|
||||
import {BinaryTextComponent} from "./binary-text.component";
|
||||
import {BinaryModel} from "../../../../../lib/models/resources/binary-model";
|
||||
|
||||
|
||||
|
||||
// More on how to set up stories at: https://storybook.js.org/docs/angular/writing-stories/introduction
|
||||
const meta: Meta<BinaryTextComponent> = {
|
||||
title: 'Fhir/Datatypes/BinaryText',
|
||||
component: BinaryTextComponent,
|
||||
decorators: [
|
||||
// moduleMetadata({
|
||||
// imports: [AppModule]
|
||||
// })
|
||||
// applicationConfig({
|
||||
// providers: [importProvidersFrom(AppModule)],
|
||||
// }),
|
||||
],
|
||||
tags: ['autodocs'],
|
||||
render: (args: BinaryTextComponent) => ({
|
||||
props: {
|
||||
backgroundColor: null,
|
||||
...args,
|
||||
},
|
||||
}),
|
||||
argTypes: {
|
||||
displayModel: {
|
||||
control: 'object',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<BinaryTextComponent>;
|
||||
|
||||
// More on writing stories with args: https://storybook.js.org/docs/angular/writing-stories/args
|
||||
let aiDisplayModel1 = new BinaryModel(R4Example1Json, fhirVersions.R4)
|
||||
aiDisplayModel1.source_id = '123-456-789'
|
||||
aiDisplayModel1.source_resource_id = '123-456-789'
|
||||
export const R4Example1: Story = {
|
||||
args: {
|
||||
displayModel: aiDisplayModel1
|
||||
}
|
||||
};
|
||||
|
|
@ -8,7 +8,7 @@ describe('CodingComponent', () => {
|
|||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ CodingComponent ]
|
||||
imports: [ CodingComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
import {Component, Input, OnInit} from '@angular/core';
|
||||
import {CodingModel} from '../../../../../lib/models/datatypes/coding-model';
|
||||
import {CommonModule} from "@angular/common";
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
imports: [CommonModule],
|
||||
selector: 'fhir-coding',
|
||||
templateUrl: './coding.component.html',
|
||||
styleUrls: ['./coding.component.scss']
|
||||
|
|
|
@ -8,7 +8,7 @@ describe('DicomComponent', () => {
|
|||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ DicomComponent ]
|
||||
imports: [ DicomComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import {Component, Input, OnInit, TemplateRef} from '@angular/core';
|
||||
import * as dwv from 'dwv';
|
||||
import {NgbModal} from '@ng-bootstrap/ng-bootstrap';
|
||||
import {NgbModal, NgbModalModule, NgbPaginationModule, NgbTooltipModule} from '@ng-bootstrap/ng-bootstrap';
|
||||
import { VERSION } from '@angular/core';
|
||||
import {BinaryModel} from '../../../../../lib/models/resources/binary-model';
|
||||
import {CommonModule} from "@angular/common";
|
||||
// Copied from https://raw.githubusercontent.com/ivmartel/dwv-angular/master/src/app/dwv/dwv.component.ts
|
||||
|
||||
// gui overrides
|
||||
|
@ -17,6 +18,9 @@ dwv.image.decoderScripts = {
|
|||
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
imports: [CommonModule, NgbModalModule, NgbTooltipModule, NgbPaginationModule],
|
||||
providers: [NgbModalModule],
|
||||
selector: 'fhir-dicom',
|
||||
templateUrl: './dicom.component.html',
|
||||
styleUrls: ['./dicom.component.scss']
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
import type { Meta, StoryObj } from '@storybook/angular';
|
||||
import {fhirVersions} from "../../../../../lib/models/constants";
|
||||
import R4Example1Json from "../../../../../lib/fixtures/r4/resources/binary/exampleDicom.json";
|
||||
import {BinaryModel} from "../../../../../lib/models/resources/binary-model";
|
||||
import {DicomComponent} from "./dicom.component";
|
||||
|
||||
// More on how to set up stories at: https://storybook.js.org/docs/angular/writing-stories/introduction
|
||||
const meta: Meta<DicomComponent> = {
|
||||
title: 'Fhir/Datatypes/Dicom',
|
||||
component: DicomComponent,
|
||||
decorators: [
|
||||
// moduleMetadata({
|
||||
// imports: [AppModule]
|
||||
// })
|
||||
// applicationConfig({
|
||||
// providers: [importProvidersFrom(AppModule)],
|
||||
// }),
|
||||
],
|
||||
tags: ['autodocs'],
|
||||
render: (args: DicomComponent) => ({
|
||||
props: {
|
||||
backgroundColor: null,
|
||||
...args,
|
||||
},
|
||||
}),
|
||||
argTypes: {
|
||||
displayModel: {
|
||||
control: 'object',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<DicomComponent>;
|
||||
|
||||
// More on writing stories with args: https://storybook.js.org/docs/angular/writing-stories/args
|
||||
let aiDisplayModel1 = new BinaryModel(R4Example1Json, fhirVersions.R4)
|
||||
aiDisplayModel1.source_id = '123-456-789'
|
||||
aiDisplayModel1.source_resource_id = '123-456-789'
|
||||
export const R4Example1: Story = {
|
||||
args: {
|
||||
displayModel: aiDisplayModel1
|
||||
}
|
||||
};
|
||||
|
|
@ -8,7 +8,7 @@ describe('HtmlComponent', () => {
|
|||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ HtmlComponent ]
|
||||
imports: [ HtmlComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
import {Component, Input, OnInit} from '@angular/core';
|
||||
import {BinaryModel} from '../../../../../lib/models/resources/binary-model';
|
||||
import {DomSanitizer, SafeHtml} from '@angular/platform-browser';
|
||||
import {CommonModule} from "@angular/common";
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
imports: [CommonModule],
|
||||
selector: 'fhir-html',
|
||||
templateUrl: './html.component.html',
|
||||
styleUrls: ['./html.component.scss']
|
||||
|
@ -13,6 +16,7 @@ export class HtmlComponent implements OnInit {
|
|||
constructor(private sanitized: DomSanitizer) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
//TODO: safely display html content
|
||||
this.contentMarkup = this.sanitized.bypassSecurityTrustHtml(this.displayModel?.content);
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
import type { Meta, StoryObj } from '@storybook/angular';
|
||||
import {fhirVersions} from "../../../../../lib/models/constants";
|
||||
import R4Example1Json from "../../../../../lib/fixtures/r4/resources/binary/exampleHtml.json";
|
||||
import {BinaryModel} from "../../../../../lib/models/resources/binary-model";
|
||||
import {HtmlComponent} from "./html.component";
|
||||
|
||||
|
||||
|
||||
// More on how to set up stories at: https://storybook.js.org/docs/angular/writing-stories/introduction
|
||||
const meta: Meta<HtmlComponent> = {
|
||||
title: 'Fhir/Datatypes/Html',
|
||||
component: HtmlComponent,
|
||||
decorators: [
|
||||
// moduleMetadata({
|
||||
// imports: [AppModule]
|
||||
// })
|
||||
// applicationConfig({
|
||||
// providers: [importProvidersFrom(AppModule)],
|
||||
// }),
|
||||
],
|
||||
tags: ['autodocs'],
|
||||
render: (args: HtmlComponent) => ({
|
||||
props: {
|
||||
backgroundColor: null,
|
||||
...args,
|
||||
},
|
||||
}),
|
||||
argTypes: {
|
||||
displayModel: {
|
||||
control: 'object',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<HtmlComponent>;
|
||||
|
||||
// More on writing stories with args: https://storybook.js.org/docs/angular/writing-stories/args
|
||||
let aiDisplayModel1 = new BinaryModel(R4Example1Json, fhirVersions.R4)
|
||||
aiDisplayModel1.source_id = '123-456-789'
|
||||
aiDisplayModel1.source_resource_id = '123-456-789'
|
||||
export const R4Example1: Story = {
|
||||
args: {
|
||||
displayModel: aiDisplayModel1
|
||||
}
|
||||
};
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
<div>
|
||||
<img src="data:${{displayModel?.content_type}};base64,${{displayModel?.data}}" alt=""/>
|
||||
<img src="data:{{displayModel?.content_type}};base64,{{displayModel?.data}}" alt=""/>
|
||||
</div>
|
||||
|
|
|
@ -8,7 +8,7 @@ describe('ImgComponent', () => {
|
|||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ ImgComponent ]
|
||||
imports: [ ImgComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
import {Component, Input, OnInit} from '@angular/core';
|
||||
import {BinaryModel} from '../../../../../lib/models/resources/binary-model';
|
||||
import {CommonModule} from "@angular/common";
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
imports: [CommonModule],
|
||||
selector: 'fhir-img',
|
||||
templateUrl: './img.component.html',
|
||||
styleUrls: ['./img.component.scss']
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
import type { Meta, StoryObj } from '@storybook/angular';
|
||||
import {fhirVersions} from "../../../../../lib/models/constants";
|
||||
import R4Example1Json from "../../../../../lib/fixtures/r4/resources/binary/exampleJpeg.json";
|
||||
import {BinaryModel} from "../../../../../lib/models/resources/binary-model";
|
||||
import {ImgComponent} from "./img.component";
|
||||
|
||||
// More on how to set up stories at: https://storybook.js.org/docs/angular/writing-stories/introduction
|
||||
const meta: Meta<ImgComponent> = {
|
||||
title: 'Fhir/Datatypes/Img',
|
||||
component: ImgComponent,
|
||||
decorators: [
|
||||
// moduleMetadata({
|
||||
// imports: [AppModule]
|
||||
// })
|
||||
// applicationConfig({
|
||||
// providers: [importProvidersFrom(AppModule)],
|
||||
// }),
|
||||
],
|
||||
tags: ['autodocs'],
|
||||
render: (args: ImgComponent) => ({
|
||||
props: {
|
||||
backgroundColor: null,
|
||||
...args,
|
||||
},
|
||||
}),
|
||||
argTypes: {
|
||||
displayModel: {
|
||||
control: 'object',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<ImgComponent>;
|
||||
|
||||
// More on writing stories with args: https://storybook.js.org/docs/angular/writing-stories/args
|
||||
let aiDisplayModel1 = new BinaryModel(R4Example1Json, fhirVersions.R4)
|
||||
aiDisplayModel1.source_id = '123-456-789'
|
||||
aiDisplayModel1.source_resource_id = '123-456-789'
|
||||
export const R4Example1: Story = {
|
||||
args: {
|
||||
displayModel: aiDisplayModel1
|
||||
}
|
||||
};
|
||||
|
|
@ -8,7 +8,7 @@ describe('MarkdownComponent', () => {
|
|||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ MarkdownComponent ]
|
||||
imports: [ MarkdownComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
import {Component, Input, OnInit} from '@angular/core';
|
||||
import {BinaryModel} from '../../../../../lib/models/resources/binary-model';
|
||||
import {CommonModule} from "@angular/common";
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
imports: [CommonModule],
|
||||
selector: 'fhir-markdown',
|
||||
templateUrl: './markdown.component.html',
|
||||
styleUrls: ['./markdown.component.scss']
|
||||
|
|
|
@ -8,7 +8,7 @@ describe('PdfComponent', () => {
|
|||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ PdfComponent ]
|
||||
imports: [ PdfComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
import {Component, Input, OnInit} from '@angular/core';
|
||||
import {BinaryModel} from '../../../../../lib/models/resources/binary-model';
|
||||
import {DomSanitizer, SafeUrl} from '@angular/platform-browser';
|
||||
import {CommonModule} from "@angular/common";
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
imports: [CommonModule],
|
||||
selector: 'fhir-pdf',
|
||||
templateUrl: './pdf.component.html',
|
||||
styleUrls: ['./pdf.component.scss']
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
import type { Meta, StoryObj } from '@storybook/angular';
|
||||
import {fhirVersions} from "../../../../../lib/models/constants";
|
||||
import R4Example1Json from "../../../../../lib/fixtures/r4/resources/binary/examplePdf.json";
|
||||
import {BinaryModel} from "../../../../../lib/models/resources/binary-model";
|
||||
import {PdfComponent} from "./pdf.component";
|
||||
|
||||
// More on how to set up stories at: https://storybook.js.org/docs/angular/writing-stories/introduction
|
||||
const meta: Meta<PdfComponent> = {
|
||||
title: 'Fhir/Datatypes/Pdf',
|
||||
component: PdfComponent,
|
||||
decorators: [
|
||||
// moduleMetadata({
|
||||
// imports: [AppModule]
|
||||
// })
|
||||
// applicationConfig({
|
||||
// providers: [importProvidersFrom(AppModule)],
|
||||
// }),
|
||||
],
|
||||
tags: ['autodocs'],
|
||||
render: (args: PdfComponent) => ({
|
||||
props: {
|
||||
backgroundColor: null,
|
||||
...args,
|
||||
},
|
||||
}),
|
||||
argTypes: {
|
||||
displayModel: {
|
||||
control: 'object',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<PdfComponent>;
|
||||
|
||||
// More on writing stories with args: https://storybook.js.org/docs/angular/writing-stories/args
|
||||
let aiDisplayModel1 = new BinaryModel(R4Example1Json, fhirVersions.R4)
|
||||
aiDisplayModel1.source_id = '123-456-789'
|
||||
aiDisplayModel1.source_resource_id = '123-456-789'
|
||||
export const R4Example1: Story = {
|
||||
args: {
|
||||
displayModel: aiDisplayModel1
|
||||
}
|
||||
};
|
||||
|
|
@ -4,5 +4,10 @@ import {FastenDisplayModel} from '../../../../lib/models/fasten/fasten-display-m
|
|||
export interface FhirResourceComponentInterface {
|
||||
displayModel: FastenDisplayModel;
|
||||
showDetails: boolean;
|
||||
|
||||
//these are used to populate the description of the resource. May not be available for all resources
|
||||
resourceCode?: string;
|
||||
resourceCodeSystem?: string;
|
||||
|
||||
markForCheck()
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ import {MediaComponent} from '../resources/media/media.component';
|
|||
|
||||
@Component({
|
||||
selector: 'fhir-resource',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
changeDetection: ChangeDetectionStrategy.Default,
|
||||
templateUrl: './fhir-resource.component.html',
|
||||
styleUrls: ['./fhir-resource.component.scss']
|
||||
})
|
||||
|
|
|
@ -2,6 +2,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|||
|
||||
import { AllergyIntoleranceComponent } from './allergy-intolerance.component';
|
||||
import {NgbCollapseModule} from '@ng-bootstrap/ng-bootstrap';
|
||||
import {AuthService} from '../../../../services/auth.service';
|
||||
|
||||
describe('AllergyIntoleranceComponent', () => {
|
||||
let component: AllergyIntoleranceComponent;
|
||||
|
@ -9,8 +10,8 @@ describe('AllergyIntoleranceComponent', () => {
|
|||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ AllergyIntoleranceComponent ],
|
||||
imports: [NgbCollapseModule]
|
||||
imports: [AllergyIntoleranceComponent, NgbCollapseModule],
|
||||
providers: [AuthService]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
|
|
|
@ -3,8 +3,14 @@ import {FhirResourceComponentInterface} from '../../fhir-resource/fhir-resource-
|
|||
import {TableRowItem, TableRowItemDataType} from '../../common/table/table-row-item';
|
||||
import {Router} from '@angular/router';
|
||||
import {AllergyIntoleranceModel} from '../../../../../lib/models/resources/allergy-intolerance-model';
|
||||
import {NgbCollapseModule} from "@ng-bootstrap/ng-bootstrap";
|
||||
import {CommonModule} from "@angular/common";
|
||||
import {BadgeComponent} from "../../common/badge/badge.component";
|
||||
import {TableComponent} from "../../common/table/table.component";
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
imports: [NgbCollapseModule, CommonModule, BadgeComponent, TableComponent],
|
||||
selector: 'fhir-allergy-intolerance',
|
||||
templateUrl: './allergy-intolerance.component.html',
|
||||
styleUrls: ['./allergy-intolerance.component.scss']
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
import type { Meta, StoryObj } from '@storybook/angular';
|
||||
import {AllergyIntoleranceComponent} from "./allergy-intolerance.component";
|
||||
import {AllergyIntoleranceModel} from "../../../../../lib/models/resources/allergy-intolerance-model";
|
||||
import {fhirVersions} from "../../../../../lib/models/constants";
|
||||
import R4Example1Json from "../../../../../lib/fixtures/r4/resources/allergyIntolerance/example1.json";
|
||||
import R4Example2Json from "../../../../../lib/fixtures/r4/resources/allergyIntolerance/example2.json";
|
||||
import R4Example3Json from "../../../../../lib/fixtures/r4/resources/allergyIntolerance/example3.json";
|
||||
|
||||
|
||||
|
||||
// More on how to set up stories at: https://storybook.js.org/docs/angular/writing-stories/introduction
|
||||
const meta: Meta<AllergyIntoleranceComponent> = {
|
||||
title: 'Fhir/AllergyIntolerance',
|
||||
component: AllergyIntoleranceComponent,
|
||||
decorators: [
|
||||
// moduleMetadata({
|
||||
// imports: [AppModule]
|
||||
// })
|
||||
// applicationConfig({
|
||||
// providers: [importProvidersFrom(AppModule)],
|
||||
// }),
|
||||
],
|
||||
tags: ['autodocs'],
|
||||
render: (args: AllergyIntoleranceComponent) => ({
|
||||
props: {
|
||||
backgroundColor: null,
|
||||
...args,
|
||||
},
|
||||
}),
|
||||
argTypes: {
|
||||
displayModel: {
|
||||
control: 'object',
|
||||
},
|
||||
showDetails: {
|
||||
control: 'boolean',
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<AllergyIntoleranceComponent>;
|
||||
|
||||
// More on writing stories with args: https://storybook.js.org/docs/angular/writing-stories/args
|
||||
let aiDisplayModel1 = new AllergyIntoleranceModel(R4Example1Json, fhirVersions.R4)
|
||||
aiDisplayModel1.source_id = '123-456-789'
|
||||
aiDisplayModel1.source_resource_id = '123-456-789'
|
||||
export const R4Example1: Story = {
|
||||
args: {
|
||||
displayModel: aiDisplayModel1
|
||||
}
|
||||
};
|
||||
|
||||
let aiDisplayModel2 = new AllergyIntoleranceModel(R4Example2Json, fhirVersions.R4)
|
||||
aiDisplayModel1.source_id = '123-456-789'
|
||||
aiDisplayModel1.source_resource_id = '123-456-789'
|
||||
export const R4Example2: Story = {
|
||||
args: {
|
||||
displayModel: aiDisplayModel2
|
||||
}
|
||||
};
|
||||
|
||||
let aiDisplayModel3 = new AllergyIntoleranceModel(R4Example3Json, fhirVersions.R4)
|
||||
aiDisplayModel1.source_id = '123-456-789'
|
||||
aiDisplayModel1.source_resource_id = '123-456-789'
|
||||
export const R4Example3: Story = {
|
||||
args: {
|
||||
displayModel: aiDisplayModel3
|
||||
}
|
||||
};
|
||||
|
|
@ -4,6 +4,9 @@ import { BinaryComponent } from './binary.component';
|
|||
import {NgbCollapseModule} from '@ng-bootstrap/ng-bootstrap';
|
||||
import {FastenApiService} from '../../../../services/fasten-api.service';
|
||||
import {RouterTestingModule} from '@angular/router/testing';
|
||||
import {HTTP_CLIENT_TOKEN} from '../../../../dependency-injection';
|
||||
import {HttpClient} from '@angular/common/http';
|
||||
import {HttpClientTestingModule} from '@angular/common/http/testing';
|
||||
|
||||
describe('BinaryComponent', () => {
|
||||
let component: BinaryComponent;
|
||||
|
@ -14,12 +17,17 @@ describe('BinaryComponent', () => {
|
|||
mockedFastenApiService = jasmine.createSpyObj('FastenApiService', ['getBinaryModel'])
|
||||
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ BinaryComponent ],
|
||||
imports: [NgbCollapseModule, RouterTestingModule],
|
||||
providers: [{
|
||||
imports: [HttpClientTestingModule, BinaryComponent, NgbCollapseModule, RouterTestingModule],
|
||||
providers: [
|
||||
{
|
||||
provide: FastenApiService,
|
||||
useValue: mockedFastenApiService
|
||||
}]
|
||||
},
|
||||
{
|
||||
provide: HTTP_CLIENT_TOKEN,
|
||||
useClass: HttpClient,
|
||||
},
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
|
|
|
@ -4,8 +4,34 @@ import {FhirResourceComponentInterface} from '../../fhir-resource/fhir-resource-
|
|||
import {Router} from '@angular/router';
|
||||
import {AttachmentModel} from '../../../../../lib/models/datatypes/attachment-model';
|
||||
import {FastenApiService} from '../../../../services/fasten-api.service';
|
||||
import {NgbCollapseModule} from "@ng-bootstrap/ng-bootstrap";
|
||||
import {CommonModule} from "@angular/common";
|
||||
import {BadgeComponent} from "../../common/badge/badge.component";
|
||||
import {TableComponent} from "../../common/table/table.component";
|
||||
import {PdfComponent} from "../../datatypes/pdf/pdf.component";
|
||||
import {ImgComponent} from "../../datatypes/img/img.component";
|
||||
import {HtmlComponent} from "../../datatypes/html/html.component";
|
||||
import {MarkdownComponent} from "../../datatypes/markdown/markdown.component";
|
||||
import {BinaryTextComponent} from "../../datatypes/binary-text/binary-text.component";
|
||||
import {DicomComponent} from "../../datatypes/dicom/dicom.component";
|
||||
import {HighlightModule} from "ngx-highlightjs";
|
||||
import {HttpClient, HttpClientModule} from "@angular/common/http";
|
||||
import {AuthService} from "../../../../services/auth.service";
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
imports: [
|
||||
NgbCollapseModule,
|
||||
CommonModule,
|
||||
PdfComponent,
|
||||
ImgComponent,
|
||||
HtmlComponent,
|
||||
MarkdownComponent,
|
||||
BinaryTextComponent,
|
||||
DicomComponent,
|
||||
HighlightModule,
|
||||
],
|
||||
providers: [FastenApiService, AuthService],
|
||||
selector: 'fhir-binary',
|
||||
templateUrl: './binary.component.html',
|
||||
styleUrls: ['./binary.component.scss']
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
import type { Meta, StoryObj } from '@storybook/angular';
|
||||
import {fhirVersions} from "../../../../../lib/models/constants";
|
||||
import R4ExampleJpegJson from "../../../../../lib/fixtures/r4/resources/binary/exampleJpeg.json";
|
||||
import R4ExampleHtmlJson from "../../../../../lib/fixtures/r4/resources/binary/exampleHtml.json";
|
||||
import R4ExampleDicomJson from "../../../../../lib/fixtures/r4/resources/binary/exampleDicom.json";
|
||||
import R4ExamplePdfJson from "../../../../../lib/fixtures/r4/resources/binary/examplePdf.json";
|
||||
import R4ExampleTextJson from "../../../../../lib/fixtures/r4/resources/binary/exampleText.json";
|
||||
import R4ExampleXmlJson from "../../../../../lib/fixtures/r4/resources/binary/exampleXml.json";
|
||||
import {BinaryComponent} from "./binary.component";
|
||||
import {BinaryModel} from "../../../../../lib/models/resources/binary-model";
|
||||
import {moduleMetadata} from "@storybook/angular";
|
||||
import {BrowserModule} from "@angular/platform-browser";
|
||||
import {HttpClient, HttpClientModule} from "@angular/common/http";
|
||||
import {CommonModule} from "@angular/common";
|
||||
|
||||
|
||||
|
||||
// More on how to set up stories at: https://storybook.js.org/docs/angular/writing-stories/introduction
|
||||
const meta: Meta<BinaryComponent> = {
|
||||
title: 'Fhir/Binary',
|
||||
component: BinaryComponent,
|
||||
decorators: [
|
||||
moduleMetadata({
|
||||
imports: [CommonModule, HttpClientModule],
|
||||
providers: [{ provide: HttpClient, useClass: HttpClient }],
|
||||
}),
|
||||
// applicationConfig({
|
||||
// providers: [importProvidersFrom(AppModule)],
|
||||
// }),
|
||||
],
|
||||
tags: ['autodocs'],
|
||||
render: (args: BinaryComponent) => ({
|
||||
props: {
|
||||
backgroundColor: null,
|
||||
...args,
|
||||
},
|
||||
}),
|
||||
argTypes: {
|
||||
displayModel: {
|
||||
control: 'object',
|
||||
},
|
||||
showDetails: {
|
||||
control: 'boolean',
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<BinaryComponent>;
|
||||
|
||||
// More on writing stories with args: https://storybook.js.org/docs/angular/writing-stories/args
|
||||
let r4ExampleJpegDisplayModel = new BinaryModel(R4ExampleJpegJson, fhirVersions.R4)
|
||||
r4ExampleJpegDisplayModel.source_id = '123-456-789'
|
||||
r4ExampleJpegDisplayModel.source_resource_id = '123-456-789'
|
||||
export const R4ExampleJpeg: Story = {
|
||||
args: {
|
||||
displayModel: r4ExampleJpegDisplayModel
|
||||
}
|
||||
};
|
||||
|
||||
let r4ExampleHtmlDisplayModel = new BinaryModel(R4ExampleHtmlJson, fhirVersions.R4)
|
||||
r4ExampleHtmlDisplayModel.source_id = '123-456-789'
|
||||
r4ExampleHtmlDisplayModel.source_resource_id = '123-456-789'
|
||||
export const R4ExampleHtml: Story = {
|
||||
args: {
|
||||
displayModel: r4ExampleHtmlDisplayModel
|
||||
}
|
||||
};
|
||||
|
||||
let r4ExampleDicomDisplayModel = new BinaryModel(R4ExampleDicomJson, fhirVersions.R4)
|
||||
r4ExampleDicomDisplayModel.source_id = '123-456-789'
|
||||
r4ExampleDicomDisplayModel.source_resource_id = '123-456-789'
|
||||
export const R4ExampleDicom: Story = {
|
||||
args: {
|
||||
displayModel: r4ExampleDicomDisplayModel
|
||||
}
|
||||
};
|
||||
|
||||
let r4ExamplePdfDisplayModel = new BinaryModel(R4ExamplePdfJson, fhirVersions.R4)
|
||||
r4ExamplePdfDisplayModel.source_id = '123-456-789'
|
||||
r4ExamplePdfDisplayModel.source_resource_id = '123-456-789'
|
||||
export const R4ExamplePdf: Story = {
|
||||
args: {
|
||||
displayModel: r4ExamplePdfDisplayModel
|
||||
}
|
||||
};
|
||||
|
||||
let r4ExampleTextDisplayModel = new BinaryModel(R4ExampleTextJson, fhirVersions.R4)
|
||||
r4ExampleTextDisplayModel.source_id = '123-456-789'
|
||||
r4ExampleTextDisplayModel.source_resource_id = '123-456-789'
|
||||
export const R4ExampleText: Story = {
|
||||
args: {
|
||||
displayModel: r4ExampleTextDisplayModel
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
let r4ExampleXmlDisplayModel = new BinaryModel(R4ExampleXmlJson, fhirVersions.R4)
|
||||
r4ExampleXmlDisplayModel.source_id = '123-456-789'
|
||||
r4ExampleXmlDisplayModel.source_resource_id = '123-456-789'
|
||||
export const R4ExampleXml: Story = {
|
||||
args: {
|
||||
displayModel: r4ExampleXmlDisplayModel
|
||||
}
|
||||
};
|
|
@ -12,10 +12,14 @@
|
|||
<!-- </div>-->
|
||||
</div>
|
||||
<div #collapse="ngbCollapse" [(ngbCollapse)]="isCollapsed" class="card-body">
|
||||
<p class="az-content-text mg-b-20">An action that is or was performed on or for a patient, practitioner, device, organization, or location. For example, this can be a physical intervention on a patient like an operation, or less invasive like long term services, counseling, or hypnotherapy.</p>
|
||||
<p class="az-content-text mg-b-20" *ngIf="!(resourceCode && resourceCodeSystem); else lookupCode">An action that is or was performed on or for a patient, practitioner, device, organization, or location. For example, this can be a physical intervention on a patient like an operation, or less invasive like long term services, counseling, or hypnotherapy.</p>
|
||||
<fhir-ui-table [displayModel]="displayModel" [tableData]="tableData"></fhir-ui-table>
|
||||
</div>
|
||||
<div *ngIf="showDetails" class="card-footer">
|
||||
<a class="float-right" routerLink="/source/{{displayModel?.source_id}}/resource/{{displayModel?.source_resource_id}}">details</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ng-template #lookupCode>
|
||||
<app-glossary-lookup class="az-content-text mg-b-20" [code]="resourceCode" [codeSystem]="resourceCodeSystem"></app-glossary-lookup>
|
||||
</ng-template>
|
||||
|
|
|
@ -12,6 +12,10 @@ import {DiagnosticReportModel} from '../../../../../lib/models/resources/diagnos
|
|||
export class DiagnosticReportComponent implements OnInit, FhirResourceComponentInterface {
|
||||
@Input() displayModel: DiagnosticReportModel
|
||||
@Input() showDetails: boolean = true
|
||||
//these are used to populate the description of the resource. May not be available for all resources
|
||||
resourceCode?: string;
|
||||
resourceCodeSystem?: string;
|
||||
|
||||
isCollapsed: boolean = false
|
||||
tableData: TableRowItem[] = []
|
||||
|
||||
|
@ -19,6 +23,9 @@ export class DiagnosticReportComponent implements OnInit, FhirResourceComponentI
|
|||
|
||||
|
||||
ngOnInit(): void {
|
||||
this.resourceCode = this.displayModel?.code_coding?.[0]?.code
|
||||
this.resourceCodeSystem = this.displayModel?.code_coding?.[0]?.system
|
||||
|
||||
this.tableData = [
|
||||
{
|
||||
label: 'Issued',
|
||||
|
|
|
@ -9,8 +9,7 @@ describe('ImmunizationComponent', () => {
|
|||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ ImmunizationComponent ],
|
||||
imports: [NgbCollapseModule]
|
||||
imports: [ImmunizationComponent, NgbCollapseModule]
|
||||
|
||||
})
|
||||
.compileComponents();
|
||||
|
|
|
@ -4,8 +4,14 @@ import {Router} from '@angular/router';
|
|||
import {ImmunizationModel} from '../../../../../lib/models/resources/immunization-model';
|
||||
import {TableRowItem, TableRowItemDataType} from '../../common/table/table-row-item';
|
||||
import * as _ from "lodash";
|
||||
import {NgbCollapseModule} from "@ng-bootstrap/ng-bootstrap";
|
||||
import {CommonModule} from "@angular/common";
|
||||
import {BadgeComponent} from "../../common/badge/badge.component";
|
||||
import {TableComponent} from "../../common/table/table.component";
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
imports: [NgbCollapseModule, CommonModule, BadgeComponent, TableComponent],
|
||||
selector: 'fhir-immunization',
|
||||
templateUrl: './immunization.component.html',
|
||||
styleUrls: ['./immunization.component.scss']
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
import type { Meta, StoryObj } from '@storybook/angular';
|
||||
import {fhirVersions} from "../../../../../lib/models/constants";
|
||||
import R4Example1Json from "../../../../../lib/fixtures/r4/resources/immunization/example1.json";
|
||||
import R4Example2Json from "../../../../../lib/fixtures/r4/resources/immunization/example2.json";
|
||||
import R4Example3Json from "../../../../../lib/fixtures/r4/resources/immunization/example3.json";
|
||||
import {ImmunizationComponent} from "./immunization.component";
|
||||
import {ImmunizationModel} from "../../../../../lib/models/resources/immunization-model";
|
||||
|
||||
|
||||
|
||||
// More on how to set up stories at: https://storybook.js.org/docs/angular/writing-stories/introduction
|
||||
const meta: Meta<ImmunizationComponent> = {
|
||||
title: 'Fhir/Immunization',
|
||||
component: ImmunizationComponent,
|
||||
decorators: [
|
||||
// moduleMetadata({
|
||||
// imports: [AppModule]
|
||||
// })
|
||||
// applicationConfig({
|
||||
// providers: [importProvidersFrom(AppModule)],
|
||||
// }),
|
||||
],
|
||||
tags: ['autodocs'],
|
||||
render: (args: ImmunizationComponent) => ({
|
||||
props: {
|
||||
backgroundColor: null,
|
||||
...args,
|
||||
},
|
||||
}),
|
||||
argTypes: {
|
||||
displayModel: {
|
||||
control: 'object',
|
||||
},
|
||||
showDetails: {
|
||||
control: 'boolean',
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<ImmunizationComponent>;
|
||||
|
||||
// More on writing stories with args: https://storybook.js.org/docs/angular/writing-stories/args
|
||||
let r4Example1DisplayModel = new ImmunizationModel(R4Example1Json, fhirVersions.R4)
|
||||
r4Example1DisplayModel.source_id = '123-456-789'
|
||||
r4Example1DisplayModel.source_resource_id = '123-456-789'
|
||||
export const R4Example1: Story = {
|
||||
args: {
|
||||
displayModel: r4Example1DisplayModel
|
||||
}
|
||||
};
|
||||
|
||||
let r4Example2DisplayModel = new ImmunizationModel(R4Example2Json, fhirVersions.R4)
|
||||
r4Example2DisplayModel.source_id = '123-456-789'
|
||||
r4Example2DisplayModel.source_resource_id = '123-456-789'
|
||||
export const R4Example2: Story = {
|
||||
args: {
|
||||
displayModel: r4Example2DisplayModel
|
||||
}
|
||||
};
|
||||
|
||||
let r4Example3DisplayModel = new ImmunizationModel(R4Example3Json, fhirVersions.R4)
|
||||
r4Example3DisplayModel.source_id = '123-456-789'
|
||||
r4Example3DisplayModel.source_resource_id = '123-456-789'
|
||||
export const R4Example3: Story = {
|
||||
args: {
|
||||
displayModel: r4Example3DisplayModel
|
||||
}
|
||||
};
|
||||
|
|
@ -12,10 +12,14 @@
|
|||
<!-- </div>-->
|
||||
</div>
|
||||
<div #collapse="ngbCollapse" [(ngbCollapse)]="isCollapsed" class="card-body">
|
||||
<p class="az-content-text mg-b-20">An order or request for both supply of the medication and the instructions for administration of the medication to a patient.</p>
|
||||
<p class="az-content-text mg-b-20" *ngIf="!(resourceCode && resourceCodeSystem); else lookupCode">An order or request for both supply of the medication and the instructions for administration of the medication to a patient.</p>
|
||||
<fhir-ui-table [displayModel]="displayModel" [tableData]="tableData"></fhir-ui-table>
|
||||
</div>
|
||||
<div *ngIf="showDetails" class="card-footer">
|
||||
<a class="float-right" routerLink="/source/{{displayModel?.source_id}}/resource/{{displayModel?.source_resource_id}}">details</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ng-template #lookupCode>
|
||||
<app-glossary-lookup class="az-content-text mg-b-20" [code]="resourceCode" [codeSystem]="resourceCodeSystem"></app-glossary-lookup>
|
||||
</ng-template>
|
||||
|
|
|
@ -9,8 +9,7 @@ describe('MedicationRequestComponent', () => {
|
|||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ MedicationRequestComponent ],
|
||||
imports: [NgbCollapseModule]
|
||||
imports: [MedicationRequestComponent, NgbCollapseModule]
|
||||
|
||||
})
|
||||
.compileComponents();
|
||||
|
|
|
@ -3,8 +3,14 @@ import {FhirResourceComponentInterface} from '../../fhir-resource/fhir-resource-
|
|||
import {TableRowItem, TableRowItemDataType} from '../../common/table/table-row-item';
|
||||
import {Router} from '@angular/router';
|
||||
import {MedicationRequestModel} from '../../../../../lib/models/resources/medication-request-model';
|
||||
import {NgbCollapseModule} from "@ng-bootstrap/ng-bootstrap";
|
||||
import {CommonModule} from "@angular/common";
|
||||
import {BadgeComponent} from "../../common/badge/badge.component";
|
||||
import {TableComponent} from "../../common/table/table.component";
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
imports: [NgbCollapseModule, CommonModule, BadgeComponent, TableComponent],
|
||||
selector: 'fhir-medication-request',
|
||||
templateUrl: './medication-request.component.html',
|
||||
styleUrls: ['./medication-request.component.scss']
|
||||
|
@ -12,6 +18,10 @@ import {MedicationRequestModel} from '../../../../../lib/models/resources/medica
|
|||
export class MedicationRequestComponent implements OnInit, FhirResourceComponentInterface {
|
||||
@Input() displayModel: MedicationRequestModel | null
|
||||
@Input() showDetails: boolean = true
|
||||
//these are used to populate the description of the resource. May not be available for all resources
|
||||
resourceCode?: string;
|
||||
resourceCodeSystem?: string;
|
||||
|
||||
isCollapsed: boolean = false
|
||||
|
||||
tableData: TableRowItem[] = []
|
||||
|
@ -20,6 +30,9 @@ export class MedicationRequestComponent implements OnInit, FhirResourceComponent
|
|||
|
||||
ngOnInit(): void {
|
||||
|
||||
this.resourceCode = this.displayModel?.medication_codeable_concept?.code
|
||||
this.resourceCodeSystem = this.displayModel?.medication_codeable_concept?.system
|
||||
|
||||
this.tableData = [
|
||||
{
|
||||
label: 'Medication',
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
import type { Meta, StoryObj } from '@storybook/angular';
|
||||
import {fhirVersions} from "../../../../../lib/models/constants";
|
||||
import R4Example1Json from "../../../../../lib/fixtures/r4/resources/medicationRequest/example1.json";
|
||||
import R4Example2Json from "../../../../../lib/fixtures/r4/resources/medicationRequest/example2.json";
|
||||
import R4Example3Json from "../../../../../lib/fixtures/r4/resources/medicationRequest/example3.json";
|
||||
import {MedicationModel} from "../../../../../lib/models/resources/medication-model";
|
||||
import {MedicationRequestComponent} from "./medication-request.component";
|
||||
import {MedicationRequestModel} from "../../../../../lib/models/resources/medication-request-model";
|
||||
|
||||
|
||||
|
||||
// More on how to set up stories at: https://storybook.js.org/docs/angular/writing-stories/introduction
|
||||
const meta: Meta<MedicationRequestComponent> = {
|
||||
title: 'Fhir/MedicationRequest',
|
||||
component: MedicationRequestComponent,
|
||||
decorators: [
|
||||
// moduleMetadata({
|
||||
// imports: [AppModule]
|
||||
// })
|
||||
// applicationConfig({
|
||||
// providers: [importProvidersFrom(AppModule)],
|
||||
// }),
|
||||
],
|
||||
tags: ['autodocs'],
|
||||
render: (args: MedicationRequestComponent) => ({
|
||||
props: {
|
||||
backgroundColor: null,
|
||||
...args,
|
||||
},
|
||||
}),
|
||||
argTypes: {
|
||||
displayModel: {
|
||||
control: 'object',
|
||||
},
|
||||
showDetails: {
|
||||
control: 'boolean',
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<MedicationRequestComponent>;
|
||||
|
||||
// More on writing stories with args: https://storybook.js.org/docs/angular/writing-stories/args
|
||||
let r4Example1DisplayModel = new MedicationRequestModel(R4Example1Json, fhirVersions.R4)
|
||||
r4Example1DisplayModel.source_id = '123-456-789'
|
||||
r4Example1DisplayModel.source_resource_id = '123-456-789'
|
||||
export const R4Example1: Story = {
|
||||
args: {
|
||||
displayModel: r4Example1DisplayModel
|
||||
}
|
||||
};
|
||||
|
||||
let r4Example2DisplayModel = new MedicationRequestModel(R4Example2Json, fhirVersions.R4)
|
||||
r4Example2DisplayModel.source_id = '123-456-789'
|
||||
r4Example2DisplayModel.source_resource_id = '123-456-789'
|
||||
export const R4Example2: Story = {
|
||||
args: {
|
||||
displayModel: r4Example2DisplayModel
|
||||
}
|
||||
};
|
||||
|
||||
let r4Example3DisplayModel = new MedicationRequestModel(R4Example3Json, fhirVersions.R4)
|
||||
r4Example3DisplayModel.source_id = '123-456-789'
|
||||
r4Example3DisplayModel.source_resource_id = '123-456-789'
|
||||
export const R4Example3: Story = {
|
||||
args: {
|
||||
displayModel: r4Example3DisplayModel
|
||||
}
|
||||
};
|
||||
|
|
@ -9,8 +9,7 @@ describe('MedicationComponent', () => {
|
|||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ MedicationComponent ],
|
||||
imports: [NgbCollapseModule]
|
||||
imports: [MedicationComponent, NgbCollapseModule]
|
||||
|
||||
})
|
||||
.compileComponents();
|
||||
|
|
|
@ -3,8 +3,14 @@ import {FhirResourceComponentInterface} from '../../fhir-resource/fhir-resource-
|
|||
import {TableRowItem, TableRowItemDataType} from '../../common/table/table-row-item';
|
||||
import {Router} from '@angular/router';
|
||||
import {MedicationModel} from '../../../../../lib/models/resources/medication-model';
|
||||
import {NgbCollapseModule} from "@ng-bootstrap/ng-bootstrap";
|
||||
import {CommonModule} from "@angular/common";
|
||||
import {BadgeComponent} from "../../common/badge/badge.component";
|
||||
import {TableComponent} from "../../common/table/table.component";
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
imports: [NgbCollapseModule, CommonModule, BadgeComponent, TableComponent],
|
||||
selector: 'fhir-medication',
|
||||
templateUrl: './medication.component.html',
|
||||
styleUrls: ['./medication.component.scss']
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
import type { Meta, StoryObj } from '@storybook/angular';
|
||||
import {fhirVersions} from "../../../../../lib/models/constants";
|
||||
import R4Example1Json from "../../../../../lib/fixtures/r4/resources/medication/example1.json";
|
||||
import R4Example2Json from "../../../../../lib/fixtures/r4/resources/medication/example2.json";
|
||||
import R4Example3Json from "../../../../../lib/fixtures/r4/resources/medication/example3.json";
|
||||
import {MedicationComponent} from "./medication.component";
|
||||
import {MedicationModel} from "../../../../../lib/models/resources/medication-model";
|
||||
|
||||
|
||||
|
||||
// More on how to set up stories at: https://storybook.js.org/docs/angular/writing-stories/introduction
|
||||
const meta: Meta<MedicationComponent> = {
|
||||
title: 'Fhir/Medication',
|
||||
component: MedicationComponent,
|
||||
decorators: [
|
||||
// moduleMetadata({
|
||||
// imports: [AppModule]
|
||||
// })
|
||||
// applicationConfig({
|
||||
// providers: [importProvidersFrom(AppModule)],
|
||||
// }),
|
||||
],
|
||||
tags: ['autodocs'],
|
||||
render: (args: MedicationComponent) => ({
|
||||
props: {
|
||||
backgroundColor: null,
|
||||
...args,
|
||||
},
|
||||
}),
|
||||
argTypes: {
|
||||
displayModel: {
|
||||
control: 'object',
|
||||
},
|
||||
showDetails: {
|
||||
control: 'boolean',
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<MedicationComponent>;
|
||||
|
||||
// More on writing stories with args: https://storybook.js.org/docs/angular/writing-stories/args
|
||||
let r4Example1DisplayModel = new MedicationModel(R4Example1Json, fhirVersions.R4)
|
||||
r4Example1DisplayModel.source_id = '123-456-789'
|
||||
r4Example1DisplayModel.source_resource_id = '123-456-789'
|
||||
export const R4Example1: Story = {
|
||||
args: {
|
||||
displayModel: r4Example1DisplayModel
|
||||
}
|
||||
};
|
||||
|
||||
let r4Example2DisplayModel = new MedicationModel(R4Example2Json, fhirVersions.R4)
|
||||
r4Example2DisplayModel.source_id = '123-456-789'
|
||||
r4Example2DisplayModel.source_resource_id = '123-456-789'
|
||||
export const R4Example2: Story = {
|
||||
args: {
|
||||
displayModel: r4Example2DisplayModel
|
||||
}
|
||||
};
|
||||
|
||||
let r4Example3DisplayModel = new MedicationModel(R4Example3Json, fhirVersions.R4)
|
||||
r4Example3DisplayModel.source_id = '123-456-789'
|
||||
r4Example3DisplayModel.source_resource_id = '123-456-789'
|
||||
export const R4Example3: Story = {
|
||||
args: {
|
||||
displayModel: r4Example3DisplayModel
|
||||
}
|
||||
};
|
||||
|
|
@ -9,8 +9,7 @@ describe('PractitionerComponent', () => {
|
|||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ PractitionerComponent ],
|
||||
imports: [NgbCollapseModule]
|
||||
imports: [PractitionerComponent, NgbCollapseModule]
|
||||
|
||||
})
|
||||
.compileComponents();
|
||||
|
|
|
@ -4,8 +4,14 @@ import {ImmunizationModel} from '../../../../../lib/models/resources/immunizatio
|
|||
import {TableRowItem} from '../../common/table/table-row-item';
|
||||
import {Router} from '@angular/router';
|
||||
import {PractitionerModel} from '../../../../../lib/models/resources/practitioner-model';
|
||||
import {NgbCollapseModule} from "@ng-bootstrap/ng-bootstrap";
|
||||
import {CommonModule} from "@angular/common";
|
||||
import {BadgeComponent} from "../../common/badge/badge.component";
|
||||
import {TableComponent} from "../../common/table/table.component";
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
imports: [NgbCollapseModule, CommonModule, BadgeComponent, TableComponent],
|
||||
selector: 'app-practitioner',
|
||||
templateUrl: './practitioner.component.html',
|
||||
styleUrls: ['./practitioner.component.scss']
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
import type { Meta, StoryObj } from '@storybook/angular';
|
||||
import {fhirVersions} from "../../../../../lib/models/constants";
|
||||
import R4Example1Json from "../../../../../lib/fixtures/r4/resources/practitioner/example1.json";
|
||||
import R4Example2Json from "../../../../../lib/fixtures/r4/resources/practitioner/example2.json";
|
||||
import R4Example3Json from "../../../../../lib/fixtures/r4/resources/practitioner/example3.json";
|
||||
import {PractitionerComponent} from "./practitioner.component";
|
||||
import {PractitionerModel} from "../../../../../lib/models/resources/practitioner-model";
|
||||
|
||||
|
||||
|
||||
// More on how to set up stories at: https://storybook.js.org/docs/angular/writing-stories/introduction
|
||||
const meta: Meta<PractitionerComponent> = {
|
||||
title: 'Fhir/Practitioner',
|
||||
component: PractitionerComponent,
|
||||
decorators: [
|
||||
// moduleMetadata({
|
||||
// imports: [AppModule]
|
||||
// })
|
||||
// applicationConfig({
|
||||
// providers: [importProvidersFrom(AppModule)],
|
||||
// }),
|
||||
],
|
||||
tags: ['autodocs'],
|
||||
render: (args: PractitionerComponent) => ({
|
||||
props: {
|
||||
backgroundColor: null,
|
||||
...args,
|
||||
},
|
||||
}),
|
||||
argTypes: {
|
||||
displayModel: {
|
||||
control: 'object',
|
||||
},
|
||||
showDetails: {
|
||||
control: 'boolean',
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<PractitionerComponent>;
|
||||
|
||||
// More on writing stories with args: https://storybook.js.org/docs/angular/writing-stories/args
|
||||
let r4Example1DisplayModel = new PractitionerModel(R4Example1Json, fhirVersions.R4)
|
||||
r4Example1DisplayModel.source_id = '123-456-789'
|
||||
r4Example1DisplayModel.source_resource_id = '123-456-789'
|
||||
export const R4Example1: Story = {
|
||||
args: {
|
||||
displayModel: r4Example1DisplayModel
|
||||
}
|
||||
};
|
||||
|
||||
let r4Example2DisplayModel = new PractitionerModel(R4Example2Json, fhirVersions.R4)
|
||||
r4Example2DisplayModel.source_id = '123-456-789'
|
||||
r4Example2DisplayModel.source_resource_id = '123-456-789'
|
||||
export const R4Example2: Story = {
|
||||
args: {
|
||||
displayModel: r4Example2DisplayModel
|
||||
}
|
||||
};
|
||||
|
||||
let r4Example3DisplayModel = new PractitionerModel(R4Example3Json, fhirVersions.R4)
|
||||
r4Example3DisplayModel.source_id = '123-456-789'
|
||||
r4Example3DisplayModel.source_resource_id = '123-456-789'
|
||||
export const R4Example3: Story = {
|
||||
args: {
|
||||
displayModel: r4Example3DisplayModel
|
||||
}
|
||||
};
|
||||
|
|
@ -12,10 +12,14 @@
|
|||
<!-- </div>-->
|
||||
</div>
|
||||
<div #collapse="ngbCollapse" [(ngbCollapse)]="isCollapsed" class="card-body">
|
||||
<p class="az-content-text mg-b-20">An action that is or was performed on or for a patient, practitioner, device, organization, or location. For example, this can be a physical intervention on a patient like an operation, or less invasive like long term services, counseling, or hypnotherapy.</p>
|
||||
<p class="az-content-text mg-b-20" *ngIf="!(resourceCode && resourceCodeSystem); else lookupCode">An action that is or was performed on or for a patient, practitioner, device, organization, or location. For example, this can be a physical intervention on a patient like an operation, or less invasive like long term services, counseling, or hypnotherapy.</p>
|
||||
<fhir-ui-table [displayModel]="displayModel" [tableData]="tableData"></fhir-ui-table>
|
||||
</div>
|
||||
<div *ngIf="showDetails" class="card-footer">
|
||||
<a class="float-right" routerLink="/source/{{displayModel?.source_id}}/resource/{{displayModel?.source_resource_id}}">details</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ng-template #lookupCode>
|
||||
<app-glossary-lookup class="az-content-text mg-b-20" [code]="resourceCode" [codeSystem]="resourceCodeSystem"></app-glossary-lookup>
|
||||
</ng-template>
|
||||
|
|
|
@ -10,8 +10,7 @@ describe('ProcedureComponent', () => {
|
|||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ ProcedureComponent ],
|
||||
imports: [NgbCollapseModule],
|
||||
imports: [ProcedureComponent, NgbCollapseModule],
|
||||
providers: [RouterTestingModule]
|
||||
|
||||
})
|
||||
|
|
|
@ -3,8 +3,14 @@ import {FhirResourceComponentInterface} from '../../fhir-resource/fhir-resource-
|
|||
import {TableRowItem, TableRowItemDataType} from '../../common/table/table-row-item';
|
||||
import {Router} from '@angular/router';
|
||||
import {ProcedureModel} from '../../../../../lib/models/resources/procedure-model';
|
||||
import {NgbCollapseModule} from "@ng-bootstrap/ng-bootstrap";
|
||||
import {CommonModule} from "@angular/common";
|
||||
import {BadgeComponent} from "../../common/badge/badge.component";
|
||||
import {TableComponent} from "../../common/table/table.component";
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
imports: [NgbCollapseModule, CommonModule, BadgeComponent, TableComponent],
|
||||
selector: 'fhir-procedure',
|
||||
templateUrl: './procedure.component.html',
|
||||
styleUrls: ['./procedure.component.scss']
|
||||
|
@ -12,6 +18,11 @@ import {ProcedureModel} from '../../../../../lib/models/resources/procedure-mode
|
|||
export class ProcedureComponent implements OnInit, FhirResourceComponentInterface {
|
||||
@Input() displayModel: ProcedureModel | null
|
||||
@Input() showDetails: boolean = true
|
||||
|
||||
//these are used to populate the description of the resource. May not be available for all resources
|
||||
resourceCode?: string;
|
||||
resourceCodeSystem?: string;
|
||||
|
||||
isCollapsed: boolean = false
|
||||
|
||||
tableData: TableRowItem[] = []
|
||||
|
@ -20,6 +31,15 @@ export class ProcedureComponent implements OnInit, FhirResourceComponentInterfac
|
|||
|
||||
ngOnInit(): void {
|
||||
|
||||
//medline only supports CPT procedure codes - "http://www.ama-assn.org/go/cpt", "2.16.840.1.113883.6.12"
|
||||
for(let coding of this.displayModel?.coding ?? []){
|
||||
if(coding.system == "http://www.ama-assn.org/go/cpt" || coding.system == "2.16.840.1.113883.6.12"){
|
||||
this.resourceCode = coding.code
|
||||
this.resourceCodeSystem = coding.system
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
this.tableData = [
|
||||
{
|
||||
label: 'Identification',
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
import type { Meta, StoryObj } from '@storybook/angular';
|
||||
import {fhirVersions} from "../../../../../lib/models/constants";
|
||||
import R4Example1Json from "../../../../../lib/fixtures/r4/resources/procedure/example1.json";
|
||||
import R4Example2Json from "../../../../../lib/fixtures/r4/resources/procedure/example2.json";
|
||||
import R4Example3Json from "../../../../../lib/fixtures/r4/resources/procedure/example3.json";
|
||||
import {ProcedureComponent} from "./procedure.component";
|
||||
import {ProcedureModel} from "../../../../../lib/models/resources/procedure-model";
|
||||
|
||||
|
||||
|
||||
// More on how to set up stories at: https://storybook.js.org/docs/angular/writing-stories/introduction
|
||||
const meta: Meta<ProcedureComponent> = {
|
||||
title: 'Fhir/Procedure',
|
||||
component: ProcedureComponent,
|
||||
decorators: [
|
||||
// moduleMetadata({
|
||||
// imports: [AppModule]
|
||||
// })
|
||||
// applicationConfig({
|
||||
// providers: [importProvidersFrom(AppModule)],
|
||||
// }),
|
||||
],
|
||||
tags: ['autodocs'],
|
||||
render: (args: ProcedureComponent) => ({
|
||||
props: {
|
||||
backgroundColor: null,
|
||||
...args,
|
||||
},
|
||||
}),
|
||||
argTypes: {
|
||||
displayModel: {
|
||||
control: 'object',
|
||||
},
|
||||
showDetails: {
|
||||
control: 'boolean',
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<ProcedureComponent>;
|
||||
|
||||
// More on writing stories with args: https://storybook.js.org/docs/angular/writing-stories/args
|
||||
let r4Example1DisplayModel = new ProcedureModel(R4Example1Json, fhirVersions.R4)
|
||||
r4Example1DisplayModel.source_id = '123-456-789'
|
||||
r4Example1DisplayModel.source_resource_id = '123-456-789'
|
||||
export const R4Example1: Story = {
|
||||
args: {
|
||||
displayModel: r4Example1DisplayModel
|
||||
}
|
||||
};
|
||||
|
||||
let r4Example2DisplayModel = new ProcedureModel(R4Example2Json, fhirVersions.R4)
|
||||
r4Example2DisplayModel.source_id = '123-456-789'
|
||||
r4Example2DisplayModel.source_resource_id = '123-456-789'
|
||||
export const R4Example2: Story = {
|
||||
args: {
|
||||
displayModel: r4Example2DisplayModel
|
||||
}
|
||||
};
|
||||
|
||||
let r4Example3DisplayModel = new ProcedureModel(R4Example3Json, fhirVersions.R4)
|
||||
r4Example3DisplayModel.source_id = '123-456-789'
|
||||
r4Example3DisplayModel.source_resource_id = '123-456-789'
|
||||
export const R4Example3: Story = {
|
||||
args: {
|
||||
displayModel: r4Example3DisplayModel
|
||||
}
|
||||
};
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
<div class="az-footer ht-40 page-footer fixed-bottom">
|
||||
<div class="container ht-100p pd-t-0-f">
|
||||
<div class="d-sm-flex justify-content-center justify-content-sm-between py-2 w-100">
|
||||
<span class="text-muted text-center text-sm-left d-block d-sm-inline-block">Copyright © Fasten 2022</span>
|
||||
<span class="text-muted text-center text-sm-left d-block d-sm-inline-block">Copyright © Fasten 2022 | {{appVersion}}</span>
|
||||
<span class="float-none float-sm-right d-block mt-1 mt-sm-0 text-center">Open Source personal electronic medical record system. <a href="https://www.fastenhealth.com/" target="_blank">It's your health. Own it.</a></span>
|
||||
</div>
|
||||
</div><!-- container -->
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import {versionInfo} from '../../../environments/versions';
|
||||
|
||||
@Component({
|
||||
selector: 'app-footer',
|
||||
|
@ -6,8 +7,11 @@ import { Component, OnInit } from '@angular/core';
|
|||
styleUrls: ['./footer.component.scss']
|
||||
})
|
||||
export class FooterComponent implements OnInit {
|
||||
appVersion: string;
|
||||
|
||||
constructor() { }
|
||||
constructor() {
|
||||
this.appVersion = versionInfo.version
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
|
|
@ -3,6 +3,9 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|||
import { GlossaryLookupComponent } from './glossary-lookup.component';
|
||||
import {FastenApiService} from '../../services/fasten-api.service';
|
||||
import {of} from 'rxjs';
|
||||
import {HTTP_CLIENT_TOKEN} from '../../dependency-injection';
|
||||
import {HttpClient} from '@angular/common/http';
|
||||
import {HttpClientTestingModule} from '@angular/common/http/testing';
|
||||
|
||||
describe('GlossaryLookupComponent', () => {
|
||||
let component: GlossaryLookupComponent;
|
||||
|
@ -13,11 +16,17 @@ describe('GlossaryLookupComponent', () => {
|
|||
mockedFastenApiService = jasmine.createSpyObj('FastenApiService', ['getGlossarySearchByCode'])
|
||||
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ GlossaryLookupComponent ],
|
||||
providers: [{
|
||||
imports: [ GlossaryLookupComponent, HttpClientTestingModule ],
|
||||
providers: [
|
||||
{
|
||||
provide: FastenApiService,
|
||||
useValue: mockedFastenApiService
|
||||
}]
|
||||
},
|
||||
{
|
||||
provide: HTTP_CLIENT_TOKEN,
|
||||
useClass: HttpClient,
|
||||
},
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
mockedFastenApiService.getGlossarySearchByCode.and.returnValue(of({
|
||||
|
|
|
@ -1,11 +1,17 @@
|
|||
import {Component, Input, OnInit} from '@angular/core';
|
||||
import {FastenApiService} from '../../services/fasten-api.service';
|
||||
import {DomSanitizer, SafeHtml} from '@angular/platform-browser';
|
||||
import {LoadingSpinnerComponent} from "../loading-spinner/loading-spinner.component";
|
||||
import {AuthService} from "../../services/auth.service";
|
||||
import {CommonModule} from "@angular/common";
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
imports: [LoadingSpinnerComponent, CommonModule],
|
||||
providers: [FastenApiService, AuthService],
|
||||
selector: 'app-glossary-lookup',
|
||||
templateUrl: './glossary-lookup.component.html',
|
||||
styleUrls: ['./glossary-lookup.component.scss']
|
||||
styleUrls: ['./glossary-lookup.component.scss'],
|
||||
})
|
||||
export class GlossaryLookupComponent implements OnInit {
|
||||
|
||||
|
@ -33,4 +39,5 @@ export class GlossaryLookupComponent implements OnInit {
|
|||
})
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,161 @@
|
|||
import type { Meta, StoryObj } from '@storybook/angular';
|
||||
import {GlossaryLookupComponent} from "./glossary-lookup.component";
|
||||
import {applicationConfig, moduleMetadata} from "@storybook/angular";
|
||||
import { DecoratorFunction, StoryContext } from '@storybook/types';
|
||||
import {HttpClient, HttpClientModule} from "@angular/common/http";
|
||||
import {BrowserModule} from "@angular/platform-browser";
|
||||
import {Observable, of} from "rxjs";
|
||||
import {HTTP_CLIENT_TOKEN} from "../../dependency-injection";
|
||||
|
||||
|
||||
//Use decorators to override Angular dependency injection for HttpClient
|
||||
// https://github.com/storybookjs/storybook/blob/d64e49ba1a715db73a3d6c697517fe6a85a8f8ef/examples/angular-cli/src/stories/addons/toolbars/locales/translate.service.ts
|
||||
// https://www.tektutorialshub.com/angular/injection-token-in-angular/
|
||||
// https://medium.com/ngconf/configure-your-angular-apps-with-an-injection-token-be16eee59c40
|
||||
|
||||
const withHttpClientProvider: DecoratorFunction<any> = (storyFunc, context) => {
|
||||
const { httpClientResp } = context.parameters;
|
||||
let { code, codeSystem } = context.args;
|
||||
// uses `moduleMetadata` decorator to cleanly add locale provider into module metadata
|
||||
|
||||
// It is also possible to do it directly in story with
|
||||
// ```
|
||||
// const sotry = storyFunc();
|
||||
// sotry.moduleMetadata = {
|
||||
// ...sotry.moduleMetadata,
|
||||
// providers: [
|
||||
// ...(sotry.moduleMetadata?.providers ?? []),
|
||||
// { provide: DEFAULT_LOCALE, useValue: locale },
|
||||
// ],
|
||||
// };
|
||||
// return sotry;
|
||||
// ```
|
||||
// but more verbose
|
||||
class MockHttpClient extends HttpClient {
|
||||
|
||||
get(): Observable<any> {
|
||||
// console.log("CALLED getGlossarySearchByCode in MockFastenApiService")
|
||||
return of(httpClientResp)
|
||||
}
|
||||
}
|
||||
|
||||
// console.log("Inside withHttpClientProvider DecoratorFunction", code, codeSystem)
|
||||
return moduleMetadata({ providers: [{ provide: HTTP_CLIENT_TOKEN, useClass: MockHttpClient }] })(
|
||||
storyFunc,
|
||||
context
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
// More on how to set up stories at: https://storybook.js.org/docs/angular/writing-stories/introduction
|
||||
const meta: Meta<GlossaryLookupComponent> = {
|
||||
title: 'Components/GlossaryLookup',
|
||||
component: GlossaryLookupComponent,
|
||||
decorators: [
|
||||
withHttpClientProvider,
|
||||
moduleMetadata({
|
||||
imports: [BrowserModule, HttpClientModule],
|
||||
}),
|
||||
// applicationConfig({
|
||||
// // imports: [BrowserModule, HttpClientModule],
|
||||
// providers: [{ provide: FastenApiService, useValue: MockFastenApiService }],
|
||||
// }),
|
||||
],
|
||||
tags: ['autodocs'],
|
||||
render: (args: GlossaryLookupComponent) => ({
|
||||
props: {
|
||||
backgroundColor: null,
|
||||
...args,
|
||||
},
|
||||
}),
|
||||
argTypes: {
|
||||
code: {
|
||||
control: 'text',
|
||||
},
|
||||
codeSystem: {
|
||||
control: 'text',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<GlossaryLookupComponent>;
|
||||
|
||||
// More on writing stories with args: https://storybook.js.org/docs/angular/writing-stories/args
|
||||
export const Empty: Story = {
|
||||
};
|
||||
|
||||
export const PlainText: Story = {
|
||||
args: {
|
||||
code: "36955009",
|
||||
codeSystem: "2.16.840.1.113883.6.96"
|
||||
},
|
||||
parameters: {
|
||||
httpClientResp: {
|
||||
url: "https://www.nidcr.nih.gov/health-info/taste-disorders/more-info?utm_source=medlineplus-connect&utm_medium=website&utm_campaign=mlp-connect",
|
||||
publisher: "U.S. National Library of Medicine",
|
||||
description: "Problems with the sense of taste can have a big impact on life. Taste stimulates the desire to eat and therefore plays a key role in nutrition. The sense of taste also helps keep us healthy by helping us detect spoiled food or drinks.",
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const Html: Story = {
|
||||
args: {
|
||||
code: "21000-5",
|
||||
codeSystem: "2.16.840.1.113883.6.96"
|
||||
},
|
||||
parameters: {
|
||||
httpClientResp: {
|
||||
url: "https://medlineplus.gov/lab-tests/rdw-red-cell-distribution-width?utm_source=mplusconnect&utm_medium=service",
|
||||
publisher: "U.S. National Library of Medicine",
|
||||
description: "<h2>What is a Red Cell Distribution Width Test?</h2>\n" +
|
||||
"<p>A red cell distribution width (RDW) test measures the differences in the volume and size of your red blood cells (erythrocytes). Red blood cells carry oxygen from your lungs to every cell in your body. Your cells need oxygen to grow, make new cells, and stay healthy.</p>\n" +
|
||||
"<p>Normally, your red blood cells are all about the same size. A high RDW means that there's a major difference between the size of your smallest and largest red blood cells. This may be a sign of a medical condition.</p>\n" +
|
||||
"<p>Other names: RDW-SD (standard deviation) test, Erythrocyte Distribution Width</p><h2>What is it used for?</h2>\n" +
|
||||
"<p>The RDW blood test is often part of a <a data-pid=\"728\" href=\"https://medlineplus.gov/lab-tests/complete-blood-count-cbc/\">complete blood count (CBC)</a>, a test that measures many different parts of your blood, including red cells. The RDW test is commonly used to help diagnose <a data-tid=\"139\" href=\"https://medlineplus.gov/anemia.html\">anemia</a>, a condition in which your red blood cells can't carry enough oxygen to the rest of your body.</p>\n" +
|
||||
"<p>The RDW test may also be used with other tests to help diagnose other conditions, including <a data-tid=\"4239\" href=\"https://medlineplus.gov/thalassemia.html\">thalassemia</a>, an inherited disease that can cause severe anemia.</p><h2>Why do I need an RDW test?</h2>\n" +
|
||||
"<p>Your health care provider may have ordered a complete blood count, which includes an RDW test, as part of a routine exam, or if you have:</p>\n" +
|
||||
"<ul>\n" +
|
||||
"<li>Symptoms of anemia, including weakness, <a data-tid=\"216\" href=\"https://medlineplus.gov/dizzinessandvertigo.html\">dizziness</a>, pale skin, and cold hands and feet</li>\n" +
|
||||
"<li>A family history of thalassemia, <a data-tid=\"402\" href=\"https://medlineplus.gov/sicklecelldisease.html\">sickle cell anemia</a>, or other inherited blood disorder</li>\n" +
|
||||
"<li>A chronic illness such as <a data-tid=\"119\" href=\"https://medlineplus.gov/crohnsdisease.html\">Crohn's disease</a>, diabetes, or <a data-tid=\"1\" href=\"https://medlineplus.gov/hivaids.html\">HIV/AIDS</a></li>\n" +
|
||||
"<li>A diet low in <a data-tid=\"5542\" href=\"https://medlineplus.gov/iron.html\">iron</a> and other <a data-tid=\"4298\" href=\"https://medlineplus.gov/minerals.html\">minerals</a></li>\n" +
|
||||
"<li>A long-term infection</li>\n" +
|
||||
"<li>Excessive <a data-tid=\"6039\" href=\"https://medlineplus.gov/bleeding.html\">blood loss</a> from an injury or surgical procedure</li>\n" +
|
||||
"</ul><h2>What happens during an RDW test?</h2>\n" +
|
||||
"<p>A health care professional will take a blood sample from a vein in your arm, using a small needle. After the needle is inserted, a small amount of blood will be collected into a test tube or vial. You may feel a little sting when the needle goes in or out. This process generally takes less than five minutes.</p><h2>Will I need to do anything to prepare for the test?</h2>\n" +
|
||||
"<p>No special preparation is necessary.</p><h2>Are there any risks to the test?</h2>\n" +
|
||||
"<p>There is very little risk to a blood test. You may experience slight pain or bruising at the spot where the needle was put in, but most symptoms go away quickly.</p><h2>What do the results mean?</h2>\n" +
|
||||
"<p>RDW results help your provider understand how much your red blood cells vary in size and volume. Even if your RDW results are normal, you may still have a medical condition that needs treatment. That's why your provider will usually look at your RDW results along with the results of other blood tests. The combined test results can show a more complete picture of your red blood cells to help diagnose a variety of conditions, including:</p>\n" +
|
||||
"<ul>\n" +
|
||||
"<li>Iron deficiency</li>\n" +
|
||||
"<li>Different types of anemia</li>\n" +
|
||||
"<li>Thalassemia</li>\n" +
|
||||
"<li>Sickle cell anemia</li>\n" +
|
||||
"</ul>\n" +
|
||||
"<p>A high RDW result can also be a sign of other conditions, such as:</p>\n" +
|
||||
"<ul>\n" +
|
||||
"<li>Chronic <a data-tid=\"310\" href=\"https://medlineplus.gov/liverdiseases.html\">liver disease</a></li>\n" +
|
||||
"<li><a data-tid=\"277\" href=\"https://medlineplus.gov/heartdiseases.html\">Heart disease</a></li>\n" +
|
||||
"<li><a data-tid=\"4\" href=\"https://medlineplus.gov/diabetes.html\">Diabetes</a></li>\n" +
|
||||
"<li><a data-tid=\"91\" href=\"https://medlineplus.gov/kidneydiseases.html\">Kidney disease</a></li>\n" +
|
||||
"<li><a data-tid=\"25\" href=\"https://medlineplus.gov/cancer.html\">Cancer</a>, especially <a data-tid=\"88\" href=\"https://medlineplus.gov/colorectalcancer.html\">colorectal cancer</a></li>\n" +
|
||||
"</ul>\n" +
|
||||
"<p>Your provider will most likely need more tests to confirm a diagnosis.</p>\n" +
|
||||
"<p>Learn more about <a data-pid=\"806\" href=\"https://medlineplus.gov/lab-tests/how-to-understand-your-lab-results/\">laboratory tests, reference ranges, and understanding results</a>.</p><h2>Is there anything else I need to know about a red cell distribution width test?</h2>\n" +
|
||||
"<p>If your test results indicate you have a chronic blood disorder, such as anemia, you may be put on a treatment plan to increase the amount of oxygen that your red blood cells can carry. Depending on your specific condition, your provider may recommend iron supplements, medicines, and/or changes in your diet.</p>\n" +
|
||||
"<p>Be sure to talk to your provider before taking any supplements or making any changes in your eating plan.</p><h2>References</h2>\n" +
|
||||
"<ol>\n" +
|
||||
"<li>Lee H, Kong S, Sohn Y, Shim H, Youn H, Lee S, Kim H, Eom H. Elevated Red Blood Cell Distribution Width as a Simple Prognostic Factor in Patients with Symptomatic Multiple Myeloma. Biomed Research International [Internet]. 2014 May 21 [cited 2017 Jan 24]; 2014(Article ID 145619, 8 pages). Available from: <a href=\"https://www.hindawi.com/journals/bmri/2014/145619/cta/\" target=\"bibliowin\">https://www.hindawi.com/journals/bmri/2014/145619/cta/</a></li>\n" +
|
||||
"<li>May Jori E, Marques Marisa B, Reddy Vishnu VB, Gangaraju Radhika. Three neglected numbers in the CBC: The RDW, MPV, and NRBC count. Cleveland Clinic Journal of Medicine [Internet]. 2019 Mar [cited 2021 Dec 22];86(3):167-172. Available from: <a href=\"https://www.ccjm.org/content/86/3/167 doi: 10.3949/ccjm.86a.18072\" target=\"bibliowin\">https://www.ccjm.org/content/86/3/167 doi: 10.3949/ccjm.86a.18072</a></li>\n" +
|
||||
"<li>Mayo Clinic [Internet].Mayo Foundation for Medical Education and Research; c1998-2021. Macrocytosis: What causes it?; 6 [cited 2021 Dec 22]; [about 3 screens]. Available from: <a href=\"http://www.mayoclinic.org/macrocytosis/expert-answers/faq-20058234.\" target=\"bibliowin\">http://www.mayoclinic.org/macrocytosis/expert-answers/faq-20058234.</a></li>\n" +
|
||||
"<li>National Heart, Lung, and Blood Institute [Internet]. Bethesda (MD): U.S. Department of Health and Human Services; Thalessemias; [cited 2021 Dec 22]; [about 27 screens]. Available from: <a href=\"https://www.nhlbi.nih.gov/health/health-topics/topics/thalassemia/\" target=\"bibliowin\">https://www.nhlbi.nih.gov/health/health-topics/topics/thalassemia/</a></li>\n" +
|
||||
"<li>National Heart, Lung, and Blood Institute [Internet]. Bethesda (MD): U.S. Department of Health and Human Services; Anemia: Overview; [updated 2012 May 18; cited 2021 Dec 22]; [about 2 screens]. Available from: <a href=\"https://www.nhlbi.nih.gov/health/health-topics/topics/anemia/treatment\" target=\"bibliowin\">https://www.nhlbi.nih.gov/health/health-topics/topics/anemia/treatment</a></li>\n" +
|
||||
"<li>National Heart, Lung, and Blood Institute [Internet]. Bethesda (MD): U.S. Department of Health and Human Services; Blood Tests; [updated 2012; cited 2021 Dec 22]; [about 19 screens]. Available from: <a href=\"https://www.nhlbi.nih.gov/health-topics/blood-tests\" target=\"bibliowin\">https://www.nhlbi.nih.gov/health-topics/blood-tests</a></li>\n" +
|
||||
"<li>Salvagno G, Sanchis-Gomar F, Picanza A, Lippi G. Red blood cell distribution width: A simple parameter with multiple clinical applications. Critical Reviews in Laboratory Science [Internet]. 2014 Dec 23 [cited 2017 Jan 24]; 52 (2): 86-105. Available from: <a href=\"http://www.tandfonline.com/doi/full/10.3109/10408363.2014.992064\" target=\"bibliowin\">http://www.tandfonline.com/doi/full/10.3109/10408363.2014.992064</a></li>\n" +
|
||||
"<li>Song Y, Huang Z, Kang Y, Lin Z, Lu P, Cai Z, Cao Y, ZHuX. Clinical Usefulness and Prognostic Value of Red Cell Distribution Width in Colorectal Cancer. Biomed Res Int [Internet]. 2018 Dec [cited 2019 Jan 27]; 2018 Article ID, 9858943. Available from: <a href=\"https://www.ncbi.nlm.nih.gov/pmc/articles/PMC6311266\" target=\"bibliowin\">https://www.ncbi.nlm.nih.gov/pmc/articles/PMC6311266</a></li>\n" +
|
||||
"<li>Thame M, Grandison Y, Mason K Higgs D, Morris J, Serjeant B, Serjeant G. The red cell distribution width in sickle cell disease—is it of clinical value? International Journal of Laboratory Hematology [Internet]. 1991 Sep [cited 2017 Jan 24]; 13 (3): 229-237. Available from: <a href=\"http://onlinelibrary.wiley.com/wol1/doi/10.1111/j.1365-2257.1991.tb00277.x/abstract\" target=\"bibliowin\">http://onlinelibrary.wiley.com/wol1/doi/10.1111/j.1365-2257.1991.tb00277.x/abstract</a></li>\n" +
|
||||
"</ol>",
|
||||
}
|
||||
}
|
||||
};
|
|
@ -0,0 +1,73 @@
|
|||
/**
|
||||
* gridstack-item.component.ts 7.3.0
|
||||
* Copyright (c) 2022 Alain Dumesny - see GridStack root license
|
||||
*/
|
||||
|
||||
import { ChangeDetectionStrategy, Component, ElementRef, Input, ViewChild, ViewContainerRef, OnDestroy } from '@angular/core';
|
||||
import { GridItemHTMLElement, GridStackNode } from 'gridstack';
|
||||
import {CommonModule} from '@angular/common';
|
||||
|
||||
/** store element to Ng Class pointer back */
|
||||
export interface GridItemCompHTMLElement extends GridItemHTMLElement {
|
||||
_gridItemComp?: GridstackItemComponent;
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML Component Wrapper for gridstack items, in combination with GridstackComponent for parent grid
|
||||
*/
|
||||
@Component({
|
||||
standalone: true,
|
||||
imports: [CommonModule],
|
||||
selector: 'gridstack-item',
|
||||
template: `
|
||||
<div class="grid-stack-item-content">
|
||||
<!-- this is where you would create the right component based on some internal type or id. doing .content for demo purpose -->
|
||||
{{options.content}}
|
||||
<ng-content></ng-content>
|
||||
<!-- where dynamic items go (like sub-grids) -->
|
||||
<ng-template #container></ng-template>
|
||||
</div>`,
|
||||
styles: [`
|
||||
:host { display: block; }
|
||||
`],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class GridstackItemComponent implements OnDestroy {
|
||||
|
||||
/** container to append items dynamically */
|
||||
@ViewChild('container', { read: ViewContainerRef, static: true}) public container?: ViewContainerRef;
|
||||
|
||||
/** list of options for creating/updating this item */
|
||||
@Input() public set options(val: GridStackNode) {
|
||||
if (this.el.gridstackNode?.grid) {
|
||||
// already built, do an update...
|
||||
this.el.gridstackNode.grid.update(this.el, val);
|
||||
} else {
|
||||
// store our custom element in options so we can update it and not re-create a generic div!
|
||||
val.el = this.el;
|
||||
this._options = val;
|
||||
}
|
||||
}
|
||||
/** return the latest grid options (from GS once built, otherwise initial values) */
|
||||
public get options(): GridStackNode {
|
||||
return this.el.gridstackNode || this._options || {el: this.el};
|
||||
}
|
||||
|
||||
private _options?: GridStackNode;
|
||||
|
||||
/** return the native element that contains grid specific fields as well */
|
||||
public get el(): GridItemCompHTMLElement { return this.elementRef.nativeElement; }
|
||||
|
||||
/** clears the initial options now that we've built */
|
||||
public clearOptions() {
|
||||
delete this._options;
|
||||
}
|
||||
|
||||
constructor(private readonly elementRef: ElementRef<GridItemHTMLElement>) {
|
||||
this.el._gridItemComp = this;
|
||||
}
|
||||
|
||||
public ngOnDestroy(): void {
|
||||
delete this.el._gridItemComp;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,237 @@
|
|||
/**
|
||||
* gridstack.component.ts 7.3.0
|
||||
* Copyright (c) 2022 Alain Dumesny - see GridStack root license
|
||||
*/
|
||||
|
||||
import { AfterContentInit, ChangeDetectionStrategy, Component, ContentChildren, ElementRef, EventEmitter, Input,
|
||||
NgZone, OnDestroy, OnInit, Output, QueryList, ViewChild, ViewContainerRef } from '@angular/core';
|
||||
import { Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import { GridHTMLElement, GridItemHTMLElement, GridStack, GridStackNode, GridStackOptions, GridStackWidget } from 'gridstack';
|
||||
|
||||
import { GridItemCompHTMLElement, GridstackItemComponent } from './gridstack-item.component';
|
||||
import {CommonModule} from '@angular/common';
|
||||
|
||||
/** events handlers emitters signature for different events */
|
||||
export type eventCB = {event: Event};
|
||||
export type elementCB = {event: Event, el: GridItemHTMLElement};
|
||||
export type nodesCB = {event: Event, nodes: GridStackNode[]};
|
||||
export type droppedCB = {event: Event, previousNode: GridStackNode, newNode: GridStackNode};
|
||||
|
||||
/** store element to Ng Class pointer back */
|
||||
export interface GridCompHTMLElement extends GridHTMLElement {
|
||||
_gridComp?: GridstackComponent;
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML Component Wrapper for gridstack, in combination with GridstackItemComponent for the items
|
||||
*/
|
||||
@Component({
|
||||
standalone: true,
|
||||
imports: [CommonModule, GridstackItemComponent],
|
||||
selector: 'gridstack',
|
||||
template: `
|
||||
<!-- content to show when when grid is empty, like instructions on how to add widgets -->
|
||||
<ng-content select="[empty-content]" *ngIf="isEmpty"></ng-content>
|
||||
<!-- where dynamic items go -->
|
||||
<ng-template #container></ng-template>
|
||||
<!-- where template items go -->
|
||||
<ng-content></ng-content>
|
||||
`,
|
||||
styles: [`
|
||||
:host { display: block; }
|
||||
`],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class GridstackComponent implements OnInit, AfterContentInit, OnDestroy {
|
||||
|
||||
/** track list of TEMPLATE grid items so we can sync between DOM and GS internals */
|
||||
@ContentChildren(GridstackItemComponent) public gridstackItems?: QueryList<GridstackItemComponent>;
|
||||
/** container to append items dynamically */
|
||||
@ViewChild('container', { read: ViewContainerRef, static: true}) public container?: ViewContainerRef;
|
||||
|
||||
/** initial options for creation of the grid */
|
||||
@Input() public set options(val: GridStackOptions) { this._options = val; }
|
||||
/** return the current running options */
|
||||
public get options(): GridStackOptions { return this._grid?.opts || this._options || {}; }
|
||||
|
||||
/** true while ng-content with 'no-item-content' should be shown when last item is removed from a grid */
|
||||
@Input() public isEmpty?: boolean;
|
||||
|
||||
/** individual list of GridStackEvent callbacks handlers as output
|
||||
* otherwise use this.grid.on('name1 name2 name3', callback) to handle multiple at once
|
||||
* see https://github.com/gridstack/gridstack.js/blob/master/demo/events.js#L4
|
||||
*
|
||||
* Note: camel casing and 'CB' added at the end to prevent @angular-eslint/no-output-native
|
||||
* eg: 'change' would trigger the raw CustomEvent so use different name.
|
||||
*/
|
||||
@Output() public addedCB = new EventEmitter<nodesCB>();
|
||||
@Output() public changeCB = new EventEmitter<nodesCB>();
|
||||
@Output() public disableCB = new EventEmitter<eventCB>();
|
||||
@Output() public dragCB = new EventEmitter<elementCB>();
|
||||
@Output() public dragStartCB = new EventEmitter<elementCB>();
|
||||
@Output() public dragStopCB = new EventEmitter<elementCB>();
|
||||
@Output() public droppedCB = new EventEmitter<droppedCB>();
|
||||
@Output() public enableCB = new EventEmitter<eventCB>();
|
||||
@Output() public removedCB = new EventEmitter<nodesCB>();
|
||||
@Output() public resizeCB = new EventEmitter<elementCB>();
|
||||
@Output() public resizeStartCB = new EventEmitter<elementCB>();
|
||||
@Output() public resizeStopCB = new EventEmitter<elementCB>();
|
||||
|
||||
/** return the native element that contains grid specific fields as well */
|
||||
public get el(): GridCompHTMLElement { return this.elementRef.nativeElement; }
|
||||
|
||||
/** return the GridStack class */
|
||||
public get grid(): GridStack | undefined { return this._grid; }
|
||||
|
||||
private _options?: GridStackOptions;
|
||||
private _grid?: GridStack;
|
||||
private loaded?: boolean;
|
||||
private ngUnsubscribe: Subject<void> = new Subject();
|
||||
|
||||
constructor(
|
||||
private readonly zone: NgZone,
|
||||
private readonly elementRef: ElementRef<GridCompHTMLElement>,
|
||||
) {
|
||||
this.el._gridComp = this;
|
||||
}
|
||||
|
||||
public ngOnInit(): void {
|
||||
// inject our own addRemove so we can create GridItemComponent instead of simple divs
|
||||
const opts: GridStackOptions = this._options || {};
|
||||
opts.addRemoveCB = GridstackComponent._addRemoveCB;
|
||||
|
||||
// init ourself before any template children are created since we track them below anyway - no need to double create+update widgets
|
||||
this.loaded = !!this.options?.children?.length;
|
||||
this._grid = GridStack.init(opts, this.el);
|
||||
delete this._options; // GS has it now
|
||||
}
|
||||
|
||||
/** wait until after all DOM is ready to init gridstack children (after angular ngFor and sub-components run first) */
|
||||
public ngAfterContentInit(): void {
|
||||
this.zone.runOutsideAngular(() => {
|
||||
// track whenever the children list changes and update the layout...
|
||||
this.gridstackItems?.changes
|
||||
.pipe(takeUntil(this.ngUnsubscribe))
|
||||
.subscribe(() => this.updateAll());
|
||||
// ...and do this once at least unless we loaded children already
|
||||
if (!this.loaded) this.updateAll();
|
||||
this.hookEvents(this.grid);
|
||||
});
|
||||
}
|
||||
|
||||
public ngOnDestroy(): void {
|
||||
this.ngUnsubscribe.next();
|
||||
this.ngUnsubscribe.complete();
|
||||
this.grid?.destroy();
|
||||
delete this._grid;
|
||||
delete this.el._gridComp;
|
||||
}
|
||||
|
||||
/**
|
||||
* called when the TEMPLATE list of items changes - get a list of nodes and
|
||||
* update the layout accordingly (which will take care of adding/removing items changed by Angular)
|
||||
*/
|
||||
public updateAll() {
|
||||
if (!this.grid) return;
|
||||
const layout: GridStackWidget[] = [];
|
||||
this.gridstackItems?.forEach(item => {
|
||||
layout.push(item.options);
|
||||
item.clearOptions();
|
||||
});
|
||||
this.grid.load(layout); // efficient that does diffs only
|
||||
}
|
||||
|
||||
/** check if the grid is empty, if so show alternative content */
|
||||
public checkEmpty() {
|
||||
if (!this.grid) return;
|
||||
this.isEmpty = !this.grid.engine.nodes.length;
|
||||
}
|
||||
|
||||
/** get all known events as easy to use Outputs for convenience */
|
||||
private hookEvents(grid?: GridStack) {
|
||||
if (!grid) return;
|
||||
grid
|
||||
.on('added', (event: Event, nodes: GridStackNode[]) => this.zone.run(() => { this.checkEmpty(); this.addedCB.emit({event, nodes}); }))
|
||||
.on('change', (event: Event, nodes: GridStackNode[]) => this.zone.run(() => this.changeCB.emit({event, nodes})))
|
||||
.on('disable', (event: Event) => this.zone.run(() => this.disableCB.emit({event})))
|
||||
.on('drag', (event: Event, el: GridItemHTMLElement) => this.zone.run(() => this.dragCB.emit({event, el})))
|
||||
.on('dragstart', (event: Event, el: GridItemHTMLElement) => this.zone.run(() => this.dragStartCB.emit({event, el})))
|
||||
.on('dragstop', (event: Event, el: GridItemHTMLElement) => this.zone.run(() => this.dragStopCB.emit({event, el})))
|
||||
.on('dropped', (event: Event, previousNode: GridStackNode, newNode: GridStackNode) => this.zone.run(() => this.droppedCB.emit({event, previousNode, newNode})))
|
||||
.on('enable', (event: Event) => this.zone.run(() => this.enableCB.emit({event})))
|
||||
.on('removed', (event: Event, nodes: GridStackNode[]) => this.zone.run(() => { this.checkEmpty(); this.removedCB.emit({event, nodes}); }))
|
||||
.on('resize', (event: Event, el: GridItemHTMLElement) => this.zone.run(() => this.resizeCB.emit({event, el})))
|
||||
.on('resizestart', (event: Event, el: GridItemHTMLElement) => this.zone.run(() => this.resizeStartCB.emit({event, el})))
|
||||
.on('resizestop', (event: Event, el: GridItemHTMLElement) => this.zone.run(() => this.resizeStopCB.emit({event, el})))
|
||||
}
|
||||
|
||||
/** called by GS when a new item needs to be created, which we do as a Angular component, or deleted (skip) */
|
||||
private static _addRemoveCB(parent: GridCompHTMLElement | HTMLElement, w: GridStackWidget | GridStackOptions, add: boolean, isGrid: boolean): HTMLElement | undefined {
|
||||
if (add) {
|
||||
if (!parent) return;
|
||||
// create the grid item dynamically - see https://angular.io/docs/ts/latest/cookbook/dynamic-component-loader.html
|
||||
if (isGrid) {
|
||||
const gridItemComp = (parent.parentElement as GridItemCompHTMLElement)._gridItemComp;
|
||||
const grid = gridItemComp?.container?.createComponent(GridstackComponent)?.instance;
|
||||
if (grid) grid.options = w as GridStackOptions;
|
||||
return grid?.el;
|
||||
} else {
|
||||
// TODO: use GridStackWidget to define what type of component to create as child, or do it in GridstackItemComponent template...
|
||||
const gridComp = (parent as GridCompHTMLElement)._gridComp;
|
||||
const gridItem = gridComp?.container?.createComponent(GridstackItemComponent)?.instance;
|
||||
return gridItem?.el;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// /**
|
||||
// * Simplest Angular Example using GridStack API directly
|
||||
// */
|
||||
// import { Component, OnInit } from '@angular/core';
|
||||
//
|
||||
// import { GridStack, GridStackWidget } from 'gridstack';
|
||||
//
|
||||
// @Component({
|
||||
// selector: 'gridstack',
|
||||
// template: `
|
||||
// <p><b>SIMPLEST</b>: angular example using GridStack API directly, so not really using any angular construct per say other than waiting for DOM rendering</p>
|
||||
// <button (click)="add()">add item</button>
|
||||
// <button (click)="delete()">remove item</button>
|
||||
// <button (click)="change()">modify item</button>
|
||||
// <div class="grid-stack"></div>
|
||||
// `,
|
||||
// // gridstack.min.css and other custom styles should be included in global styles.scss
|
||||
// })
|
||||
// export class GridstackComponent implements OnInit {
|
||||
// public items: GridStackWidget[] = [
|
||||
// { x: 0, y: 3, w: 12, h: 6, content: '0' },
|
||||
// { x: 0, y: 0, w: 4, h: 3, content: '1' },
|
||||
// { x: 4, y: 0, w: 4, h: 3, content: '2' },
|
||||
// { x: 8, y: 0, w: 4, h: 3, content: '3' },
|
||||
// ];
|
||||
// private grid!: GridStack;
|
||||
//
|
||||
// constructor() {}
|
||||
//
|
||||
// // simple div above doesn't require Angular to run, so init gridstack here
|
||||
// public ngOnInit() {
|
||||
// this.grid = GridStack.init({
|
||||
// cellHeight: 70,
|
||||
// })
|
||||
// .load(this.items); // and load our content directly (will create DOM)
|
||||
// }
|
||||
//
|
||||
// public add() {
|
||||
// this.grid.addWidget({w: 3, content: 'new content'});
|
||||
// }
|
||||
// public delete() {
|
||||
// this.grid.removeWidget(this.grid.engine.nodes[0].el!);
|
||||
// }
|
||||
// public change() {
|
||||
// this.grid.update(this.grid.engine.nodes[0].el!, {w: 1});
|
||||
// }
|
||||
// }
|
|
@ -4,6 +4,8 @@ import { HeaderComponent } from './header.component';
|
|||
import {HttpClientTestingModule} from '@angular/common/http/testing';
|
||||
import {RouterModule} from '@angular/router';
|
||||
import {RouterTestingModule} from '@angular/router/testing';
|
||||
import {HTTP_CLIENT_TOKEN} from '../../dependency-injection';
|
||||
import {HttpClient} from '@angular/common/http';
|
||||
|
||||
describe('HeaderComponent', () => {
|
||||
let component: HeaderComponent;
|
||||
|
@ -12,7 +14,13 @@ describe('HeaderComponent', () => {
|
|||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [ HttpClientTestingModule, RouterTestingModule, RouterModule ],
|
||||
declarations: [ HeaderComponent ]
|
||||
declarations: [ HeaderComponent ],
|
||||
providers: [
|
||||
{
|
||||
provide: HTTP_CLIENT_TOKEN,
|
||||
useClass: HttpClient,
|
||||
},
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<br/>
|
||||
You can click on a row to see the raw data.
|
||||
<br/>
|
||||
If you have any feedback regarding the data displayed, please <a href="https://github.com/fastenhealth/docs/issues">file a ticket <i class="fab fa-github"></i></a>
|
||||
If you have any feedback regarding the data displayed, please <a href="https://github.com/fastenhealth/fasten-onprem/issues">file a ticket <i class="fab fa-github"></i></a>
|
||||
</div>
|
||||
|
||||
<ngx-datatable
|
||||
|
|
|
@ -8,7 +8,7 @@ describe('LoadingSpinnerComponent', () => {
|
|||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ LoadingSpinnerComponent ]
|
||||
imports: [ LoadingSpinnerComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import {Component, Input, OnInit} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
selector: 'app-loading-spinner',
|
||||
templateUrl: './loading-spinner.component.html',
|
||||
styleUrls: ['./loading-spinner.component.scss']
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
import type { Meta, StoryObj } from '@storybook/angular';
|
||||
import {LoadingSpinnerComponent} from './loading-spinner.component';
|
||||
|
||||
// More on how to set up stories at: https://storybook.js.org/docs/angular/writing-stories/introduction
|
||||
const meta: Meta<LoadingSpinnerComponent> = {
|
||||
title: 'Components/LoadingSpinner',
|
||||
component: LoadingSpinnerComponent,
|
||||
decorators: [
|
||||
// moduleMetadata({
|
||||
// imports: [AppModule]
|
||||
// })
|
||||
// applicationConfig({
|
||||
// providers: [importProvidersFrom(AppModule)],
|
||||
// }),
|
||||
],
|
||||
tags: ['autodocs'],
|
||||
render: (args: LoadingSpinnerComponent) => ({
|
||||
props: {
|
||||
backgroundColor: null,
|
||||
...args,
|
||||
},
|
||||
}),
|
||||
argTypes: {
|
||||
loadingTitle: {
|
||||
control: 'text',
|
||||
},
|
||||
loadingSubTitle: {
|
||||
control: 'text',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<LoadingSpinnerComponent>;
|
||||
|
||||
// More on writing stories with args: https://storybook.js.org/docs/angular/writing-stories/args
|
||||
export const Primary: Story = {};
|
||||
|
||||
export const Secondary: Story = {
|
||||
args: {
|
||||
loadingTitle: "Custom loading title",
|
||||
},
|
||||
};
|
||||
|
|
@ -12,6 +12,8 @@ import {DiagnosticReportModel} from '../../../lib/models/resources/diagnostic-re
|
|||
import {FastenDisplayModel} from '../../../lib/models/fasten/fasten-display-model';
|
||||
import * as _ from "lodash";
|
||||
import {ConditionModel} from '../../../lib/models/resources/condition-model';
|
||||
import {RecResourceRelatedDisplayModel} from '../../../lib/utils/resource_related_display_model';
|
||||
import {CodingModel} from '../../../lib/models/datatypes/coding-model';
|
||||
|
||||
@Component({
|
||||
selector: 'app-report-medical-history-condition',
|
||||
|
@ -71,7 +73,9 @@ export class ReportMedicalHistoryConditionComponent implements OnInit {
|
|||
}
|
||||
|
||||
//add resources to the lookup table, ensure uniqueness.
|
||||
this.conditionDisplayModel = this.recExtractResources(this.conditionGroup)
|
||||
let result = RecResourceRelatedDisplayModel(this.conditionGroup)
|
||||
this.resourcesLookup = result.resourcesLookup
|
||||
this.conditionDisplayModel = result.displayModel
|
||||
|
||||
let involvedInCareMap: {[resource_id: string]: {displayName: string, role?: string, email?: string}} = {}
|
||||
|
||||
|
@ -99,7 +103,7 @@ export class ReportMedicalHistoryConditionComponent implements OnInit {
|
|||
|
||||
let telecomEmails =_.find(practitionerModel.telecom, {"system": "email"})
|
||||
let email = _.get(telecomEmails, '[0].value')
|
||||
let qualification = _.find(practitionerModel.qualification, {"system": "http://nucc.org/provider-taxonomy"})
|
||||
let qualification = _.find(practitionerModel.qualification, {"system": "http://nucc.org/provider-taxonomy"}) as CodingModel
|
||||
|
||||
involvedInCareMap[id] = _.mergeWith(
|
||||
{},
|
||||
|
@ -138,42 +142,4 @@ export class ReportMedicalHistoryConditionComponent implements OnInit {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
This function flattens all resources
|
||||
*/
|
||||
recExtractResources(resource: ResourceFhir): FastenDisplayModel{
|
||||
let resourceId = this.genResourceId(resource)
|
||||
let resourceDisplayModel: FastenDisplayModel = this.resourcesLookup[resourceId]
|
||||
|
||||
//ensure display model is populated
|
||||
if(!resourceDisplayModel){
|
||||
try{
|
||||
resourceDisplayModel = fhirModelFactory(resource?.source_resource_type as ResourceType, resource)
|
||||
this.resourcesLookup[resourceId] = resourceDisplayModel
|
||||
}catch(e){
|
||||
console.error(e) //failed to parse a model
|
||||
return null
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(!resource.related_resources){
|
||||
return resourceDisplayModel
|
||||
} else {
|
||||
for(let relatedResource of resource.related_resources){
|
||||
resourceDisplayModel.related_resources[relatedResource.source_resource_type] = resourceDisplayModel.related_resources[relatedResource.source_resource_type] || []
|
||||
|
||||
let relatedResourceDisplayModel = this.recExtractResources(relatedResource)
|
||||
if(relatedResourceDisplayModel){
|
||||
resourceDisplayModel.related_resources[relatedResource.source_resource_type].push(relatedResourceDisplayModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
return resourceDisplayModel
|
||||
}
|
||||
|
||||
genResourceId(relatedResource: ResourceFhir): string {
|
||||
return `/source/${relatedResource?.source_id}/resource/${relatedResource?.source_resource_type}/${relatedResource?.source_resource_id}`
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,145 @@
|
|||
<div class="card card-dashboard-seven mb-3">
|
||||
<div class="card-header tx-medium">
|
||||
<div class="row cursor-pointer" routerLink="/source/{{eobDisplayModel?.source_id}}/resource/{{eobDisplayModel?.source_resource_id}}">
|
||||
<!-- Condition Header -->
|
||||
<div class="col-6">
|
||||
{{eobDisplayModel?.sort_title ? eobDisplayModel?.sort_title : condition?.display ? condition?.display : 'unknown'}}
|
||||
</div>
|
||||
<div class="col-6">
|
||||
{{eobDisplayModel?.billablePeriod?.start | date }} <span *ngIf="eobDisplayModel?.billablePeriod?.end">- {{eobDisplayModel?.billablePeriod?.end | date}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- card-header -->
|
||||
<div class="card-body">
|
||||
|
||||
<div class="row">
|
||||
<!-- Condition Details -->
|
||||
<!-- {{conditionDisplayModel | json}}-->
|
||||
<div class="col-6 mb-2">
|
||||
|
||||
<div *ngIf="involvedInCare.length > 0" class="row pl-3">
|
||||
<div class="col-12 mt-3 mb-2 tx-indigo">
|
||||
<p>Involved in Care</p>
|
||||
</div>
|
||||
<ng-container *ngFor="let practitioner of involvedInCare">
|
||||
<div class="col-6">
|
||||
<strong>{{practitioner.displayName}}</strong>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
{{practitioner.role}}
|
||||
<!-- TODO: add email address link here -->
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
|
||||
<div *ngIf="condition" class="col-12 mt-3 mb-2">
|
||||
<p class="tx-indigo">Definition</p>
|
||||
<app-glossary-lookup [code]="condition?.code" [codeSystem]="condition?.system"></app-glossary-lookup>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row pt-2" *ngIf="explanationOfBenefitGroup?.related_resources?.length > 0">
|
||||
<div class="col-12">
|
||||
<a class="cursor-pointer tx-indigo" (click)="collapse.toggle()">show all</a>
|
||||
<div #collapse="ngbCollapse" [ngbCollapse]="true">
|
||||
|
||||
<ul>
|
||||
<li class="cursor-pointer tx-indigo" *ngFor="let resourceEntry of resourcesLookup | keyvalue" [routerLink]="resourceEntry.key">{{resourceEntry.value.source_resource_type}} {{resourceEntry.value.sort_title ? '- '+resourceEntry.value.sort_title : '' }} </li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<div class="col-6 bg-gray-100">
|
||||
<div class="row">
|
||||
|
||||
<div class="col-6 mt-3 mb-2 tx-indigo">
|
||||
<strong>{{eobDisplayModel?.billablePeriod?.start | date}}</strong>
|
||||
</div>
|
||||
<div class="col-6 mt-3 mb-2 tx-indigo">
|
||||
<small>{{locations?.[0]?.name || 'unknown'}}</small>
|
||||
</div>
|
||||
|
||||
<div *ngIf="eobDisplayModel?.related_resources?.MedicationRequest || eobDisplayModel?.related_resources?.Medication" class="col-12 mt-2 mb-2">
|
||||
<strong>Medications:</strong>
|
||||
<ul>
|
||||
<li class="cursor-pointer" [ngbPopover]="medicationRequestPopoverContent" placement="top-left" popoverClass="card-fhir-resource-popover" *ngFor="let medication of eobDisplayModel?.related_resources?.MedicationRequest">
|
||||
{{medication.display }}
|
||||
|
||||
<ng-template #medicationRequestPopoverContent>
|
||||
<fhir-resource [displayModel]="medication"></fhir-resource>
|
||||
</ng-template>
|
||||
</li>
|
||||
<li class="cursor-pointer" [ngbPopover]="medicationPopoverContent" placement="top-left" popoverClass="card-fhir-resource-popover" *ngFor="let medication of eobDisplayModel?.related_resources?.Medication">
|
||||
{{medication.title}}
|
||||
|
||||
<ng-template #medicationPopoverContent>
|
||||
<fhir-resource [displayModel]="medication"></fhir-resource>
|
||||
</ng-template>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div *ngIf="procedures.length > 0" class="col-12 mt-2 mb-2">
|
||||
<strong>Procedures:</strong>
|
||||
<ul>
|
||||
<li class="cursor-pointer" [ngbPopover]="procedurePopoverContent" placement="top-left" popoverClass="card-fhir-resource-popover" *ngFor="let procedure of procedures">
|
||||
{{procedure.display}}
|
||||
|
||||
<ng-template #procedurePopoverContent>
|
||||
<fhir-resource [displayModel]="procedure"></fhir-resource>
|
||||
</ng-template>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div *ngIf="eobDisplayModel?.related_resources?.DiagnosticReport as diagnosticReports" class="col-12 mt-2 mb-2">
|
||||
<strong>Tests and Examinations:</strong>
|
||||
<ul>
|
||||
<li class="cursor-pointer" [ngbPopover]="diagnosticReportPopoverContent" placement="top-left" popoverClass="card-fhir-resource-popover" *ngFor="let diagnosticReport of diagnosticReports">
|
||||
{{diagnosticReport.title}}
|
||||
|
||||
<ng-template #diagnosticReportPopoverContent>
|
||||
<fhir-resource [displayModel]="diagnosticReport"></fhir-resource>
|
||||
</ng-template>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div *ngIf="eobDisplayModel?.related_resources?.DocumentReference as documentReferences" class="col-12 mt-2 mb-2">
|
||||
<strong>Attachments:</strong>
|
||||
<ul>
|
||||
<li class="cursor-pointer" [ngbPopover]="documentReferencePopoverContent" placement="top-left" popoverClass="card-fhir-resource-popover" *ngFor="let documentReference of documentReferences">
|
||||
{{documentReference.sort_title}}
|
||||
|
||||
<ng-template #documentReferencePopoverContent>
|
||||
<fhir-resource [displayModel]="documentReference"></fhir-resource>
|
||||
</ng-template>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div *ngIf="eobDisplayModel?.related_resources?.Device as devices" class="col-12 mt-2 mb-2">
|
||||
<strong>Device:</strong>
|
||||
<ul>
|
||||
<li routerLink="/source/{{device?.source_id}}/resource/{{device?.source_resource_id}}" *ngFor="let device of devices">
|
||||
{{device.model}}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div><!-- card-body -->
|
||||
</div>
|
|
@ -0,0 +1,23 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ReportMedicalHistoryExplanationOfBenefitComponent } from './report-medical-history-explanation-of-benefit.component';
|
||||
|
||||
describe('ReportMedicalHistoryExplanationOfBenefitComponent', () => {
|
||||
let component: ReportMedicalHistoryExplanationOfBenefitComponent;
|
||||
let fixture: ComponentFixture<ReportMedicalHistoryExplanationOfBenefitComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ ReportMedicalHistoryExplanationOfBenefitComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(ReportMedicalHistoryExplanationOfBenefitComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,161 @@
|
|||
import {Component, Input, OnInit} from '@angular/core';
|
||||
import {ResourceFhir} from '../../models/fasten/resource_fhir';
|
||||
import {FastenDisplayModel} from '../../../lib/models/fasten/fasten-display-model';
|
||||
import {ResourceType} from '../../../lib/models/constants';
|
||||
import {CareTeamModel} from '../../../lib/models/resources/care-team-model';
|
||||
import {PractitionerModel} from '../../../lib/models/resources/practitioner-model';
|
||||
import {RecResourceRelatedDisplayModel} from '../../../lib/utils/resource_related_display_model';
|
||||
import {EncounterModel} from '../../../lib/models/resources/encounter-model';
|
||||
import * as _ from "lodash";
|
||||
import {ExplanationOfBenefitModel} from '../../../lib/models/resources/explanation-of-benefit-model';
|
||||
import {MedicationModel} from '../../../lib/models/resources/medication-model';
|
||||
import {ProcedureModel} from '../../../lib/models/resources/procedure-model';
|
||||
import {DiagnosticReportModel} from '../../../lib/models/resources/diagnostic-report-model';
|
||||
import {DeviceModel} from '../../../lib/models/resources/device-model';
|
||||
import {CodingModel} from '../../../lib/models/datatypes/coding-model';
|
||||
import {LocationModel} from '../../../lib/models/resources/location-model';
|
||||
|
||||
@Component({
|
||||
selector: 'app-report-medical-history-explanation-of-benefit',
|
||||
templateUrl: './report-medical-history-explanation-of-benefit.component.html',
|
||||
styleUrls: ['./report-medical-history-explanation-of-benefit.component.scss']
|
||||
})
|
||||
export class ReportMedicalHistoryExplanationOfBenefitComponent implements OnInit {
|
||||
|
||||
@Input() explanationOfBenefitGroup: ResourceFhir
|
||||
|
||||
eobDisplayModel: Partial<ExplanationOfBenefitModel>
|
||||
|
||||
//lookup table for all resources
|
||||
resourcesLookup: {[name:string]: FastenDisplayModel} = {}
|
||||
|
||||
condition: CodingModel
|
||||
|
||||
//EOB embeds multiple resource type references
|
||||
involvedInCare: {displayName: string, role?: string, email?: string}[] = []
|
||||
locations: LocationModel[] = []
|
||||
encounters: EncounterModel[] = []
|
||||
medications: {[resourceId: string]: MedicationModel[]} = {}
|
||||
procedures: ProcedureModel[] = []
|
||||
diagnosticReports: {[encounterResourceId: string]: DiagnosticReportModel[]} = {}
|
||||
device: {[encounterResourceId: string]: DeviceModel[]} = {}
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
if(!this.explanationOfBenefitGroup){
|
||||
return
|
||||
}
|
||||
|
||||
//add resources to the lookup table, ensure uniqueness.
|
||||
let result = RecResourceRelatedDisplayModel(this.explanationOfBenefitGroup)
|
||||
this.resourcesLookup = result.resourcesLookup
|
||||
this.eobDisplayModel = result.displayModel
|
||||
|
||||
|
||||
console.log("Resources Lookup", this.resourcesLookup)
|
||||
|
||||
let involvedInCareMap: {[resource_id: string]: {displayName: string, role?: string, email?: string}} = {}
|
||||
|
||||
//extract data from EOB directly
|
||||
this.condition = this.eobDisplayModel.diagnosis?.[0]?.diagnosisCodeableConcept?.coding?.[0]
|
||||
this.eobDisplayModel.careTeam?.forEach((careTeam) => {
|
||||
if(careTeam.provider.reference){
|
||||
return
|
||||
}
|
||||
let id = careTeam.role + careTeam.provider.display
|
||||
involvedInCareMap[id] = _.mergeWith(
|
||||
{},
|
||||
involvedInCareMap[id],
|
||||
{
|
||||
displayName: careTeam.provider?.display,
|
||||
role: careTeam.role?.[0]?.text,
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
this.eobDisplayModel.procedures?.forEach((procedure) => {
|
||||
let procedureModel = new ProcedureModel({})
|
||||
procedureModel.performed_datetime = procedure.date
|
||||
procedureModel.coding = procedure.procedureCodeableConcept.coding
|
||||
procedureModel.display = procedure.procedureCodeableConcept.text || procedure.procedureCodeableConcept.coding?.[0]?.display
|
||||
this.procedures.push(procedureModel)
|
||||
})
|
||||
|
||||
console.log("CONDITION", this.condition)
|
||||
|
||||
// this.medications = this.eobDisplayModel.prescription
|
||||
|
||||
|
||||
//loop though all resources, process display data
|
||||
for(let resourceId in this.resourcesLookup){
|
||||
let resource = this.resourcesLookup[resourceId]
|
||||
|
||||
switch(resource.source_resource_type){
|
||||
case ResourceType.CareTeam:
|
||||
for(let participant of (resource as CareTeamModel).participants){
|
||||
let id = participant.reference.reference || participant.display
|
||||
involvedInCareMap[id] = _.mergeWith(
|
||||
{},
|
||||
involvedInCareMap[id],
|
||||
{
|
||||
displayName: participant.display,
|
||||
role: participant.role
|
||||
},
|
||||
)
|
||||
}
|
||||
break
|
||||
case ResourceType.Practitioner:
|
||||
let practitionerModel = resource as PractitionerModel
|
||||
let id = `${resource.source_resource_type}/${resource.source_resource_id}`
|
||||
|
||||
let telecomEmails = _.find(practitionerModel.telecom, {"system": "email"})
|
||||
let email = _.get(telecomEmails, '[0].value')
|
||||
let qualification = _.find(practitionerModel.qualification, {"system": "http://nucc.org/provider-taxonomy"}) as CodingModel
|
||||
|
||||
involvedInCareMap[id] = _.mergeWith(
|
||||
{},
|
||||
involvedInCareMap[id],
|
||||
{
|
||||
displayName: practitionerModel.name?.family && practitionerModel.name?.given ? `${practitionerModel.name?.family }, ${practitionerModel.name?.given}` : practitionerModel.name?.text,
|
||||
role: qualification?.display || practitionerModel.name?.prefix || practitionerModel.name?.suffix,
|
||||
email: email,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
break
|
||||
case ResourceType.Encounter:
|
||||
this.encounters.push(resource as EncounterModel);
|
||||
(resource as EncounterModel).participant?.map((participant) => {
|
||||
let id = participant.reference.reference
|
||||
involvedInCareMap[id] = _.mergeWith(
|
||||
{},
|
||||
involvedInCareMap[id],
|
||||
{
|
||||
displayName: participant.display,
|
||||
role: participant.role,
|
||||
},
|
||||
)
|
||||
})
|
||||
break
|
||||
case ResourceType.Location:
|
||||
this.locations.push(resource as LocationModel)
|
||||
// case ResourceType.ExplanationOfBenefit:
|
||||
// let eobDisplayModel = (resource as ExplanationOfBenefitModel)
|
||||
// involvedInCareMap[eobDisplayModel.provider.reference] =
|
||||
break
|
||||
case ResourceType.Procedure:
|
||||
this.procedures.push(resource as ProcedureModel)
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
console.log("GENERATED INVOLVED IN CARE MAP", involvedInCareMap)
|
||||
for(let resourceId in involvedInCareMap){
|
||||
this.involvedInCare.push(involvedInCareMap[resourceId])
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -3,6 +3,9 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|||
import { ResourceListComponent } from './resource-list.component';
|
||||
import {HttpClientTestingModule} from '@angular/common/http/testing';
|
||||
import {ResourceListOutletDirective} from './resource-list-outlet.directive';
|
||||
import {FastenApiService} from '../../services/fasten-api.service';
|
||||
import {HTTP_CLIENT_TOKEN} from '../../dependency-injection';
|
||||
import {HttpClient} from '@angular/common/http';
|
||||
|
||||
describe('ResourceListComponent', () => {
|
||||
let component: ResourceListComponent;
|
||||
|
@ -11,7 +14,14 @@ describe('ResourceListComponent', () => {
|
|||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [HttpClientTestingModule],
|
||||
declarations: [ ResourceListComponent, ResourceListOutletDirective ]
|
||||
declarations: [ ResourceListComponent, ResourceListOutletDirective ],
|
||||
providers: [
|
||||
FastenApiService,
|
||||
{
|
||||
provide: HTTP_CLIENT_TOKEN,
|
||||
useClass: HttpClient,
|
||||
},
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
|
|
|
@ -68,6 +68,9 @@ import { DocumentReferenceComponent } from './fhir/resources/document-reference/
|
|||
import { DicomComponent } from './fhir/datatypes/dicom/dicom.component';
|
||||
import { MediaComponent } from './fhir/resources/media/media.component';
|
||||
import { GlossaryLookupComponent } from './glossary-lookup/glossary-lookup.component';
|
||||
import { ReportMedicalHistoryExplanationOfBenefitComponent } from './report-medical-history-explanation-of-benefit/report-medical-history-explanation-of-benefit.component';
|
||||
import {GridstackComponent} from './gridstack/gridstack.component';
|
||||
import {GridstackItemComponent} from './gridstack/gridstack-item.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
|
@ -83,6 +86,29 @@ import { GlossaryLookupComponent } from './glossary-lookup/glossary-lookup.compo
|
|||
ChartsModule,
|
||||
HighlightModule,
|
||||
PipesModule,
|
||||
|
||||
//standalone components
|
||||
LoadingSpinnerComponent,
|
||||
GlossaryLookupComponent,
|
||||
BadgeComponent,
|
||||
TableComponent,
|
||||
CodingComponent,
|
||||
AllergyIntoleranceComponent,
|
||||
MedicationComponent,
|
||||
MedicationRequestComponent,
|
||||
PractitionerComponent,
|
||||
ProcedureComponent,
|
||||
ImmunizationComponent,
|
||||
BinaryTextComponent,
|
||||
HtmlComponent,
|
||||
ImgComponent,
|
||||
PdfComponent,
|
||||
MarkdownComponent,
|
||||
DicomComponent,
|
||||
BinaryComponent,
|
||||
GridstackComponent,
|
||||
GridstackItemComponent,
|
||||
|
||||
],
|
||||
declarations: [
|
||||
ComponentsSidebarComponent,
|
||||
|
@ -119,31 +145,15 @@ import { GlossaryLookupComponent } from './glossary-lookup/glossary-lookup.compo
|
|||
ReportMedicalHistoryEditorComponent,
|
||||
ReportMedicalHistoryConditionComponent,
|
||||
ReportLabsObservationComponent,
|
||||
LoadingSpinnerComponent,
|
||||
BinaryComponent,
|
||||
PdfComponent,
|
||||
ImgComponent,
|
||||
BinaryTextComponent,
|
||||
MarkdownComponent,
|
||||
HtmlComponent,
|
||||
|
||||
FhirResourceComponent,
|
||||
FhirResourceOutletDirective,
|
||||
FallbackComponent,
|
||||
ImmunizationComponent,
|
||||
BadgeComponent,
|
||||
TableComponent,
|
||||
CodingComponent,
|
||||
AllergyIntoleranceComponent,
|
||||
MedicationComponent,
|
||||
MedicationRequestComponent,
|
||||
ProcedureComponent,
|
||||
DiagnosticReportComponent,
|
||||
PractitionerComponent,
|
||||
NlmTypeaheadComponent,
|
||||
DocumentReferenceComponent,
|
||||
GlossaryLookupComponent,
|
||||
DicomComponent,
|
||||
MediaComponent,
|
||||
ReportMedicalHistoryExplanationOfBenefitComponent,
|
||||
],
|
||||
exports: [
|
||||
ComponentsSidebarComponent,
|
||||
|
@ -178,25 +188,36 @@ import { GlossaryLookupComponent } from './glossary-lookup/glossary-lookup.compo
|
|||
ReportHeaderComponent,
|
||||
ReportMedicalHistoryEditorComponent,
|
||||
ReportMedicalHistoryConditionComponent,
|
||||
ReportMedicalHistoryExplanationOfBenefitComponent,
|
||||
ReportLabsObservationComponent,
|
||||
LoadingSpinnerComponent,
|
||||
BinaryComponent,
|
||||
FhirResourceComponent,
|
||||
FhirResourceOutletDirective,
|
||||
FallbackComponent,
|
||||
ImmunizationComponent,
|
||||
BadgeComponent,
|
||||
TableComponent,
|
||||
CodingComponent,
|
||||
AllergyIntoleranceComponent,
|
||||
MedicationComponent,
|
||||
MedicationRequestComponent,
|
||||
ProcedureComponent,
|
||||
DiagnosticReportComponent,
|
||||
PractitionerComponent,
|
||||
NlmTypeaheadComponent,
|
||||
DocumentReferenceComponent,
|
||||
GlossaryLookupComponent
|
||||
|
||||
//standalone components
|
||||
BadgeComponent,
|
||||
TableComponent,
|
||||
CodingComponent,
|
||||
LoadingSpinnerComponent,
|
||||
GlossaryLookupComponent,
|
||||
AllergyIntoleranceComponent,
|
||||
MedicationComponent,
|
||||
MedicationRequestComponent,
|
||||
PractitionerComponent,
|
||||
ProcedureComponent,
|
||||
ImmunizationComponent,
|
||||
BinaryComponent,
|
||||
GridstackComponent,
|
||||
GridstackItemComponent,
|
||||
|
||||
]
|
||||
})
|
||||
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
import {InjectionToken} from "@angular/core";
|
||||
|
||||
export const HTTP_CLIENT_TOKEN = new InjectionToken<string>('__HTTP_CLIENT_TOKEN__');
|
|
@ -0,0 +1,27 @@
|
|||
export class DashboardWidgetConfig {
|
||||
id: string
|
||||
item_type: "bar_chart" | "bubble_chart" | "doughnut_chart" | "line_chart" | "pie_chart" | "scatter_chart" | "calendar" | "striped_table" | "basic_table"
|
||||
|
||||
|
||||
title_text: string
|
||||
description_text: string
|
||||
|
||||
queries: {
|
||||
q: string,
|
||||
"aggregator": "avg",
|
||||
"conditional_formats": [],
|
||||
"type": "line",
|
||||
"style": {
|
||||
"palette": "grey" | "pastel" | "light" | "default"
|
||||
}
|
||||
}[]
|
||||
|
||||
|
||||
//used for display purposes within the Dashboard, not for the actual chart
|
||||
minWidth?: number
|
||||
minHeight?: number
|
||||
width: number
|
||||
height: number
|
||||
x?: number
|
||||
y?: number
|
||||
}
|
|
@ -5,6 +5,8 @@ import {HttpClientTestingModule} from '@angular/common/http/testing';
|
|||
import {RouterTestingModule} from '@angular/router/testing';
|
||||
import {RouterModule} from '@angular/router';
|
||||
import {FormsModule} from '@angular/forms';
|
||||
import {HTTP_CLIENT_TOKEN} from '../../dependency-injection';
|
||||
import {HttpClient} from '@angular/common/http';
|
||||
|
||||
describe('AuthSigninComponent', () => {
|
||||
let component: AuthSigninComponent;
|
||||
|
@ -14,6 +16,12 @@ describe('AuthSigninComponent', () => {
|
|||
await TestBed.configureTestingModule({
|
||||
declarations: [ AuthSigninComponent ],
|
||||
imports: [HttpClientTestingModule, FormsModule, RouterTestingModule],
|
||||
providers: [
|
||||
{
|
||||
provide: HTTP_CLIENT_TOKEN,
|
||||
useClass: HttpClient,
|
||||
},
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|||
import { AuthSignupComponent } from './auth-signup.component';
|
||||
import {HttpClientTestingModule} from '@angular/common/http/testing';
|
||||
import {FormsModule} from '@angular/forms';
|
||||
import {HTTP_CLIENT_TOKEN} from '../../dependency-injection';
|
||||
import {HttpClient} from '@angular/common/http';
|
||||
|
||||
describe('AuthSignupComponent', () => {
|
||||
let component: AuthSignupComponent;
|
||||
|
@ -12,6 +14,12 @@ describe('AuthSignupComponent', () => {
|
|||
await TestBed.configureTestingModule({
|
||||
declarations: [ AuthSignupComponent ],
|
||||
imports: [HttpClientTestingModule, FormsModule],
|
||||
providers: [
|
||||
{
|
||||
provide: HTTP_CLIENT_TOKEN,
|
||||
useClass: HttpClient,
|
||||
},
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
|
|
|
@ -31,165 +31,77 @@
|
|||
</nav>
|
||||
|
||||
<nav class="nav">
|
||||
<a class="nav-link" routerLink="/" ngbTooltip="not yet implemented"><i class="far fa-save"></i> Save Report</a>
|
||||
<a class="nav-link" routerLink="/" ngbTooltip="not yet implemented"><i class="far fa-file-pdf"></i> Export to PDF</a>
|
||||
<a class="nav-link" routerLink="/" ngbTooltip="not yet implemented"><i class="far fa-envelope"></i>Send to Email</a>
|
||||
<a class="nav-link" routerLink="/" ngbTooltip="not yet implemented"><i class="fas fa-ellipsis-h"></i></a>
|
||||
<a class="nav-link" routerLink="/" ngbTooltip="not yet implemented"><i class="far fa-save"></i> Export</a>
|
||||
<a class="nav-link" routerLink="/" ngbTooltip="not yet implemented"><i class="far fa-file-pdf"></i> Create</a>
|
||||
<a class="nav-link" (click)="toggleEditableGrid()"><i class="fas fa-edit"></i> Edit </a>
|
||||
<a class="nav-link"><i class="fas fa-ellipsis"></i></a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<!-- Summary Cards -->
|
||||
<div class="row row-sm">
|
||||
<div class="col-md-4 col-lg-4 mg-b-20 mg-md-b-0 mg-lg-b-20">
|
||||
<div class="card card-dashboard-five">
|
||||
<div class="card-header">
|
||||
<h6 class="card-title">Medical Records</h6>
|
||||
<span class="card-text">Summary of medical encounters and records stored in Fasten</span>
|
||||
</div><!-- card-header -->
|
||||
<div class="card-body row row-sm">
|
||||
<div class="col-6 d-sm-flex align-items-center">
|
||||
<div class="card-chart bg-primary">
|
||||
<canvas baseChart class="w-50" [chartType]="'bar'" [datasets]="acquisitionOneChartData" [labels]="acquisitionOneChartLabels" [options]="acquisitionOneChartOptions" [colors]="acquisitionOneChartColors"></canvas>
|
||||
</div>
|
||||
<div>
|
||||
<label>Encounters</label>
|
||||
<h4>{{encounterCount}}</h4>
|
||||
</div>
|
||||
</div><!-- col -->
|
||||
<div class="col-6 d-sm-flex align-items-center">
|
||||
<div class="card-chart bg-purple">
|
||||
<canvas baseChart class="w-50" [chartType]="'bar'" [datasets]="acquisitionTwoChartData" [labels]="acquisitionTwoChartLabels" [options]="acquisitionTwoChartOptions" [colors]="acquisitionTwoChartColors"></canvas>
|
||||
</div>
|
||||
<div>
|
||||
<label>All Records</label>
|
||||
<h4>{{recordsCount}}</h4>
|
||||
</div>
|
||||
</div><!-- col -->
|
||||
</div><!-- card-body -->
|
||||
</div><!-- card-dashboard-five -->
|
||||
</div><!-- col -->
|
||||
<div class="col-md-4 col-lg-4">
|
||||
<div class="card card-dashboard-five">
|
||||
<div class="card-header">
|
||||
<h6 class="card-title">Sources</h6>
|
||||
<span class="card-text"> A source is a medical provider, hospital or insurance company that Fasten can import your data from</span>
|
||||
</div><!-- card-header -->
|
||||
<div class="card-body row row-sm">
|
||||
<div class="col-6">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="mg-b-10 mg-sm-b-0 mg-sm-r-10 wd-50">
|
||||
<canvas baseChart [chartType]="'doughnut'" [datasets]="sessionsChartOneData" [labels]="sessionsChartOneLabels" [options]="sessionsChartOneOptions" height="45"></canvas>
|
||||
</div>
|
||||
<div>
|
||||
<label>Sources</label>
|
||||
<h4>{{sources.length}}</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- col -->
|
||||
<div class="col-6 d-flex align-items-center">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="mg-b-10 mg-sm-b-0 mg-sm-r-10 wd-50">
|
||||
<canvas baseChart [chartType]="'doughnut'" [datasets]="sessionsChartTwoData" [labels]="sessionsChartTwoLabels" [options]="sessionsChartTwoOptions" height="45"></canvas>
|
||||
</div>
|
||||
<div>
|
||||
<label>Updates</label>
|
||||
<h4>19</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- col -->
|
||||
</div><!-- card-body -->
|
||||
</div><!-- card-dashboard-five -->
|
||||
</div><!-- col -->
|
||||
</div><!-- row -->
|
||||
|
||||
<div *ngIf="!loading else isLoadingTemplate" class="card card-dashboard-seven">
|
||||
<div class="card-header">
|
||||
<div class="row row-sm">
|
||||
<div class="col-6 col-md-4 col-xl">
|
||||
<div class="media">
|
||||
<div ><fa-icon [icon]="['fas', 'calendar']"></fa-icon></div>
|
||||
<div class="media-body"><label >Start Date</label>
|
||||
<div class="date"><span >Sept 01, 2018</span>
|
||||
<a ngbTooltip="not yet implemented"><fa-icon [icon]="['fas', 'caret-down']"></fa-icon></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-md-4 col-xl">
|
||||
<div class="media">
|
||||
<div ><fa-icon [icon]="['fas', 'calendar']"></fa-icon></div>
|
||||
<div class="media-body"><label >End Date</label>
|
||||
<div class="date"><span >Sept 30, 2018</span><a ngbTooltip="not yet implemented"><fa-icon [icon]="['fas', 'caret-down']"></fa-icon></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-md-4 col-xl mg-t-15 mg-md-t-0">
|
||||
<div class="media">
|
||||
<div ><fa-icon [icon]="['fas', 'hospital']"></fa-icon></div>
|
||||
<div class="media-body"><label >Source Type</label>
|
||||
<div class="date"><span >All</span><a ngbTooltip="not yet implemented"><fa-icon [icon]="['fas', 'caret-down']"></fa-icon></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div *ngIf="sources.length; else emptyDashboard" class="table-responsive">
|
||||
<table class="table mg-b-0">
|
||||
<tbody>
|
||||
<tr *ngFor="let source of sources" (click)="selectSource(source)" class="alert cursor-pointer" role="alert">
|
||||
<td class="align-middle">
|
||||
<div class="media">
|
||||
<img [src]="'assets/sources/'+source.source_type+'.png'"
|
||||
alt="{{source.source_type}}"
|
||||
class="mr-3"
|
||||
style="width:100px;">
|
||||
<div class="media-body">
|
||||
<h5>{{metadataSource[source.source_type]?.display}}</h5>
|
||||
<p>
|
||||
{{getPatientSummary(patientForSource[source.id]?.resource_raw)}}
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="align-middle"><p><small class="tx-gray-600">status:</small><br/> {{isActive(source)}}</p></td>
|
||||
<td class="align-middle"><p><small class="tx-gray-600">last updated:</small><br/> <span [ngbTooltip]="source.updated_at | date">{{source.updated_at | amTimeAgo}}</span></p></td>
|
||||
<td class="align-middle"><p><small class="tx-gray-600">expires:</small><br/> <span [ngbTooltip]="source.expires_at | amFromUnix | date">{{source.expires_at | amFromUnix | amTimeAgo}}</span></p></td>
|
||||
<td class="align-middle"><p><i class="fas fa-chevron-right"></i></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div><!-- table-responsive -->
|
||||
|
||||
<ng-template #emptyDashboard>
|
||||
<div class="modal-body tx-center pd-y-20 pd-x-20">
|
||||
<h4 class="tx-purple mg-b-20">No Sources Connected!</h4>
|
||||
<p class="mg-b-20 mg-x-20">
|
||||
Fasten securely connects your healthcare providers together, creating a single location to access your entire medical history.
|
||||
</p>
|
||||
<p class="mg-b-20 mg-x-20">
|
||||
Click below to add a new healthcare provider to Fasten.
|
||||
</p>
|
||||
<button [routerLink]="'/sources'" type="button" class="btn btn-purple pd-x-25">Add Source</button>
|
||||
</div><!-- modal-body -->
|
||||
</ng-template>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ng-template #isLoadingTemplate>
|
||||
<div class="row">
|
||||
<div class="row mt-5 mb-3">
|
||||
<div class="col-12">
|
||||
<app-loading-spinner [loadingTitle]="'Please wait, loading sources...'"></app-loading-spinner>
|
||||
|
||||
<div class="alert alert-warning" role="alert">
|
||||
<strong>Warning!</strong> Fasten Health is in the process of implementing a customizable widget based dashboard.
|
||||
<br/>
|
||||
<ul>
|
||||
<li>Users will be able to add, remove, and re-organize widgets on their dashboard.</li>
|
||||
<li>Users will be able to create multiple dashboards, and switch between them.</li>
|
||||
<li>Users will be able to share their dashboards with other users.</li>
|
||||
</ul>
|
||||
<br/>
|
||||
<strong>This functionality is not yet available</strong>, but this example dashboard below will give you a sense of what this may look like. This dashboard only contains placeholder data.
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
|
||||
<gridstack id="gridstack" #gridComp [options]="gridOptions">
|
||||
<gridstack-item class="grid-stack-item" gs-w="8" gs-h="5" gs-x="0" gs-y="0">
|
||||
<app-complex-line-widget></app-complex-line-widget>
|
||||
</gridstack-item>
|
||||
|
||||
|
||||
<gridstack-item class="grid-stack-item" gs-w="2" gs-h="2" gs-x="8" gs-y="0">
|
||||
<app-simple-line-chart-widget></app-simple-line-chart-widget>
|
||||
</gridstack-item>
|
||||
|
||||
<gridstack-item class="grid-stack-item" gs-w="2" gs-h="2" gs-x="10" gs-y="0">
|
||||
<app-simple-line-chart-widget></app-simple-line-chart-widget>
|
||||
</gridstack-item>
|
||||
|
||||
<gridstack-item class="grid-stack-item" gs-w="4" gs-h="3" gs-x="8" gs-y="2">
|
||||
<app-grouped-bar-chart-widget></app-grouped-bar-chart-widget>
|
||||
</gridstack-item>
|
||||
|
||||
<gridstack-item class="grid-stack-item" gs-w="4" gs-h="5" gs-x="0" gs-y="5">
|
||||
<app-patient-vitals-widget></app-patient-vitals-widget>
|
||||
</gridstack-item>
|
||||
|
||||
<gridstack-item class="grid-stack-item" gs-w="8" gs-h="5" gs-x="4" gs-y="5">
|
||||
<app-donut-chart-widget></app-donut-chart-widget>
|
||||
</gridstack-item>
|
||||
|
||||
<gridstack-item class="grid-stack-item" gs-w="4" gs-h="2" gs-x="0" gs-y="10">
|
||||
<app-dual-gauges-widget></app-dual-gauges-widget>
|
||||
</gridstack-item>
|
||||
|
||||
<gridstack-item class="grid-stack-item" gs-w="8" gs-h="4" gs-x="4" gs-y="10">
|
||||
<app-table-widget></app-table-widget>
|
||||
</gridstack-item>
|
||||
|
||||
|
||||
<gridstack-item *ngFor="let item of dashboardItems" class="grid-stack-item"
|
||||
[attr.gs-w]="item.width"
|
||||
[attr.gs-h]="item.height"
|
||||
[attr.gs-x]="item.x"
|
||||
[attr.gs-y]="item.y"
|
||||
[attr.gs-min-w]="item.minWidth"
|
||||
[attr.gs-min-h]="item.minHeight"
|
||||
>
|
||||
<app-dashboard-widget [widgetConfig]="item"></app-dashboard-widget>
|
||||
</gridstack-item>
|
||||
</gridstack>
|
||||
|
||||
|
||||
</div><!-- az-content-body -->
|
||||
</div>
|
||||
|
|
|
@ -4,6 +4,8 @@ import { DashboardComponent } from './dashboard.component';
|
|||
import {HttpClientTestingModule} from '@angular/common/http/testing';
|
||||
import {RouterTestingModule} from '@angular/router/testing';
|
||||
import {RouterModule} from '@angular/router';
|
||||
import {HTTP_CLIENT_TOKEN} from '../../dependency-injection';
|
||||
import {HttpClient} from '@angular/common/http';
|
||||
|
||||
describe('DashboardComponent', () => {
|
||||
let component: DashboardComponent;
|
||||
|
@ -12,7 +14,13 @@ describe('DashboardComponent', () => {
|
|||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [HttpClientTestingModule, RouterTestingModule, RouterModule],
|
||||
declarations: [ DashboardComponent ]
|
||||
declarations: [ DashboardComponent ],
|
||||
providers: [
|
||||
{
|
||||
provide: HTTP_CLIENT_TOKEN,
|
||||
useClass: HttpClient,
|
||||
},
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component, ComponentFactoryResolver, EmbeddedViewRef, OnInit, ViewChild, ViewContainerRef } from '@angular/core';
|
||||
import {Source} from '../../models/fasten/source';
|
||||
import {Router} from '@angular/router';
|
||||
import {ResourceFhir} from '../../models/fasten/resource_fhir';
|
||||
|
@ -7,6 +7,15 @@ import {MetadataSource} from '../../models/fasten/metadata-source';
|
|||
import {FastenApiService} from '../../services/fasten-api.service';
|
||||
import {Summary} from '../../models/fasten/summary';
|
||||
import {LighthouseService} from '../../services/lighthouse.service';
|
||||
import { GridStack, GridStackOptions, GridStackWidget } from 'gridstack';
|
||||
import { GridstackComponent } from '../../components/gridstack/gridstack.component';
|
||||
import {DashboardWidgetComponent} from '../../widgets/dashboard-widget/dashboard-widget.component';
|
||||
import {DashboardWidgetConfig} from '../../models/widget/dashboard-widget-config';
|
||||
|
||||
|
||||
// unique ids sets for each item for correct ngFor updating
|
||||
//TODO: fix this
|
||||
let ids = 1;
|
||||
|
||||
@Component({
|
||||
selector: 'app-dashboard',
|
||||
|
@ -23,10 +32,14 @@ export class DashboardComponent implements OnInit {
|
|||
|
||||
metadataSource: { [name: string]: MetadataSource }
|
||||
|
||||
@ViewChild(GridstackComponent) gridComp?: GridstackComponent;
|
||||
|
||||
constructor(
|
||||
private lighthouseApi: LighthouseService,
|
||||
private fastenApi: FastenApiService,
|
||||
private router: Router
|
||||
private router: Router,
|
||||
private componentFactoryResolver: ComponentFactoryResolver,
|
||||
private vcRef: ViewContainerRef,
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
|
@ -89,470 +102,81 @@ export class DashboardComponent implements OnInit {
|
|||
return expiresDate < currentDate ? 'active' : 'expired'
|
||||
}
|
||||
|
||||
pageViewChartData = [{
|
||||
label: 'This week',
|
||||
data: [36.57, 38.9, 42.3, 41.8, 37.4, 32.5, 28.1, 24.7, 23.4, 20.4, 16.5, 12.1, 9.2, 5.1, 9.6, 10.8, 13.2, 18.2, 13.9, 18.7, 13.7, 11.3, 13.7, 15.8, 12.9, 17.5, 21.9, 18.2, 14.3, 18.2, 14.8, 13.01, 14.5, 15.4, 16.6, 19.4, 14.5, 17.7, 13.8, 9.4, 11.9, 9.7, 6.1, 1.4, 2.3, 2.3, 4.5, 3.7, 5.7, 5.08, 1.9, 8.2,
|
||||
7.9, 5.02, 2.8, 6.8, 6.2, 9.8, 9.3, 11.9, 10, 9, 6, 4.5, 2.7, 4.3, 3.6, 4.2, 2, 1.4, 3.7, 1.5, 5.7, 4.9, 1, 4.7, 6.3, 4.2, 5.1, 5.2, 3.8, 8.2, 7.2, 6.5, 1.7, 11.4, 10.5, 3.8, 4.7, 8.5, 10.2, 11, 15.6, 19.7, 18.1, 13.5, 12, 7.5, 3.7, 9.7, 9.2, 13.4, 18.4, 22.4, 18.7, 15.2, 14.5, 14.4, 12, 13.7, 13.3, 15.4,
|
||||
15.8, 17.7, 14.3, 10.6, 12.7, 14.7, 18.6, 22.9, 18, 22.8, 23.8, 27.1, 24.7, 20, 22.7, 20.9, 16.6, 15.1, 13.1, 10.7, 11.4, 13.1, 10.1, 9.2, 9.2, 10.3, 15.2, 12.5, 14, 18.2, 16.3, 17.7, 18.9, 15.3, 18.1, 16.3, 14.8, 10 ],
|
||||
borderWidth: 2,
|
||||
fill: true
|
||||
},
|
||||
{
|
||||
label: 'Current week',
|
||||
data: [53, 50.3, 49.4, 47.7, 49, 50.6, 48.7, 48.8, 53.5, 52.9, 49, 50.2, 48.3, 44.8, 40.7, 41.2, 45.6, 44.6, 41.3, 38.2, 39.6, 41, 39.4, 35.6, 38.5, 38.5, 40.6, 38.7, 42.9, 46.3, 43.5, 40.6, 36.5, 31.7, 28.9, 29.6, 29.5, 33.1, 37, 35.8, 37.6, 39.6, 39, 34.1, 37.4, 39.2, 38.4, 37.7, 40.1, 35.8, 31.5, 31.8,
|
||||
30.5, 25.7, 28.2, 28.4, 30, 32.1, 32.9, 37.6, 35.2, 39.1, 41.3, 41.4, 43.7, 39.4, 39.2, 43.8, 42.4, 43.6, 38.7 , 43.5, 41.8, 44.8, 46.1, 47.6, 49, 46.4, 51.2, 50.1, 53.6, 56, 52.7, 56.6, 60.2, 58.3, 56.5, 55.7, 54.7, 54.2, 58.6, 57, 60.5, 57.6, 56.1, 55.1, 54.3, 52.3, 54.5, 54.1, 51.9, 51.1, 46.3, 48.3,
|
||||
45.8, 48.2, 43.3, 45.8, 43.4, 41.3, 40.9, 38.4, 40.1, 44.8, 44, 41.4, 37.8, 39.2, 35.2, 32.1, 35.6, 38, 37.9, 38.7, 37.4, 37.5, 33.1, 35, 33.1, 31.8, 29.1, 31.9, 34.3, 32.9, 33.1, 37.1, 32.6, 36.9, 35.9, 38.1, 42.5, 41.5, 45.5, 46.3, 45.7, 45.4, 42.5, 44.4, 39.7, 44.7],
|
||||
borderWidth: 2,
|
||||
fill: true
|
||||
}];
|
||||
|
||||
pageViewChartLabels = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49',
|
||||
'50', '51', '52', '53', '54', '55', '56', '57', '58', '59', '60', '61', '62', '63', '64', '65', '66', '67', '68', '69', '70', '71', '72', '73', '74', '75', '76', '77', '78', '79', '80', '81', '82', '83', '84', '85', '86', '87', '88', '89', '90', '91', '92', '93', '94', '95', '96', '97', '98', '99',
|
||||
'100', '101', '102', '103', '104', '105', '106', '107', '108', '109', '110', '111', '112', '113', '114', '115', '116', '117', '118', '119', '120', '121', '122', '123', '124', '125', '126', '127', '128', '129', '130', '131', '132', '133', '134', '135', '136', '137', '138', '139', '140', '141', '142', '143', '144', '145', '146', '147', '148', '149'];
|
||||
// GridStack options and configuration for tesitng.
|
||||
|
||||
pageViewChartOptions = {
|
||||
public gridEditDisabled = true;
|
||||
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
scales: {
|
||||
yAxes: [{
|
||||
display: true,
|
||||
gridLines: {
|
||||
drawBorder: false,
|
||||
display: true,
|
||||
drawTicks: false,
|
||||
color: '#eef0fa',
|
||||
zeroLineColor: 'rgba(90, 113, 208, 0)',
|
||||
},
|
||||
ticks: {
|
||||
display: false,
|
||||
beginAtZero: true,
|
||||
min: 0,
|
||||
max: 100,
|
||||
stepSize: 32,
|
||||
padding: 10,
|
||||
public gridOptions: GridStackOptions = {
|
||||
margin: 5,
|
||||
float: false,
|
||||
minRow: 1,
|
||||
acceptWidgets: false,
|
||||
alwaysShowResizeHandle: true,
|
||||
|
||||
//these 2 options can be used to enable/disable editability
|
||||
// disableDrag: true,
|
||||
// disableResize: true
|
||||
// children: [
|
||||
// // {x: 0, y: 0, minW: 2},
|
||||
// // {x: 1, y: 1},
|
||||
// // {x: 2, y: 2},
|
||||
// ],
|
||||
}
|
||||
}],
|
||||
xAxes: [{
|
||||
display: false,
|
||||
position: 'bottom',
|
||||
gridLines: {
|
||||
drawBorder: false,
|
||||
display: false,
|
||||
drawTicks: false,
|
||||
},
|
||||
ticks: {
|
||||
beginAtZero: true,
|
||||
stepSize: 10,
|
||||
fontColor: "#a7afb7",
|
||||
padding: 10,
|
||||
}
|
||||
}],
|
||||
},
|
||||
legend: {
|
||||
display: false,
|
||||
},
|
||||
elements: {
|
||||
point: {
|
||||
radius: 0
|
||||
},
|
||||
line: {
|
||||
tension: 0
|
||||
}
|
||||
},
|
||||
tooltips: {
|
||||
backgroundColor: 'rgba(2, 171, 254, 1)',
|
||||
},
|
||||
};
|
||||
|
||||
pageViewChartColors = [
|
||||
{
|
||||
backgroundColor: [
|
||||
'rgba(255, 255, 255, 1)',
|
||||
],
|
||||
borderColor: [
|
||||
'rgb(0, 123, 255)'
|
||||
]
|
||||
},
|
||||
{
|
||||
backgroundColor: [
|
||||
'rgba(86, 11, 208, .05)',
|
||||
],
|
||||
borderColor: [
|
||||
'rgb(86, 11, 208)'
|
||||
],
|
||||
}
|
||||
];
|
||||
public toggleEditableGrid() {
|
||||
this.gridEditDisabled = !this.gridEditDisabled;
|
||||
console.log('toggle - is disabled', this.gridEditDisabled)
|
||||
|
||||
|
||||
|
||||
bounceRateChartData = [{
|
||||
label: 'This week',
|
||||
data: [27.2, 29.9, 29.6, 25.7, 25.9, 29.3, 31.1, 27.9, 28.4, 25.4, 23.2, 18.2, 14, 12.7, 11, 13.7, 9.7, 12.6, 10.9, 12.7, 13.8, 12.9, 13.8, 10.2, 5.8, 7.6, 8.8, 5.6, 5.6, 6.3, 4.2, 3.6, 5.4, 6.5, 8.1, 10.9, 7.6, 9.7, 10.9, 9.5, 5.4, 4.9, .7, 2.3, 5.5, 10, 10.6, 8.3, 8.4, 8.5, 5.8 ],
|
||||
borderWidth: 2,
|
||||
fill: true
|
||||
}];
|
||||
|
||||
bounceRateChartLabels = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '50', '51'];
|
||||
|
||||
bounceRateChartOptions = {
|
||||
|
||||
responsive:true,
|
||||
maintainAspectRatio:false,
|
||||
scales: {
|
||||
yAxes: [{
|
||||
display: false,
|
||||
gridLines: {
|
||||
drawBorder: false,
|
||||
display: true,
|
||||
drawTicks: false,
|
||||
},
|
||||
ticks: {
|
||||
display: false,
|
||||
beginAtZero: true,
|
||||
min: 0,
|
||||
max: 40,
|
||||
stepSize: 10,
|
||||
}
|
||||
}],
|
||||
xAxes: [{
|
||||
display: false,
|
||||
position: 'bottom',
|
||||
gridLines: {
|
||||
drawBorder: false,
|
||||
display: false,
|
||||
drawTicks: false,
|
||||
},
|
||||
ticks: {
|
||||
beginAtZero: true,
|
||||
stepSize: 10,
|
||||
fontColor: "#a7afb7",
|
||||
padding: 10,
|
||||
}
|
||||
}],
|
||||
},
|
||||
legend: {
|
||||
display: false,
|
||||
},
|
||||
elements: {
|
||||
point: {
|
||||
radius: 0
|
||||
},
|
||||
line: {
|
||||
tension: 0
|
||||
}
|
||||
},
|
||||
tooltips: {
|
||||
backgroundColor: 'rgba(2, 171, 254, 1)',
|
||||
},
|
||||
};
|
||||
|
||||
bounceRateChartColors = [
|
||||
{
|
||||
backgroundColor: [
|
||||
'rgba(0, 204, 212, .2)',
|
||||
],
|
||||
borderColor: [
|
||||
'rgb(0, 204, 212)'
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
// Total users chart
|
||||
usersBarChartData = [{
|
||||
label: '# of Votes',
|
||||
data: [27.2, 29.9, 29.6, 25.7, 25.9, 29.3, 31.1, 27.9, 28.4, 25.4, 23.2, 18.2, 14, 12.7, 11, 13.7, 9.7, 12.6, 10.9, 12.7, 13.8],
|
||||
borderWidth: 1,
|
||||
fill: false
|
||||
}];
|
||||
|
||||
usersBarChartLabels = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18'];
|
||||
|
||||
usersBarChartOptions = {
|
||||
|
||||
responsive:true,
|
||||
maintainAspectRatio:false,
|
||||
scales: {
|
||||
yAxes: [{
|
||||
display: false,
|
||||
ticks: {
|
||||
display: false,
|
||||
},
|
||||
gridLines: {
|
||||
drawBorder: false,
|
||||
display: false
|
||||
}
|
||||
}],
|
||||
xAxes: [{
|
||||
display: false,
|
||||
barThickness: 5.5,
|
||||
ticks: {
|
||||
display: false,
|
||||
},
|
||||
gridLines: {
|
||||
drawBorder: false,
|
||||
display: false
|
||||
}
|
||||
}]
|
||||
|
||||
},
|
||||
legend: {
|
||||
display: false
|
||||
},
|
||||
elements: {
|
||||
point: {
|
||||
radius: 0
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
usersBarChartColors = [
|
||||
{
|
||||
backgroundColor: '#007bff',
|
||||
borderColor: '#007bff'
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
// Total users chart
|
||||
sessionsChartData = [{
|
||||
label: '# of Votes',
|
||||
data: [2, 4, 10, 20, 45, 40, 35, 18],
|
||||
borderWidth: 1,
|
||||
fill: false
|
||||
},
|
||||
{
|
||||
label: '# of Rate',
|
||||
data: [3, 6, 15, 35, 50, 45, 35, 25],
|
||||
borderWidth: 1,
|
||||
fill: false
|
||||
}];
|
||||
|
||||
sessionsChartLabels = [0,1,2,3,4,5,6,7];
|
||||
|
||||
sessionsChartOptions = {
|
||||
|
||||
responsive:true,
|
||||
maintainAspectRatio:false,
|
||||
scales: {
|
||||
yAxes: [{
|
||||
display: false,
|
||||
ticks: {
|
||||
beginAtZero:true,
|
||||
fontSize: 11,
|
||||
max: 80
|
||||
},
|
||||
gridLines: {
|
||||
drawBorder: false,
|
||||
}
|
||||
}],
|
||||
xAxes: [{
|
||||
barPercentage: 0.6,
|
||||
gridLines: {
|
||||
color: 'rgba(0,0,0,0.08)',
|
||||
drawBorder: false
|
||||
},
|
||||
ticks: {
|
||||
beginAtZero:true,
|
||||
fontSize: 11,
|
||||
display: false
|
||||
}
|
||||
}]
|
||||
|
||||
},
|
||||
legend: {
|
||||
display: false
|
||||
},
|
||||
elements: {
|
||||
point: {
|
||||
radius: 0
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
sessionsChartColors = [
|
||||
{
|
||||
backgroundColor: '#560bd0'
|
||||
},
|
||||
{
|
||||
backgroundColor: '#cad0e8'
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
// Sessions by channel doughnut chart
|
||||
sessionsByChannelChartData = [{
|
||||
data: [25,20,30,15,10],
|
||||
backgroundColor: ['#6f42c1', '#007bff','#17a2b8','#00cccc','#adb2bd'],
|
||||
}];
|
||||
|
||||
sessionsByChannelChartLabels: ['Search', 'Email', 'Referral', 'Social', 'Other'];
|
||||
sessionsByChannelChartOptions = {
|
||||
cutoutPercentage: 50,
|
||||
maintainAspectRatio: false,
|
||||
responsive: true,
|
||||
legend: {
|
||||
display: false,
|
||||
},
|
||||
animation: {
|
||||
animateScale: true,
|
||||
animateRotate: true
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Sessions by channel doughnut chart
|
||||
sessionsChartOneData = [{
|
||||
data: [40,60],
|
||||
backgroundColor: ['#007bff', '#cad0e8'],
|
||||
borderColor: ['#007bff', '#cad0e8'],
|
||||
}];
|
||||
|
||||
sessionsChartOneLabels: ['Search', 'Email'];
|
||||
sessionsChartOneOptions = {
|
||||
cutoutPercentage: 78,
|
||||
maintainAspectRatio: false,
|
||||
responsive: true,
|
||||
legend: {
|
||||
display: false,
|
||||
},
|
||||
animation: {
|
||||
animateScale: true,
|
||||
animateRotate: true
|
||||
}
|
||||
};
|
||||
|
||||
// Sessions by channel doughnut chart
|
||||
sessionsChartTwoData = [{
|
||||
data: [25,75],
|
||||
backgroundColor: ['#00cccc', '#cad0e8'],
|
||||
borderColor: ['#00cccc', '#cad0e8']
|
||||
}];
|
||||
|
||||
sessionsChartTwoLabels: ['Search', 'Email'];
|
||||
sessionsChartTwoOptions = {
|
||||
cutoutPercentage: 78,
|
||||
maintainAspectRatio: false,
|
||||
responsive: true,
|
||||
legend: {
|
||||
display: false,
|
||||
},
|
||||
animation: {
|
||||
animateScale: true,
|
||||
animateRotate: true
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Acquisition chart one
|
||||
acquisitionOneChartData = [{
|
||||
label: '# of Votes',
|
||||
data: [4,2.5,5,3,5],
|
||||
borderWidth: 1,
|
||||
fill: false
|
||||
}];
|
||||
|
||||
acquisitionOneChartLabels = ['1', '2', '3', '4', '5'];
|
||||
|
||||
acquisitionOneChartOptions = {
|
||||
|
||||
responsive:true,
|
||||
maintainAspectRatio:false,
|
||||
scales: {
|
||||
yAxes: [{
|
||||
display: false,
|
||||
ticks: {
|
||||
display: false,
|
||||
},
|
||||
gridLines: {
|
||||
drawBorder: false,
|
||||
display: false
|
||||
}
|
||||
}],
|
||||
xAxes: [{
|
||||
display: false,
|
||||
barThickness: 5.5,
|
||||
ticks: {
|
||||
display: false,
|
||||
},
|
||||
gridLines: {
|
||||
drawBorder: false,
|
||||
display: false
|
||||
}
|
||||
}]
|
||||
|
||||
},
|
||||
legend: {
|
||||
display: false
|
||||
},
|
||||
elements: {
|
||||
point: {
|
||||
radius: 0
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
acquisitionOneChartColors = [
|
||||
{
|
||||
backgroundColor: '#fff',
|
||||
borderColor: '#fff'
|
||||
}
|
||||
];
|
||||
|
||||
// Acquisition chart two
|
||||
acquisitionTwoChartData = [{
|
||||
label: '# of Votes',
|
||||
data: [5,2,3,5,1.5],
|
||||
borderWidth: 1,
|
||||
fill: false
|
||||
}];
|
||||
|
||||
acquisitionTwoChartLabels = ['1', '2', '3', '4', '5'];
|
||||
|
||||
acquisitionTwoChartOptions = {
|
||||
|
||||
responsive:true,
|
||||
maintainAspectRatio:false,
|
||||
scales: {
|
||||
yAxes: [{
|
||||
display: false,
|
||||
ticks: {
|
||||
display: false,
|
||||
},
|
||||
gridLines: {
|
||||
drawBorder: false,
|
||||
display: false
|
||||
}
|
||||
}],
|
||||
xAxes: [{
|
||||
display: false,
|
||||
barThickness: 5.5,
|
||||
ticks: {
|
||||
display: false,
|
||||
},
|
||||
gridLines: {
|
||||
drawBorder: false,
|
||||
display: false
|
||||
}
|
||||
}]
|
||||
|
||||
},
|
||||
legend: {
|
||||
display: false
|
||||
},
|
||||
elements: {
|
||||
point: {
|
||||
radius: 0
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
acquisitionTwoChartColors = [
|
||||
{
|
||||
backgroundColor: '#fff',
|
||||
borderColor: '#fff'
|
||||
}
|
||||
];
|
||||
this.gridEditDisabled ? this.gridComp.grid?.disable(true) : this.gridComp.grid?.enable(true);
|
||||
|
||||
}
|
||||
|
||||
public dashboardItems: DashboardWidgetConfig[] = []
|
||||
/**
|
||||
* TEST dynamic grid operations - uses grid API directly (since we don't track structure that gets out of sync)
|
||||
*/
|
||||
public add(gridComp: GridstackComponent) {
|
||||
// TODO: BUG the content doesn't appear until widget is moved around (or another created). Need to force
|
||||
// angular detection changes...
|
||||
// gridComp.grid?.addWidget({x:3, y:0, w:2, content:`item ${ids}`, id:String(ids++)});
|
||||
|
||||
this.dashboardItems.push({x:3, y:0, width:4, height:3, id:String(ids++)} as DashboardWidgetConfig)
|
||||
|
||||
// this.makeWidget(gridComp);
|
||||
}
|
||||
public delete(gridComp: GridstackComponent) {
|
||||
gridComp.grid?.removeWidget(gridComp.grid.engine.nodes[0]?.el!);
|
||||
}
|
||||
public modify(gridComp: GridstackComponent) {
|
||||
gridComp.grid?.update(gridComp.grid.engine.nodes[0]?.el!, {w:3})
|
||||
}
|
||||
public newLayout(gridComp: GridstackComponent) {
|
||||
this.dashboardItems = [
|
||||
{x:0, y:0, id:'1', width:4, height: 4, item_type: "calendar"}, // new size/constrain
|
||||
{x:4, y:0, id:'2', width:4, height: 4, item_type: "basic_table"},
|
||||
{x:8, y:0, id:'3', width:3, height: 3, item_type: "line_chart"}, // delete item
|
||||
{x:3, y:5, w:2, width:4, height:3}, // new item
|
||||
] as DashboardWidgetConfig[];
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// getRootNodeFromParsedComponent(component: any) {
|
||||
// const componentFactory =
|
||||
// this.componentFactoryResolver.resolveComponentFactory(component);
|
||||
// let ref = this.vcRef?.createComponent(componentFactory);
|
||||
//
|
||||
// const hostView = <EmbeddedViewRef<any>>ref?.hostView;
|
||||
// return hostView.rootNodes[0];
|
||||
// }
|
||||
//
|
||||
// makeWidget(gridComp: GridstackComponent) {
|
||||
// console.log('called makeWidget');
|
||||
//
|
||||
// gridComp.grid?.el.appendChild(
|
||||
// this.getRootNodeFromParsedComponent(DashboardItemComponent)
|
||||
// );
|
||||
// gridComp.grid?.makeWidget('#widget1');
|
||||
// }
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<!-- Header Row -->
|
||||
<report-header [reportHeaderTitle]="'Medical History'" [reportHeaderSubTitle]="'Organized by conditions, describes the scope and breadth of medical care'"></report-header>
|
||||
|
||||
<ng-container [ngTemplateOutlet]="loading ? isLoadingTemplate : (conditions.length == 0 && unassigned_encounters.length == 0) ? emptyReport : report"></ng-container>
|
||||
<ng-container [ngTemplateOutlet]="loading ? isLoadingTemplate : (conditions.length == 0 && unassigned_encounters.length == 0 && explanationOfBenefits.length == 0) ? emptyReport : report"></ng-container>
|
||||
|
||||
<ng-template #report>
|
||||
<!-- Editor Button -->
|
||||
|
@ -33,6 +33,7 @@
|
|||
|
||||
<!-- Condition List -->
|
||||
<app-report-medical-history-condition *ngFor="let condition of conditions; let i = index" [conditionGroup]="condition"></app-report-medical-history-condition>
|
||||
<app-report-medical-history-explanation-of-benefit *ngFor="let eob of explanationOfBenefits; let i = index" [explanationOfBenefitGroup]="eob"></app-report-medical-history-explanation-of-benefit>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #emptyReport>
|
||||
|
|
|
@ -16,6 +16,7 @@ export class MedicalHistoryComponent implements OnInit {
|
|||
|
||||
closeResult = '';
|
||||
conditions: ResourceFhir[] = []
|
||||
explanationOfBenefits: ResourceFhir[] = []
|
||||
|
||||
unassigned_encounters: ResourceFhir[] = []
|
||||
resourceLookup: {[name: string]: ResourceFhir} = {}
|
||||
|
@ -32,6 +33,7 @@ export class MedicalHistoryComponent implements OnInit {
|
|||
this.loading = false
|
||||
this.conditions = [].concat(results["Condition"] || [], results["Composition"] || [])
|
||||
this.unassigned_encounters = results["Encounter"] || []
|
||||
this.explanationOfBenefits = results["ExplanationOfBenefit"] || []
|
||||
|
||||
//populate a lookup table with all resources
|
||||
for(let condition of this.conditions){
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<strong>Work-in-Progress!</strong> Some sources may not be implemented correctly.
|
||||
Some sources may require frequent re-connection, as background refresh has not been implemented yet.
|
||||
<br/>
|
||||
If you have feedback regarding healthcare sources, please <a href="https://github.com/fastenhealth/docs/issues">file a ticket <i class="fab fa-github"></i></a>
|
||||
If you have feedback regarding healthcare sources, please <a href="https://github.com/fastenhealth/fasten-onprem/issues">file a ticket <i class="fab fa-github"></i></a>
|
||||
|
||||
<span *ngIf="environment_name == 'sandbox'">
|
||||
<br/>
|
||||
|
|
|
@ -3,6 +3,8 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|||
import { MedicalSourcesComponent } from './medical-sources.component';
|
||||
import {HttpClientTestingModule} from '@angular/common/http/testing';
|
||||
import {RouterTestingModule} from '@angular/router/testing';
|
||||
import {HTTP_CLIENT_TOKEN} from '../../dependency-injection';
|
||||
import {HttpClient} from '@angular/common/http';
|
||||
|
||||
describe('MedicalSourcesComponent', () => {
|
||||
let component: MedicalSourcesComponent;
|
||||
|
@ -12,6 +14,12 @@ describe('MedicalSourcesComponent', () => {
|
|||
await TestBed.configureTestingModule({
|
||||
declarations: [ MedicalSourcesComponent ],
|
||||
imports: [HttpClientTestingModule, RouterTestingModule],
|
||||
providers: [
|
||||
{
|
||||
provide: HTTP_CLIENT_TOKEN,
|
||||
useClass: HttpClient,
|
||||
},
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
|
|
|
@ -4,6 +4,8 @@ import { ResourceCreatorComponent } from './resource-creator.component';
|
|||
import {HttpClientTestingModule} from '@angular/common/http/testing';
|
||||
import {RouterTestingModule} from '@angular/router/testing';
|
||||
import {NgbCollapseModule, NgbDatepickerModule} from '@ng-bootstrap/ng-bootstrap';
|
||||
import {HTTP_CLIENT_TOKEN} from '../../dependency-injection';
|
||||
import {HttpClient} from '@angular/common/http';
|
||||
|
||||
describe('ResourceCreatorComponent', () => {
|
||||
let component: ResourceCreatorComponent;
|
||||
|
@ -12,7 +14,13 @@ describe('ResourceCreatorComponent', () => {
|
|||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [HttpClientTestingModule, RouterTestingModule, NgbDatepickerModule, NgbCollapseModule],
|
||||
declarations: [ ResourceCreatorComponent ]
|
||||
declarations: [ ResourceCreatorComponent ],
|
||||
providers: [
|
||||
{
|
||||
provide: HTTP_CLIENT_TOKEN,
|
||||
useClass: HttpClient,
|
||||
},
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
|
|
|
@ -4,6 +4,8 @@ import { ResourceDetailComponent } from './resource-detail.component';
|
|||
import {HttpClientTestingModule} from '@angular/common/http/testing';
|
||||
import {RouterTestingModule} from '@angular/router/testing';
|
||||
import {ActivatedRoute, convertToParamMap} from '@angular/router';
|
||||
import {HTTP_CLIENT_TOKEN} from '../../dependency-injection';
|
||||
import {HttpClient} from '@angular/common/http';
|
||||
|
||||
describe('ResourceDetailComponent', () => {
|
||||
let component: ResourceDetailComponent;
|
||||
|
@ -17,7 +19,11 @@ describe('ResourceDetailComponent', () => {
|
|||
{
|
||||
provide: ActivatedRoute,
|
||||
useValue: {snapshot: {paramMap: convertToParamMap( { 'resource_id': 'b64.cmVzb3VyY2VfZmhpcjpiNjQuYzI5MWNtTmxPbUZsZEc1aE9qRXlNelExTmpjNE9UQXhNak0wTlRZM01ETT06UGF0aWVudDoxMjM0NTY3ODkwMTIzNDU2NzAz' } )}}
|
||||
}
|
||||
},
|
||||
{
|
||||
provide: HTTP_CLIENT_TOKEN,
|
||||
useClass: HttpClient,
|
||||
},
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue