@medplum/react 0.9.34 → 0.9.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/README.md +1 -1
  2. package/dist/cjs/QuestionnaireUtils.d.ts +1 -10
  3. package/dist/cjs/SearchControlField.d.ts +2 -3
  4. package/dist/cjs/SearchFieldEditor.d.ts +1 -2
  5. package/dist/cjs/SearchFilterEditor.d.ts +1 -2
  6. package/dist/cjs/SearchFilterValueDialog.d.ts +1 -2
  7. package/dist/cjs/SearchFilterValueDisplay.d.ts +1 -1
  8. package/dist/cjs/SearchFilterValueInput.d.ts +0 -2
  9. package/dist/cjs/SearchPopupMenu.d.ts +1 -2
  10. package/dist/cjs/auth/AuthenticationForm.d.ts +1 -0
  11. package/dist/cjs/index.js +95 -124
  12. package/dist/cjs/index.js.map +1 -1
  13. package/dist/cjs/index.min.js +1 -1
  14. package/dist/cjs/index.min.js.map +1 -1
  15. package/dist/cjs/styles.css +51 -51
  16. package/dist/esm/AddressInput.js.map +1 -1
  17. package/dist/esm/ContactPointInput.js +2 -1
  18. package/dist/esm/ContactPointInput.js.map +1 -1
  19. package/dist/esm/GoogleButton.js +1 -1
  20. package/dist/esm/GoogleButton.js.map +1 -1
  21. package/dist/esm/HumanNameInput.js.map +1 -1
  22. package/dist/esm/PlanDefinitionBuilder.js +2 -0
  23. package/dist/esm/PlanDefinitionBuilder.js.map +1 -1
  24. package/dist/esm/Popup.js +15 -1
  25. package/dist/esm/Popup.js.map +1 -1
  26. package/dist/esm/QuantityInput.js.map +1 -1
  27. package/dist/esm/QuestionnaireBuilder.js +9 -0
  28. package/dist/esm/QuestionnaireBuilder.js.map +1 -1
  29. package/dist/esm/QuestionnaireUtils.d.ts +1 -10
  30. package/dist/esm/QuestionnaireUtils.js +1 -78
  31. package/dist/esm/QuestionnaireUtils.js.map +1 -1
  32. package/dist/esm/RequestGroupDisplay.js +1 -0
  33. package/dist/esm/RequestGroupDisplay.js.map +1 -1
  34. package/dist/esm/ResourceTimeline.js.map +1 -1
  35. package/dist/esm/SearchControl.js +11 -14
  36. package/dist/esm/SearchControl.js.map +1 -1
  37. package/dist/esm/SearchControlField.d.ts +2 -3
  38. package/dist/esm/SearchControlField.js +6 -8
  39. package/dist/esm/SearchControlField.js.map +1 -1
  40. package/dist/esm/SearchFieldEditor.d.ts +1 -2
  41. package/dist/esm/SearchFieldEditor.js +2 -2
  42. package/dist/esm/SearchFieldEditor.js.map +1 -1
  43. package/dist/esm/SearchFilterEditor.d.ts +1 -2
  44. package/dist/esm/SearchFilterEditor.js +5 -6
  45. package/dist/esm/SearchFilterEditor.js.map +1 -1
  46. package/dist/esm/SearchFilterValueDialog.d.ts +1 -2
  47. package/dist/esm/SearchFilterValueDialog.js +1 -1
  48. package/dist/esm/SearchFilterValueDialog.js.map +1 -1
  49. package/dist/esm/SearchFilterValueDisplay.d.ts +1 -1
  50. package/dist/esm/SearchFilterValueDisplay.js +11 -11
  51. package/dist/esm/SearchFilterValueDisplay.js.map +1 -1
  52. package/dist/esm/SearchFilterValueInput.d.ts +0 -2
  53. package/dist/esm/SearchFilterValueInput.js +1 -1
  54. package/dist/esm/SearchFilterValueInput.js.map +1 -1
  55. package/dist/esm/SearchPopupMenu.d.ts +1 -2
  56. package/dist/esm/SearchPopupMenu.js.map +1 -1
  57. package/dist/esm/TimingInput.js.map +1 -1
  58. package/dist/esm/auth/AuthenticationForm.d.ts +1 -0
  59. package/dist/esm/auth/AuthenticationForm.js +14 -6
  60. package/dist/esm/auth/AuthenticationForm.js.map +1 -1
  61. package/dist/esm/auth/SignInForm.js +1 -1
  62. package/dist/esm/auth/SignInForm.js.map +1 -1
  63. package/dist/esm/index.js +1 -1
  64. package/dist/esm/index.min.js +1 -1
  65. package/dist/esm/index.min.js.map +1 -1
  66. package/dist/esm/styles.css +51 -51
  67. package/dist/esm/utils/blame.js +1 -0
  68. package/dist/esm/utils/blame.js.map +1 -1
  69. package/dist/esm/utils/outcomes.js +19 -1
  70. package/dist/esm/utils/outcomes.js.map +1 -1
  71. package/package.json +11 -11
  72. package/stats.html +0 -4034
package/README.md CHANGED
@@ -4,7 +4,7 @@ The Medplum React Component Library provides many helpful components to quickly
4
4
 
5
5
  The Medplum SDK can be used with any compliant FHIR server. However, some advanced features are only available when paired with a Medplum server.
