@ews-admin/global-design-system 1.27.0 → 1.29.0
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.
- package/dist/components/SearchAutocomplete/SearchAutocomplete.d.ts +8 -1
- package/dist/components/SearchAutocomplete/SearchAutocomplete.d.ts.map +1 -1
- package/dist/components/Select/Select.d.ts +15 -0
- package/dist/components/Select/Select.d.ts.map +1 -1
- package/dist/index.d.ts +34 -1
- package/dist/index.esm.js +48 -7
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +48 -7
- package/dist/index.js.map +1 -1
- package/dist/molecules/SpecialtySearchAutocomplete/SpecialtySearchAutocomplete.d.ts +6 -0
- package/dist/molecules/SpecialtySearchAutocomplete/SpecialtySearchAutocomplete.d.ts.map +1 -1
- package/dist/utils/env-config.d.ts +5 -0
- package/dist/utils/env-config.d.ts.map +1 -1
- package/package.json +3 -1
- package/src/components/SearchAutocomplete/SearchAutocomplete.tsx +14 -1
- package/src/components/Select/Select.tsx +20 -2
- package/src/molecules/SpecialtySearchAutocomplete/SpecialtySearchAutocomplete.tsx +31 -1
- package/src/utils/env-config.ts +34 -1
|
@@ -17,6 +17,12 @@ export interface SpecialtySearchAutocompleteProps {
|
|
|
17
17
|
title?: string;
|
|
18
18
|
label?: string;
|
|
19
19
|
getSelectedCountText?: (count: number) => React.ReactNode;
|
|
20
|
+
/**
|
|
21
|
+
* MED-1499: accessible label / tooltip of the button that removes a
|
|
22
|
+
* selected specialty from its chip. Defaults to the English
|
|
23
|
+
* "Remove specialty".
|
|
24
|
+
*/
|
|
25
|
+
removeSpecialtyLabel?: string;
|
|
20
26
|
}
|
|
21
27
|
export declare const SpecialtySearchAutocomplete: React.FC<SpecialtySearchAutocompleteProps>;
|
|
22
28
|
//# sourceMappingURL=SpecialtySearchAutocomplete.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SpecialtySearchAutocomplete.d.ts","sourceRoot":"","sources":["../../../src/molecules/SpecialtySearchAutocomplete/SpecialtySearchAutocomplete.tsx"],"names":[],"mappings":"AACA,OAAO,KAA2C,MAAM,OAAO,CAAC;AAIhE,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;CACtD;AAED,MAAM,WAAW,gCAAgC;IAC/C,mBAAmB,EAAE,SAAS,EAAE,CAAC;IACjC,mBAAmB,EAAE,CAAC,WAAW,EAAE,SAAS,EAAE,KAAK,IAAI,CAAC;IACxD,WAAW,EAAE,SAAS,EAAE,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oBAAoB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,KAAK,CAAC,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"SpecialtySearchAutocomplete.d.ts","sourceRoot":"","sources":["../../../src/molecules/SpecialtySearchAutocomplete/SpecialtySearchAutocomplete.tsx"],"names":[],"mappings":"AACA,OAAO,KAA2C,MAAM,OAAO,CAAC;AAIhE,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;CACtD;AAED,MAAM,WAAW,gCAAgC;IAC/C,mBAAmB,EAAE,SAAS,EAAE,CAAC;IACjC,mBAAmB,EAAE,CAAC,WAAW,EAAE,SAAS,EAAE,KAAK,IAAI,CAAC;IACxD,WAAW,EAAE,SAAS,EAAE,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oBAAoB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,KAAK,CAAC,SAAS,CAAC;IAC1D;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,eAAO,MAAM,2BAA2B,EAAE,KAAK,CAAC,EAAE,CAChD,gCAAgC,CAuLjC,CAAC"}
|
|
@@ -25,6 +25,11 @@ export interface EnvConfig {
|
|
|
25
25
|
* For local development, sensible defaults are provided but can be overridden
|
|
26
26
|
* (e.g. to point BFF URL at a mock server).
|
|
27
27
|
*
|
|
28
|
+
* When env is empty (build without VITE_ENV), the environment is derived from
|
|
29
|
+
* the current host instead of always assuming local, so a deployed build never
|
|
30
|
+
* links to http://local.*.medecine360local.com. A console warning names the
|
|
31
|
+
* derived environment so the misconfigured build stays visible.
|
|
32
|
+
*
|
|
28
33
|
* @param env - The environment name (VITE_ENV value)
|
|
29
34
|
* @param overrides - Optional URL overrides (e.g. for local mock servers)
|
|
30
35
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env-config.d.ts","sourceRoot":"","sources":["../../src/utils/env-config.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,KAAK,GAAG,IAAI,GAAG,MAAM,CAAC;AAE1D,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,WAAW,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;CAClB;
|
|
1
|
+
{"version":3,"file":"env-config.d.ts","sourceRoot":"","sources":["../../src/utils/env-config.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,KAAK,GAAG,IAAI,GAAG,MAAM,CAAC;AAE1D,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,WAAW,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;CAClB;AA6DD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,MAAM,EACX,SAAS,CAAC,EAAE,kBAAkB,GAC7B,SAAS,CAeX"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ews-admin/global-design-system",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.29.0",
|
|
4
4
|
"description": "EWS Global Design System - Reusable components for EWS applications",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.esm.js",
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
"@storybook/react": "^7.5.0",
|
|
42
42
|
"@storybook/react-vite": "^7.5.0",
|
|
43
43
|
"@storybook/testing-library": "^0.2.2",
|
|
44
|
+
"@testing-library/react": "14",
|
|
44
45
|
"@types/react": "^18.2.37",
|
|
45
46
|
"@types/react-dom": "^18.2.15",
|
|
46
47
|
"@typescript-eslint/eslint-plugin": "^8.44.0",
|
|
@@ -51,6 +52,7 @@
|
|
|
51
52
|
"eslint-plugin-react": "^7.37.5",
|
|
52
53
|
"eslint-plugin-react-hooks": "^5.2.0",
|
|
53
54
|
"jest": "29",
|
|
55
|
+
"jest-environment-jsdom": "29",
|
|
54
56
|
"postcss": "^8.5.6",
|
|
55
57
|
"rollup": "^4.6.1",
|
|
56
58
|
"rollup-plugin-dts": "^6.1.0",
|
|
@@ -22,6 +22,13 @@ interface SearchAutocompleteProps<T extends SearchableEntity> {
|
|
|
22
22
|
minSearchLength?: number;
|
|
23
23
|
debounceTime?: number;
|
|
24
24
|
error?: string;
|
|
25
|
+
/**
|
|
26
|
+
* MED-1501: builds the hint appended to the placeholder while the search
|
|
27
|
+
* term is still too short. Return `null` (or an empty string) to suppress
|
|
28
|
+
* the hint entirely. Defaults to the English "(min. N characters)", so
|
|
29
|
+
* callers that do not pass it keep today's placeholder.
|
|
30
|
+
*/
|
|
31
|
+
getMinCharactersHint?: (minSearchLength: number) => string | null;
|
|
25
32
|
}
|
|
26
33
|
|
|
27
34
|
export function SearchAutocomplete<T extends SearchableEntity>({
|
|
@@ -37,6 +44,7 @@ export function SearchAutocomplete<T extends SearchableEntity>({
|
|
|
37
44
|
minSearchLength = 2,
|
|
38
45
|
debounceTime = 300,
|
|
39
46
|
error,
|
|
47
|
+
getMinCharactersHint = (min) => `(min. ${min} characters)`,
|
|
40
48
|
}: SearchAutocompleteProps<T>) {
|
|
41
49
|
const [searchTerm, setSearchTerm] = useState("");
|
|
42
50
|
const [entities, setEntities] = useState<T[]>([]);
|
|
@@ -171,12 +179,17 @@ export function SearchAutocomplete<T extends SearchableEntity>({
|
|
|
171
179
|
onSelect("");
|
|
172
180
|
};
|
|
173
181
|
|
|
182
|
+
const minCharactersHint = getMinCharactersHint(minSearchLength);
|
|
183
|
+
const inputPlaceholder = minCharactersHint
|
|
184
|
+
? `${placeholder} ${minCharactersHint}`
|
|
185
|
+
: placeholder;
|
|
186
|
+
|
|
174
187
|
return (
|
|
175
188
|
<div className="relative" ref={dropdownRef}>
|
|
176
189
|
<div className="relative">
|
|
177
190
|
<Input
|
|
178
191
|
type="text"
|
|
179
|
-
placeholder={
|
|
192
|
+
placeholder={inputPlaceholder}
|
|
180
193
|
value={selectedEntity ? "" : searchTerm}
|
|
181
194
|
onChange={handleSearchChange}
|
|
182
195
|
onFocus={() =>
|
|
@@ -107,6 +107,21 @@ export interface SelectProps<T = unknown> {
|
|
|
107
107
|
* avatar menu).
|
|
108
108
|
*/
|
|
109
109
|
renderTrigger?: () => React.ReactNode;
|
|
110
|
+
/**
|
|
111
|
+
* MED-1501: placeholder of the in-dropdown search input, so the caller can
|
|
112
|
+
* translate it. Defaults to the English "Search options...".
|
|
113
|
+
*/
|
|
114
|
+
searchPlaceholder?: string;
|
|
115
|
+
/**
|
|
116
|
+
* MED-1501: text shown when a search matches no option, so the caller can
|
|
117
|
+
* translate it. Defaults to the English "No options found".
|
|
118
|
+
*/
|
|
119
|
+
noOptionsFoundText?: string;
|
|
120
|
+
/**
|
|
121
|
+
* MED-1501: text shown when there is no option at all, so the caller can
|
|
122
|
+
* translate it. Defaults to the English "No options available".
|
|
123
|
+
*/
|
|
124
|
+
noOptionsAvailableText?: string;
|
|
110
125
|
}
|
|
111
126
|
|
|
112
127
|
const Select = forwardRef<HTMLDivElement, SelectProps>(
|
|
@@ -133,6 +148,9 @@ const Select = forwardRef<HTMLDivElement, SelectProps>(
|
|
|
133
148
|
renderOption,
|
|
134
149
|
renderValue,
|
|
135
150
|
renderTrigger,
|
|
151
|
+
searchPlaceholder = "Search options...",
|
|
152
|
+
noOptionsFoundText = "No options found",
|
|
153
|
+
noOptionsAvailableText = "No options available",
|
|
136
154
|
...props
|
|
137
155
|
},
|
|
138
156
|
ref
|
|
@@ -495,7 +513,7 @@ const Select = forwardRef<HTMLDivElement, SelectProps>(
|
|
|
495
513
|
type="text"
|
|
496
514
|
value={searchTerm}
|
|
497
515
|
onChange={(e) => setSearchTerm(e.target.value)}
|
|
498
|
-
placeholder=
|
|
516
|
+
placeholder={searchPlaceholder}
|
|
499
517
|
leftIcon={
|
|
500
518
|
<Search className="w-4 h-4 text-ews-gray-400" />
|
|
501
519
|
}
|
|
@@ -511,7 +529,7 @@ const Select = forwardRef<HTMLDivElement, SelectProps>(
|
|
|
511
529
|
>
|
|
512
530
|
{filteredOptions.length === 0 ? (
|
|
513
531
|
<div className="px-3 py-2 text-sm text-ews-gray-500">
|
|
514
|
-
{searchTerm ?
|
|
532
|
+
{searchTerm ? noOptionsFoundText : noOptionsAvailableText}
|
|
515
533
|
</div>
|
|
516
534
|
) : (
|
|
517
535
|
filteredOptions.map((option, index) => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Stethoscope } from "lucide-react";
|
|
1
|
+
import { Stethoscope, X } from "lucide-react";
|
|
2
2
|
import React, { useCallback, useEffect, useState } from "react";
|
|
3
3
|
import { MultiSearchAutocomplete } from "../../components/MultiSearchAutocomplete";
|
|
4
4
|
import { cn } from "../../utils";
|
|
@@ -22,6 +22,12 @@ export interface SpecialtySearchAutocompleteProps {
|
|
|
22
22
|
title?: string;
|
|
23
23
|
label?: string;
|
|
24
24
|
getSelectedCountText?: (count: number) => React.ReactNode;
|
|
25
|
+
/**
|
|
26
|
+
* MED-1499: accessible label / tooltip of the button that removes a
|
|
27
|
+
* selected specialty from its chip. Defaults to the English
|
|
28
|
+
* "Remove specialty".
|
|
29
|
+
*/
|
|
30
|
+
removeSpecialtyLabel?: string;
|
|
25
31
|
}
|
|
26
32
|
|
|
27
33
|
export const SpecialtySearchAutocomplete: React.FC<
|
|
@@ -38,6 +44,7 @@ export const SpecialtySearchAutocomplete: React.FC<
|
|
|
38
44
|
title = "Medical Specialties",
|
|
39
45
|
label = "Select Specialties",
|
|
40
46
|
getSelectedCountText,
|
|
47
|
+
removeSpecialtyLabel = "Remove specialty",
|
|
41
48
|
}) => {
|
|
42
49
|
const [filteredSpecialties, setFilteredSpecialties] = useState<Specialty[]>(
|
|
43
50
|
[]
|
|
@@ -83,6 +90,19 @@ export const SpecialtySearchAutocomplete: React.FC<
|
|
|
83
90
|
[onSpecialtiesChange, maxSelections]
|
|
84
91
|
);
|
|
85
92
|
|
|
93
|
+
// MED-1499: a selected specialty must be removable from its chip, not only
|
|
94
|
+
// by toggling it off again in the dropdown list.
|
|
95
|
+
const handleRemoveSpecialty = useCallback(
|
|
96
|
+
(specialtyToRemove: Specialty) => {
|
|
97
|
+
onSpecialtiesChange(
|
|
98
|
+
selectedSpecialties.filter(
|
|
99
|
+
(specialty) => specialty.id !== specialtyToRemove.id
|
|
100
|
+
)
|
|
101
|
+
);
|
|
102
|
+
},
|
|
103
|
+
[onSpecialtiesChange, selectedSpecialties]
|
|
104
|
+
);
|
|
105
|
+
|
|
86
106
|
const getEntityById = useCallback(
|
|
87
107
|
async (id: string): Promise<Specialty | undefined> => {
|
|
88
108
|
return availableSpecialties.find((specialty) => specialty.id === id);
|
|
@@ -127,6 +147,16 @@ export const SpecialtySearchAutocomplete: React.FC<
|
|
|
127
147
|
renderSelectedItem={(specialty) => (
|
|
128
148
|
<span className="inline-flex items-center px-3 py-1 text-sm font-medium rounded-full border bg-ews-primary/10 text-ews-primary border-ews-primary/20">
|
|
129
149
|
{specialty.label}
|
|
150
|
+
<button
|
|
151
|
+
type="button"
|
|
152
|
+
onClick={() => handleRemoveSpecialty(specialty)}
|
|
153
|
+
className="ml-2 text-ews-primary/60 hover:text-ews-primary"
|
|
154
|
+
disabled={disabled}
|
|
155
|
+
title={removeSpecialtyLabel}
|
|
156
|
+
aria-label={removeSpecialtyLabel}
|
|
157
|
+
>
|
|
158
|
+
<X className="w-3 h-3" />
|
|
159
|
+
</button>
|
|
130
160
|
</span>
|
|
131
161
|
)}
|
|
132
162
|
renderListItem={(specialty, isSelected) => (
|
package/src/utils/env-config.ts
CHANGED
|
@@ -56,6 +56,28 @@ function buildUrls(environment: Environment): Omit<EnvConfig, "env"> {
|
|
|
56
56
|
};
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Derives the environment from the host the app is served from, for builds
|
|
61
|
+
* that did not set VITE_ENV. Hosts actually served (Traefik rules in
|
|
62
|
+
* infrastructure/medecine360-hetzner/deployment/docker-stack.*.yml):
|
|
63
|
+
* - local: localhost, 127.0.0.1, *.medecine360local.com (local.app., api., ...)
|
|
64
|
+
* - dev: dev.{app,login,dashboard,admin,api}.medecine360.com
|
|
65
|
+
* - qa: qa.{app,login,dashboard,admin,api}.medecine360.com
|
|
66
|
+
* - prod: medecine360.com, {login,dashboard,admin,api}.medecine360.com
|
|
67
|
+
* Any other host (or no window, e.g. SSR or tests) falls back to local.
|
|
68
|
+
*/
|
|
69
|
+
function environmentFromHost(): Environment {
|
|
70
|
+
const hostname =
|
|
71
|
+
typeof window !== "undefined" ? window.location?.hostname?.toLowerCase() : undefined;
|
|
72
|
+
if (!hostname) return "local";
|
|
73
|
+
if (hostname === "medecine360.com" || hostname.endsWith(".medecine360.com")) {
|
|
74
|
+
if (hostname.startsWith("dev.")) return "dev";
|
|
75
|
+
if (hostname.startsWith("qa.")) return "qa";
|
|
76
|
+
return "prod";
|
|
77
|
+
}
|
|
78
|
+
return "local";
|
|
79
|
+
}
|
|
80
|
+
|
|
59
81
|
/**
|
|
60
82
|
* Creates an environment configuration with all derived URLs.
|
|
61
83
|
*
|
|
@@ -65,6 +87,11 @@ function buildUrls(environment: Environment): Omit<EnvConfig, "env"> {
|
|
|
65
87
|
* For local development, sensible defaults are provided but can be overridden
|
|
66
88
|
* (e.g. to point BFF URL at a mock server).
|
|
67
89
|
*
|
|
90
|
+
* When env is empty (build without VITE_ENV), the environment is derived from
|
|
91
|
+
* the current host instead of always assuming local, so a deployed build never
|
|
92
|
+
* links to http://local.*.medecine360local.com. A console warning names the
|
|
93
|
+
* derived environment so the misconfigured build stays visible.
|
|
94
|
+
*
|
|
68
95
|
* @param env - The environment name (VITE_ENV value)
|
|
69
96
|
* @param overrides - Optional URL overrides (e.g. for local mock servers)
|
|
70
97
|
*/
|
|
@@ -72,7 +99,13 @@ export function createEnvConfig(
|
|
|
72
99
|
env: string,
|
|
73
100
|
overrides?: EnvConfigOverrides,
|
|
74
101
|
): EnvConfig {
|
|
75
|
-
|
|
102
|
+
let environment = env as Environment;
|
|
103
|
+
if (!environment) {
|
|
104
|
+
environment = environmentFromHost();
|
|
105
|
+
console.warn(
|
|
106
|
+
`[env-config] VITE_ENV is not set; using "${environment}" derived from the current host.`,
|
|
107
|
+
);
|
|
108
|
+
}
|
|
76
109
|
const derived = buildUrls(environment);
|
|
77
110
|
|
|
78
111
|
return {
|