6
6
 
7
- Check out a live demo: [https://docs.medplum.com/storybook/index.html](https://docs.medplum.com/storybook/index.html)
7
+ Check out a live demo: <https://storybook.medplum.com/>
8
8
 
9
9
  ## Key Features
10
10
 
@@ -1,4 +1,4 @@
1
- import { Questionnaire, QuestionnaireItem } from '@medplum/fhirtypes';
1
+ import { QuestionnaireItem } from '@medplum/fhirtypes';
2
2
  export declare enum QuestionnaireItemType {
3
3
  group = "group",
4
4
  display = "display",
@@ -18,13 +18,4 @@ export declare enum QuestionnaireItemType {
18
18
  reference = "reference",
19
19
  quantity = "quantity"
20
20
  }
21
- /**
22
- * Adds initial values to a questionnaire resource from key value pairs.
23
- * The values map uses "linkId" as key.
24
- * The value depends on the questionnaire item type.
25
- * @param questionnaire The original questionnaire.
26
- * @param values Key value pairs for initial values.
27
- * @returns Rewritten questionnaire with initial values.
28
- */
29
- export declare function addQuestionnaireInitialValues(questionnaire: Questionnaire, values: Record<string, string>): Questionnaire;
30
21
  export declare function isChoiceQuestion(item: QuestionnaireItem): boolean;
@@ -1,4 +1,4 @@
1
- import { IndexedStructureDefinition, SearchRequest } from '@medplum/core';
1
+ import { SearchRequest } from '@medplum/core';
2
2
  import { ElementDefinition, SearchParameter } from '@medplum/fhirtypes';
3
3
  /**
4
4
  * The SearchControlField type describes a field in the search control.
@@ -34,8 +34,7 @@ export interface SearchControlField {
34
34
  }
35
35
  /**
36
36
  * Returns the collection of field definitions for the search request.
37
- * @param typeSchema The schema for the resource type
38
37
  * @param search The search request definition.
39
38
  * @returns An array of field definitions.
40
39
  */
41
- export declare function getFieldDefinitions(schema: IndexedStructureDefinition, search: SearchRequest): SearchControlField[];
40
+ export declare function getFieldDefinitions(search: SearchRequest): SearchControlField[];
@@ -1,7 +1,6 @@
1
1
  /// <reference types="react" />
2
- import { IndexedStructureDefinition, SearchRequest } from '@medplum/core';
2
+ import { SearchRequest } from '@medplum/core';
3
3
  interface SearchFieldEditorProps {
4
- schema: IndexedStructureDefinition;
5
4
  visible: boolean;
6
5
  search: SearchRequest;
7
6
  onOk: (search: SearchRequest) => void;
@@ -1,8 +1,7 @@
1
1
  /// <reference types="react" />
2
- import { IndexedStructureDefinition, SearchRequest } from '@medplum/core';
2
+ import { SearchRequest } from '@medplum/core';
3
3
  import './SearchFilterEditor.css';
4
4
  export interface SearchFilterEditorProps {
5
- schema: IndexedStructureDefinition;
6
5
  visible: boolean;
7
6
  search: SearchRequest;
8
7
  onOk: (search: SearchRequest) => void;
@@ -1,10 +1,9 @@
1
1
  /// <reference types="react" />
2
- import { Filter, IndexedStructureDefinition } from '@medplum/core';
2
+ import { Filter } from '@medplum/core';
3
3
  import { SearchParameter } from '@medplum/fhirtypes';
4
4
  export interface SearchFilterValueDialogProps {
5
5
  title: string;
6
6
  visible: boolean;
7
- schema: IndexedStructureDefinition;
8
7
  resourceType: string;
9
8
  searchParam?: SearchParameter;
10
9
  filter?: Filter;
@@ -4,4 +4,4 @@ export interface SearchFilterValueDisplayProps {
4
4
  readonly resourceType: string;
5
5
  readonly filter: Filter;
6
6
  }
7
- export declare function SearchFilterValueDisplay(props: SearchFilterValueDisplayProps): JSX.Element | null;
7
+ export declare function SearchFilterValueDisplay(props: SearchFilterValueDisplayProps): JSX.Element;
@@ -1,8 +1,6 @@
1
1
  /// <reference types="react" />
2
- import { IndexedStructureDefinition } from '@medplum/core';
3
2
  import { SearchParameter } from '@medplum/fhirtypes';
4
3
  export interface SearchFilterValueInputProps {
5
- schema: IndexedStructureDefinition;
6
4
  resourceType: string;
7
5
  searchParam: SearchParameter;
8
6
  defaultValue?: string;
@@ -1,8 +1,7 @@
1
1
  /// <reference types="react" />
2
- import { Filter, IndexedStructureDefinition, SearchRequest } from '@medplum/core';
2
+ import { Filter, SearchRequest } from '@medplum/core';
3
3
  import { SearchParameter } from '@medplum/fhirtypes';
4
4
  export interface SearchPopupMenuProps {
5
- schema: IndexedStructureDefinition;
6
5
  search: SearchRequest;
7
6
  visible: boolean;
8
7
  x: number;
@@ -6,6 +6,7 @@ export interface AuthenticationFormProps {
6
6
  readonly scope?: string;
7
7
  readonly nonce?: string;
8
8
  readonly googleClientId?: string;
9
+ readonly generatePkce?: boolean;
9
10
  readonly codeChallenge?: string;
10
11
  readonly codeChallengeMethod?: string;
11
12
  readonly onForgotPassword?: () => void;
package/dist/cjs/index.js CHANGED
@@ -18,7 +18,25 @@
18
18
 
19
19
  function getIssuesForExpression(outcome, expression) {
20
20
  var _a;
21
- return (_a = outcome === null || outcome === void 0 ? void 0 : outcome.issue) === null || _a === void 0 ? void 0 : _a.filter((issue) => { var _a; return ((_a = issue.expression) === null || _a === void 0 ? void 0 : _a[0]) === expression; });
21
+ return (_a = outcome === null || outcome === void 0 ? void 0 : outcome.issue) === null || _a === void 0 ? void 0 : _a.filter((issue) => { var _a; return isExpressionMatch((_a = issue.expression) === null || _a === void 0 ? void 0 : _a[0], expression); });
22
+ }
23
+ function isExpressionMatch(expr1, expr2) {
24
+ // Expression can be either "fieldName" or "resourceType.fieldName"
25
+ if (expr1 === expr2) {
26
+ return true;
27
+ }
28
+ if (!expr1 || !expr2) {
29
+ return false;
30
+ }
31
+ const dot1 = expr1.indexOf('.');
32
+ if (dot1 >= 0 && expr1.substring(dot1 + 1) === expr2) {
33
+ return true;
34
+ }
35
+ const dot2 = expr2.indexOf('.');
36
+ if (dot2 >= 0 && expr2.substring(dot2 + 1) === expr1) {
37
+ return true;
38
+ }
39
+ return false;
22
40
  }
23
41
 
24
42
  function Input(props) {
@@ -518,7 +536,7 @@
518
536
  return clientId;
519
537
  }
520
538
  const origin = window.location.protocol + '//' + window.location.host;
521
- const authorizedOrigins = (_b = (_a = "http://localhost:3000,http://127.0.0.1:3000,http://localhost:6006,http://127.0.0.1:6006,https://app.medplum.com,https://docs.medplum.com") === null || _a === void 0 ? void 0 : _a.split(',')) !== null && _b !== void 0 ? _b : [];
539
+ const authorizedOrigins = (_b = (_a = "http://localhost:3000,http://127.0.0.1:3000,http://localhost:6006,http://127.0.0.1:6006,https://app.medplum.com,https://docs.medplum.com,https://storybook.medplum.com,https://graphiql.medplum.com,https://www.medplum.com") === null || _a === void 0 ? void 0 : _a.split(',')) !== null && _b !== void 0 ? _b : [];
522
540
  if (authorizedOrigins.includes(origin)) {
523
541
  return "921088377005-3j1sa10vr6hj86jgmdfh2l53v3mp7lfi.apps.googleusercontent.com";
524
542
  }
@@ -706,9 +724,16 @@
706
724
  const googleClientId = getGoogleClientId(props.googleClientId);
707
725
  const [outcome, setOutcome] = React.useState();
708
726
  const issues = getIssuesForExpression(outcome, undefined);
727
+ function startPkce() {
728
+ return __awaiter(this, void 0, void 0, function* () {
729
+ if (props.generatePkce) {
730
+ yield medplum.startPkce();
731
+ }
732
+ });
733
+ }
709
734
  return (React__default["default"].createElement(Form, { style: { maxWidth: 400 }, onSubmit: (formData) => {
710
- medplum
711
- .startLogin({
735
+ startPkce()
736
+ .then(() => medplum.startLogin({
712
737
  projectId: props.projectId,
713
738
  clientId: props.clientId,
714
739
  scope: props.scope,
@@ -718,7 +743,7 @@
718
743
  email: formData.email,
719
744
  password: formData.password,
720
745
  remember: formData.remember === 'true',
721
- })
746
+ }))
722
747
  .then(props.handleAuthResponse)
723
748
  .catch(setOutcome);
724
749
  } },
@@ -730,8 +755,8 @@
730
755
  googleClientId && (React__default["default"].createElement(React__default["default"].Fragment, null,
731
756
  React__default["default"].createElement("div", { className: "medplum-signin-google-container" },
732
757
  React__default["default"].createElement(GoogleButton, { googleClientId: googleClientId, handleGoogleCredential: (response) => {
733
- medplum
734
- .startGoogleLogin({
758
+ startPkce()
759
+ .then(() => medplum.startGoogleLogin({
735
760
  projectId: props.projectId,
736
761
  clientId: props.clientId,
737
762
  scope: props.scope,
@@ -740,7 +765,7 @@
740
765
  codeChallengeMethod: props.codeChallengeMethod,
741
766
  googleClientId: response.clientId,
742
767
  googleCredential: response.credential,
743
- })
768
+ }))
744
769
  .then(props.handleAuthResponse)
745
770
  .catch(setOutcome);
746
771
  } })),
@@ -891,7 +916,7 @@
891
916
  }
892
917
  return (React__default["default"].createElement(Document, { width: 450 }, (() => {
893
918
  if (!login) {
894
- return (React__default["default"].createElement(AuthenticationForm, { projectId: props.projectId, clientId: props.clientId, scope: props.scope, nonce: props.nonce, googleClientId: props.googleClientId, codeChallenge: props.codeChallenge, codeChallengeMethod: props.codeChallengeMethod, onForgotPassword: props.onForgotPassword, onRegister: props.onRegister, handleAuthResponse: handleAuthResponse }, props.children));
919
+ return (React__default["default"].createElement(AuthenticationForm, { projectId: props.projectId, clientId: props.clientId, scope: props.scope, nonce: props.nonce, googleClientId: props.googleClientId, generatePkce: !props.onCode, codeChallenge: props.codeChallenge, codeChallengeMethod: props.codeChallengeMethod, onForgotPassword: props.onForgotPassword, onRegister: props.onRegister, handleAuthResponse: handleAuthResponse }, props.children));
895
920
  }
896
921
  else if (memberships) {
897
922
  return React__default["default"].createElement(ChooseProfileForm, { login: login, memberships: memberships, handleAuthResponse: handleAuthResponse });
@@ -1685,7 +1710,8 @@
1685
1710
  React__default["default"].createElement("option", null, "pager"),
1686
1711
  React__default["default"].createElement("option", null, "phone"),
1687
1712
  React__default["default"].createElement("option", null, "other"),
1688
- React__default["default"].createElement("option", null, "sms")),
1713
+ React__default["default"].createElement("option", null, "sms"),
1714
+ React__default["default"].createElement("option", null, "url")),
1689
1715
  React__default["default"].createElement(Select, { defaultValue: contactPoint === null || contactPoint === void 0 ? void 0 : contactPoint.use, onChange: setUse, testid: "use" },
1690
1716
  React__default["default"].createElement("option", null),
1691
1717
  React__default["default"].createElement("option", null, "home"),
@@ -2550,11 +2576,24 @@
2550
2576
  ((_b = propsRef.current) === null || _b === void 0 ? void 0 : _b.autoClose) &&
2551
2577
  (ref === null || ref === void 0 ? void 0 : ref.current) &&
2552
2578
  !ref.current.contains(e.target)) {
2579
+ killEvent(e);
2553
2580
  props.onClose();
2554
2581
  }
2555
2582
  }
2583
+ function handleScroll(e) {
2584
+ var _a;
2585
+ if ((_a = propsRef.current) === null || _a === void 0 ? void 0 : _a.visible) {
2586
+ killEvent(e);
2587
+ }
2588
+ }
2556
2589
  document.addEventListener('click', handleClick, true);
2557
- return () => document.removeEventListener('click', handleClick, true);
2590
+ window.addEventListener('wheel', handleScroll, { passive: false });
2591
+ window.addEventListener('touchmove', handleScroll, true);
2592
+ return () => {
2593
+ document.removeEventListener('click', handleClick, true);
2594
+ window.removeEventListener('wheel', handleScroll);
2595
+ window.removeEventListener('touchmove', handleScroll, true);
2596
+ };
2558
2597
  }, [props]);
2559
2598
  // Listen for changes in the location
2560
2599
  // If the browser navigates to a new page, close the popup
@@ -2956,27 +2995,25 @@
2956
2995
 
2957
2996
  /**
2958
2997
  * Returns the collection of field definitions for the search request.
2959
- * @param typeSchema The schema for the resource type
2960
2998
  * @param search The search request definition.
2961
2999
  * @returns An array of field definitions.
2962
3000
  */
2963
- function getFieldDefinitions(schema, search) {
3001
+ function getFieldDefinitions(search) {
2964
3002
  const resourceType = search.resourceType;
2965
3003
  const fields = [];
2966
3004
  for (const name of search.fields || ['id', '_lastUpdated']) {
2967
- fields.push(getFieldDefinition(schema, resourceType, name));
3005
+ fields.push(getFieldDefinition(resourceType, name));
2968
3006
  }
2969
3007
  return fields;
2970
3008
  }
2971
3009
  /**
2972
3010
  * Return the field definition for a given field name.
2973
3011
  * Field names can be either property names or search parameter codes.
2974
- * @param typeSchema The schema for the resource type
2975
3012
  * @param resourceType The resource type.
2976
3013
  * @param name The search field name (either property name or search parameter code).
2977
3014
  * @returns The field definition.
2978
3015
  */
2979
- function getFieldDefinition(schema, resourceType, name) {
3016
+ function getFieldDefinition(resourceType, name) {
2980
3017
  var _a;
2981
3018
  if (name === '_lastUpdated') {
2982
3019
  return {
@@ -3008,7 +3045,7 @@
3008
3045
  ],
3009
3046
  };
3010
3047
  }
3011
- const typeSchema = schema.types[resourceType];
3048
+ const typeSchema = core.globalSchema.types[resourceType];
3012
3049
  const exactElementDefinition = typeSchema.properties[name];
3013
3050
  const exactSearchParam = (_a = typeSchema.searchParams) === null || _a === void 0 ? void 0 : _a[name.toLowerCase()];
3014
3051
  // Best case: Exact match of element definition or search parameter.
@@ -3039,7 +3076,7 @@
3039
3076
  // Patient.email is a search parameter for the Patient.telecom element.
3040
3077
  // So we need to walk backwards to find the element definition.
3041
3078
  if (exactSearchParam) {
3042
- const details = core.getSearchParameterDetails(schema, resourceType, exactSearchParam);
3079
+ const details = core.getSearchParameterDetails(resourceType, exactSearchParam);
3043
3080
  return { name, elementDefinition: details.elementDefinition, searchParams: [exactSearchParam] };
3044
3081
  }
3045
3082
  // Worst case: no element definition and no search parameter.
@@ -3655,7 +3692,7 @@
3655
3692
  return null;
3656
3693
  }
3657
3694
  const resourceType = props.search.resourceType;
3658
- const typeDef = props.schema.types[resourceType];
3695
+ const typeDef = core.globalSchema.types[resourceType];
3659
3696
  const selected = (_a = state.search.fields) !== null && _a !== void 0 ? _a : [];
3660
3697
  const available = getFieldsList(typeDef)
3661
3698
  .filter((field) => !(selected === null || selected === void 0 ? void 0 : selected.includes(field)))
@@ -3715,22 +3752,23 @@
3715
3752
 
3716
3753
  function SearchFilterValueDisplay(props) {
3717
3754
  var _a, _b;
3718
- const medplum = useMedplum();
3719
- const schema = medplum.getSchema();
3720
- const searchParam = (_b = (_a = schema.types[props.resourceType]) === null || _a === void 0 ? void 0 : _a.searchParams) === null || _b === void 0 ? void 0 : _b[props.filter.code];
3721
- const filter = props.filter;
3722
- if ((searchParam === null || searchParam === void 0 ? void 0 : searchParam.type) === 'reference') {
3723
- return React__default["default"].createElement(ResourceName, { value: { reference: filter.value } });
3724
- }
3725
- if (props.filter.code === '_lastUpdated' || (searchParam === null || searchParam === void 0 ? void 0 : searchParam.type) === 'datetime') {
3726
- return React__default["default"].createElement(React__default["default"].Fragment, null, core.formatDateTime(filter.value));
3755
+ const { resourceType, filter } = props;
3756
+ const searchParam = (_b = (_a = core.globalSchema.types[resourceType]) === null || _a === void 0 ? void 0 : _a.searchParams) === null || _b === void 0 ? void 0 : _b[filter.code];
3757
+ if (searchParam) {
3758
+ if (searchParam.type === 'reference') {
3759
+ return React__default["default"].createElement(ResourceName, { value: { reference: filter.value } });
3760
+ }
3761
+ const searchParamDetails = core.getSearchParameterDetails(resourceType, searchParam);
3762
+ if (filter.code === '_lastUpdated' || searchParamDetails.type === core.SearchParameterType.DATETIME) {
3763
+ return React__default["default"].createElement(React__default["default"].Fragment, null, core.formatDateTime(filter.value));
3764
+ }
3727
3765
  }
3728
3766
  return React__default["default"].createElement(React__default["default"].Fragment, null, filter.value);
3729
3767
  }
3730
3768
 
3731
3769
  function SearchFilterValueInput(props) {
3732
3770
  var _a;
3733
- const details = core.getSearchParameterDetails(props.schema, props.resourceType, props.searchParam);
3771
+ const details = core.getSearchParameterDetails(props.resourceType, props.searchParam);
3734
3772
  const name = 'filter-value';
3735
3773
  switch (details.type) {
3736
3774
  case core.SearchParameterType.REFERENCE:
@@ -3791,9 +3829,8 @@
3791
3829
  if (!props.visible) {
3792
3830
  return null;
3793
3831
  }
3794
- const schema = props.schema;
3795
3832
  const resourceType = props.search.resourceType;
3796
- const searchParams = schema.types[resourceType].searchParams;
3833
+ const searchParams = core.globalSchema.types[resourceType].searchParams;
3797
3834
  const filters = search.filters || [];
3798
3835
  return (React__default["default"].createElement(Dialog, { title: "Filters", visible: props.visible, onOk: () => props.onOk(searchRef.current), onCancel: props.onCancel },
3799
3836
  React__default["default"].createElement("div", { className: "medplum-filter-editor" },
@@ -3812,7 +3849,7 @@
3812
3849
  React__default["default"].createElement("tbody", null,
3813
3850
  filters.map((filter, index) => {
3814
3851
  if (index === editingIndex) {
3815
- return (React__default["default"].createElement(FilterRowInput, { key: `filter-${index}-${filters.length}-input`, schema: schema, resourceType: resourceType, searchParams: searchParams, defaultValue: filter, okText: "Save", onOk: (newFilter) => {
3852
+ return (React__default["default"].createElement(FilterRowInput, { key: `filter-${index}-${filters.length}-input`, resourceType: resourceType, searchParams: searchParams, defaultValue: filter, okText: "Save", onOk: (newFilter) => {
3816
3853
  const newFilters = [...filters];
3817
3854
  newFilters[index] = newFilter;
3818
3855
  setSearch(setFilters(searchRef.current, newFilters));
@@ -3823,7 +3860,7 @@
3823
3860
  return (React__default["default"].createElement(FilterRowDisplay, { key: `filter-${index}-${filters.length}-display`, resourceType: resourceType, searchParams: searchParams, filter: filter, onEdit: () => setEditingIndex(index), onDelete: () => setSearch(deleteFilter(searchRef.current, index)) }));
3824
3861
  }
3825
3862
  }),
3826
- React__default["default"].createElement(FilterRowInput, { schema: schema, resourceType: resourceType, searchParams: searchParams, okText: "Add", onOk: onAddFilter }))))));
3863
+ React__default["default"].createElement(FilterRowInput, { resourceType: resourceType, searchParams: searchParams, okText: "Add", onOk: onAddFilter }))))));
3827
3864
  }
3828
3865
  function FilterRowDisplay(props) {
3829
3866
  const { filter } = props;
@@ -3860,7 +3897,7 @@
3860
3897
  React__default["default"].createElement("td", null, operators && (React__default["default"].createElement(Select, { testid: "filter-operation", defaultValue: value.operator, onChange: setFilterOperator },
3861
3898
  React__default["default"].createElement("option", { value: "" }),
3862
3899
  operators.map((operator) => (React__default["default"].createElement("option", { key: operator, value: operator }, getOpString(operator))))))),
3863
- React__default["default"].createElement("td", null, searchParam && value.operator && (React__default["default"].createElement(SearchFilterValueInput, { schema: props.schema, resourceType: props.resourceType, searchParam: searchParam, defaultValue: value.value, onChange: setFilterValue }))),
3900
+ React__default["default"].createElement("td", null, searchParam && value.operator && (React__default["default"].createElement(SearchFilterValueInput, { resourceType: props.resourceType, searchParam: searchParam, defaultValue: value.value, onChange: setFilterValue }))),
3864
3901
  React__default["default"].createElement("td", null,
3865
3902
  value.code && value.operator && value.value && (React__default["default"].createElement(Button, { size: "small", onClick: () => {
3866
3903
  props.onOk(valueRef.current);
@@ -3881,7 +3918,7 @@
3881
3918
  return (React__default["default"].createElement(Dialog, { title: props.title, visible: props.visible, onOk: onOk, onCancel: props.onCancel },
3882
3919
  React__default["default"].createElement("div", { style: { width: 500 } },
3883
3920
  React__default["default"].createElement(Form, { onSubmit: onOk },
3884
- React__default["default"].createElement(SearchFilterValueInput, { schema: props.schema, resourceType: props.resourceType, searchParam: props.searchParam, defaultValue: value, autoFocus: true, onChange: setValue })))));
3921
+ React__default["default"].createElement(SearchFilterValueInput, { resourceType: props.resourceType, searchParam: props.searchParam, defaultValue: value, autoFocus: true, onChange: setValue })))));
3885
3922
  }
3886
3923
 
3887
3924
  function MenuSeparator() {
@@ -4077,7 +4114,7 @@
4077
4114
  function SearchControl(props) {
4078
4115
  var _a, _b, _c, _d;
4079
4116
  const medplum = useMedplum();
4080
- const [schema, setSchema] = React.useState();
4117
+ const [schemaLoaded, setSchemaLoaded] = React.useState(false);
4081
4118
  const [outcome, setOutcome] = React.useState();
4082
4119
  const { search, onLoad } = props;
4083
4120
  const [state, setState] = React.useState({
@@ -4095,7 +4132,7 @@
4095
4132
  React.useEffect(() => {
4096
4133
  setOutcome(undefined);
4097
4134
  medplum
4098
- .search(search.resourceType, core.formatSearchQuery(Object.assign(Object.assign({}, search), { total: 'accurate' })))
4135
+ .search(search.resourceType, core.formatSearchQuery(Object.assign(Object.assign({}, search), { total: 'accurate', fields: undefined })))
4099
4136
  .then((response) => {
4100
4137
  setState(Object.assign(Object.assign({}, stateRef.current), { searchResponse: response }));
4101
4138
  if (onLoad) {
@@ -4187,21 +4224,18 @@
4187
4224
  }
4188
4225
  }
4189
4226
  React.useEffect(() => {
4227
+ setSchemaLoaded(false);
4190
4228
  medplum
4191
4229
  .requestSchema(props.search.resourceType)
4192
- .then((newSchema) => {
4193
- // The schema could have the same object identity,
4194
- // so need to use the spread operator to kick React re-render.
4195
- setSchema(Object.assign({}, newSchema));
4196
- })
4230
+ .then(() => setSchemaLoaded(true))
4197
4231
  .catch(console.log);
4198
4232
  }, [medplum, props.search.resourceType]);
4199
- const typeSchema = (_a = schema === null || schema === void 0 ? void 0 : schema.types) === null || _a === void 0 ? void 0 : _a[props.search.resourceType];
4233
+ const typeSchema = schemaLoaded && ((_a = core.globalSchema === null || core.globalSchema === void 0 ? void 0 : core.globalSchema.types) === null || _a === void 0 ? void 0 : _a[props.search.resourceType]);
4200
4234
  if (!typeSchema) {
4201
4235
  return React__default["default"].createElement(Loading, null);
4202
4236
  }
4203
4237
  const checkboxColumn = props.checkboxesEnabled;
4204
- const fields = getFieldDefinitions(schema, search);
4238
+ const fields = getFieldDefinitions(search);
4205
4239
  const resourceType = search.resourceType;
4206
4240
  const lastResult = state.searchResponse;
4207
4241
  const entries = lastResult === null || lastResult === void 0 ? void 0 : lastResult.entry;
@@ -4251,7 +4285,7 @@
4251
4285
  (resources === null || resources === void 0 ? void 0 : resources.length) === 0 && (React__default["default"].createElement("div", { "data-testid": "empty-search", className: "medplum-empty-search" }, "No results")),
4252
4286
  outcome && (React__default["default"].createElement("div", { "data-testid": "search-error", className: "medplum-empty-search" },
4253
4287
  React__default["default"].createElement("pre", { style: { textAlign: 'left' } }, JSON.stringify(outcome, undefined, 2)))),
4254
- React__default["default"].createElement(SearchPopupMenu, { schema: schema, search: props.search, visible: state.popupVisible, x: state.popupX, y: state.popupY, searchParams: state.popupSearchParams, onPrompt: (searchParam, filter) => {
4288
+ React__default["default"].createElement(SearchPopupMenu, { search: props.search, visible: state.popupVisible, x: state.popupX, y: state.popupY, searchParams: state.popupSearchParams, onPrompt: (searchParam, filter) => {
4255
4289
  setState(Object.assign(Object.assign({}, stateRef.current), { popupVisible: false, filterDialogVisible: true, filterDialogSearchParam: searchParam, filterDialogFilter: filter }));
4256
4290
  }, onChange: (result) => {
4257
4291
  emitSearchChange(result);
@@ -4259,19 +4293,19 @@
4259
4293
  }, onClose: () => {
4260
4294
  setState(Object.assign(Object.assign({}, stateRef.current), { popupVisible: false, popupSearchParams: undefined }));
4261
4295
  } }),
4262
- React__default["default"].createElement(SearchFieldEditor, { schema: schema, search: props.search, visible: stateRef.current.fieldEditorVisible, onOk: (result) => {
4296
+ React__default["default"].createElement(SearchFieldEditor, { search: props.search, visible: stateRef.current.fieldEditorVisible, onOk: (result) => {
4263
4297
  emitSearchChange(result);
4264
4298
  setState(Object.assign(Object.assign({}, stateRef.current), { fieldEditorVisible: false }));
4265
4299
  }, onCancel: () => {
4266
4300
  setState(Object.assign(Object.assign({}, stateRef.current), { fieldEditorVisible: false }));
4267
4301
  } }),
4268
- React__default["default"].createElement(SearchFilterEditor, { schema: schema, search: props.search, visible: stateRef.current.filterEditorVisible, onOk: (result) => {
4302
+ React__default["default"].createElement(SearchFilterEditor, { search: props.search, visible: stateRef.current.filterEditorVisible, onOk: (result) => {
4269
4303
  emitSearchChange(result);
4270
4304
  setState(Object.assign(Object.assign({}, stateRef.current), { filterEditorVisible: false }));
4271
4305
  }, onCancel: () => {
4272
4306
  setState(Object.assign(Object.assign({}, stateRef.current), { filterEditorVisible: false }));
4273
4307
  } }),
4274
- React__default["default"].createElement(SearchFilterValueDialog, { visible: stateRef.current.filterDialogVisible, title: 'Input', schema: schema, resourceType: resourceType, searchParam: state.filterDialogSearchParam, filter: state.filterDialogFilter, defaultValue: '', onOk: (filter) => {
4308
+ React__default["default"].createElement(SearchFilterValueDialog, { visible: stateRef.current.filterDialogVisible, title: 'Input', resourceType: resourceType, searchParam: state.filterDialogSearchParam, filter: state.filterDialogFilter, defaultValue: '', onOk: (filter) => {
4275
4309
  emitSearchChange(addFilter(props.search, filter.code, filter.operator, filter.value));
4276
4310
  setState(Object.assign(Object.assign({}, stateRef.current), { filterDialogVisible: false }));
4277
4311
  }, onCancel: () => {
@@ -4820,6 +4854,8 @@
4820
4854
  return (React__default["default"].createElement(React__default["default"].Fragment, null,
4821
4855
  React__default["default"].createElement(FormSection, { title: "Title", htmlFor: `actionTitle-${action.id}` },
4822
4856
  React__default["default"].createElement(Input, { name: `actionTitle-${action.id}`, defaultValue: action.title, onChange: (newValue) => changeProperty('title', newValue) })),
4857
+ React__default["default"].createElement(FormSection, { title: "Description", htmlFor: `actionDescription-${action.id}` },
4858
+ React__default["default"].createElement(Input, { name: `actionDescription-${action.id}`, defaultValue: action.description, onChange: (newValue) => changeProperty('description', newValue) })),
4823
4859
  React__default["default"].createElement(FormSection, { title: "Type of Action", description: "The type of the action to be performed.", htmlFor: `actionType-${action.id}` },
4824
4860
  React__default["default"].createElement(Select, { name: `actionType-${action.id}`, defaultValue: actionType, onChange: setActionType },
4825
4861
  React__default["default"].createElement("option", null),
@@ -4934,81 +4970,6 @@
4934
4970
  QuestionnaireItemType["reference"] = "reference";
4935
4971
  QuestionnaireItemType["quantity"] = "quantity";
4936
4972
  })(exports.QuestionnaireItemType || (exports.QuestionnaireItemType = {}));
4937
- /**
4938
- * Adds initial values to a questionnaire resource from key value pairs.
4939
- * The values map uses "linkId" as key.
4940
- * The value depends on the questionnaire item type.
4941
- * @param questionnaire The original questionnaire.
4942
- * @param values Key value pairs for initial values.
4943
- * @returns Rewritten questionnaire with initial values.
4944
- */
4945
- function addQuestionnaireInitialValues(questionnaire, values) {
4946
- return Object.assign(Object.assign({}, questionnaire), { item: addInitialValuesToItemArray(questionnaire.item, values) });
4947
- }
4948
- function addInitialValuesToItemArray(items, values) {
4949
- if (!items) {
4950
- return undefined;
4951
- }
4952
- return items.map((item) => addInitialValueToItem(item, values));
4953
- }
4954
- function addInitialValueToItem(item, values) {
4955
- const { linkId, type } = item;
4956
- if (!linkId || !type) {
4957
- return item;
4958
- }
4959
- if (type === 'group') {
4960
- return Object.assign(Object.assign({}, item), { item: addInitialValuesToItemArray(item.item, values) });
4961
- }
4962
- const suppliedValue = values[linkId];
4963
- if (!suppliedValue) {
4964
- return item;
4965
- }
4966
- let initialValue = undefined;
4967
- switch (type) {
4968
- case core.PropertyType.boolean:
4969
- initialValue = { valueBoolean: suppliedValue === 'true' };
4970
- break;
4971
- case core.PropertyType.code:
4972
- case core.PropertyType.Coding:
4973
- initialValue = { valueCoding: { code: suppliedValue } };
4974
- break;
4975
- case core.PropertyType.date:
4976
- initialValue = { valueDate: suppliedValue };
4977
- break;
4978
- case core.PropertyType.dateTime:
4979
- case core.PropertyType.instant:
4980
- initialValue = { valueDateTime: suppliedValue };
4981
- break;
4982
- case core.PropertyType.decimal:
4983
- initialValue = { valueDecimal: parseFloat(suppliedValue) };
4984
- break;
4985
- case core.PropertyType.integer:
4986
- case core.PropertyType.positiveInt:
4987
- case core.PropertyType.unsignedInt:
4988
- initialValue = { valueInteger: parseInt(suppliedValue) };
4989
- break;
4990
- case core.PropertyType.SystemString:
4991
- case core.PropertyType.string:
4992
- case core.PropertyType.markdown:
4993
- initialValue = { valueString: suppliedValue };
4994
- break;
4995
- case core.PropertyType.time:
4996
- initialValue = { valueTime: suppliedValue };
4997
- break;
4998
- case core.PropertyType.uri:
4999
- case core.PropertyType.url:
5000
- initialValue = { valueUri: suppliedValue };
5001
- break;
5002
- case core.PropertyType.canonical:
5003
- case core.PropertyType.Reference:
5004
- initialValue = { valueReference: { reference: suppliedValue } };
5005
- break;
5006
- }
5007
- if (!initialValue) {
5008
- return item;
5009
- }
5010
- return Object.assign(Object.assign({}, item), { initial: [initialValue] });
5011
- }
5012
4973
  function isChoiceQuestion(item) {
5013
4974
  return item.type === 'choice' || item.type === 'open-choice';
5014
4975
  }
@@ -5442,6 +5403,15 @@
5442
5403
  if (!items) {
5443
5404
  return undefined;
5444
5405
  }
5406
+ items.forEach((item) => {
5407
+ var _a, _b;
5408
+ if ((_a = item.id) === null || _a === void 0 ? void 0 : _a.match(/^id-\d+$/)) {
5409
+ nextId = Math.max(nextId, parseInt(item.id.substring(3)) + 1);
5410
+ }
5411
+ if ((_b = item.linkId) === null || _b === void 0 ? void 0 : _b.match(/^q\d+$/)) {
5412
+ nextLinkId = Math.max(nextLinkId, parseInt(item.linkId.substring(1)) + 1);
5413
+ }
5414
+ });
5445
5415
  return items.map((item) => (Object.assign(Object.assign({}, item), { id: item.id || generateId(), item: ensureQuestionnaireItemKeys(item.item), answerOption: ensureQuestionnaireOptionKeys(item.answerOption) })));
5446
5416
  }
5447
5417
  function ensureQuestionnaireOptionKeys(options) {
@@ -5479,6 +5449,7 @@
5479
5449
  React__default["default"].createElement("div", { className: "medplum-request-group-task-checkmark" }, (task === null || task === void 0 ? void 0 : task.status) === 'completed' ? '🗹' : '☐'),
5480
5450
  React__default["default"].createElement("div", { className: "medplum-request-group-task-details" },
5481
5451
  React__default["default"].createElement("div", { className: "medplum-request-group-task-title" }, action.title),
5452
+ action.description && React__default["default"].createElement("div", null, action.description),
5482
5453
  React__default["default"].createElement("div", null,
5483
5454
  "Last edited by\u00A0",
5484
5455
  React__default["default"].createElement(ResourceName, { value: (_e = task === null || task === void 0 ? void 0 : task.meta) === null || _e === void 0 ? void 0 : _e.author }),
@@ -5636,6 +5607,7 @@
5636
5607
  function blame(history) {
5637
5608
  // Convert to array of array of lines
5638
5609
  const versions = history.entry
5610
+ .filter((entry) => !!entry.resource)
5639
5611
  .map((entry) => {
5640
5612
  var _a;
5641
5613
  return ({
@@ -6178,7 +6150,6 @@
6178
6150
  exports.addLastMonthFilter = addLastMonthFilter;
6179
6151
  exports.addMissingFilter = addMissingFilter;
6180
6152
  exports.addNextMonthFilter = addNextMonthFilter;
6181
- exports.addQuestionnaireInitialValues = addQuestionnaireInitialValues;
6182
6153
  exports.addThisMonthFilter = addThisMonthFilter;
6183
6154
  exports.addTodayFilter = addTodayFilter;
6184
6155
  exports.addTomorrowFilter = addTomorrowFilter;