Skip to content

Commit 778ecdd

Browse files
committed
feat(context): add support for custom context path extraction and generation
1 parent 844072e commit 778ecdd

File tree

5 files changed

+117
-4
lines changed

5 files changed

+117
-4
lines changed

.changeset/sad-trains-fix.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
---
2+
"@equinor/fusion-framework-module-context": minor
3+
"@equinor/fusion-framework-cli": minor
4+
---
5+
6+
**@equinor/fusion-framework-react:**
7+
8+
- Enhanced `useAppContextNavigation` to support custom context path extraction and generation. This allows for more flexible navigation handling based on application-specific requirements.
9+
10+
**@equinor/fusion-framework-module-context:**
11+
12+
- Added support for custom context path extraction and generation in `ContextConfigBuilder`, `ContextProvider`, and `ContextModuleConfigurator`.
13+
- Introduced `setContextPathExtractor` and `setContextPathGenerator` methods in `ContextConfigBuilder` to allow developers to define custom logic for extracting and generating context paths.
14+
- Updated `ContextProvider` to utilize `extractContextIdFromPath` and `generatePathFromContext` from the configuration, enabling dynamic path handling.
15+
- Enhanced `ContextModuleConfigurator` to include `extractContextIdFromPath` and `generatePathFromContext` in the module configuration.
16+
17+
If you are using `@equinor/fusion-framework-module-context` and need custom logic for context path handling:
18+
19+
1. Use `setContextPathExtractor` to define how to extract context IDs from paths.
20+
2. Use `setContextPathGenerator` to define how to generate paths based on context items.
21+
22+
Example:
23+
24+
```typescript
25+
builder.setContextPathExtractor((path) => {
26+
// Custom logic to extract context ID from path
27+
return path.match(/\/custom\/(.+)/)?.[1];
28+
});
29+
30+
builder.setContextPathGenerator((context, path) => {
31+
// Custom logic to generate path from context
32+
return path.replace(/^(\/)?custom\/[^/]+(.*)$/, `/app/${item.id}$2`);
33+
});
34+
```
35+
36+
If your portal is generating context paths based on context items, you can now define custom logic for context path handling:
37+
38+
```typescript
39+
contextProvider.currentContext$
40+
.pipe(
41+
map((context) => {
42+
// Custom logic to generate path from context
43+
const path = contextProvider.generatePathFromContext?.(
44+
context,
45+
location.pathname
46+
);
47+
return path ?? fallbackPathGenerator(context, location.pathname);
48+
}),
49+
filter(Boolean)
50+
)
51+
.subscribe((path) => history.push(path));
52+
```

packages/cli/src/bin/dev-portal/useAppContextNavigation.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,16 @@ export const useAppContextNavigation = () => {
6464
}
6565

6666
// extract the context id from the current path
67-
const pathContextId = extractContextIdFromPath(currentPathname);
67+
const pathContextId =
68+
context?.extractContextIdFromPath?.(currentPathname) ??
69+
extractContextIdFromPath(currentPathname);
6870

6971
// generate path to the selected context
7072
const pathname = pathContextId
7173
? item
7274
? // context id exists in the url, replace it with the new context id
73-
currentPathname.replace(pathContextId, item.id)
75+
(context?.generatePathFromContext?.(item, currentPathname) ??
76+
currentPathname.replace(pathContextId, item.id))
7477
: // context was cleared, set the path to the root
7578
'/'
7679
: // could not find context id in the url, set the path to the new context id
@@ -87,7 +90,10 @@ export const useAppContextNavigation = () => {
8790
// if app has its own navigation, use it to navigate
8891
if (appNavigation) {
8992
// update the path of the app navigation, preserving search and hash
90-
appNavigation.replace({ ...appNavigation.path, pathname });
93+
appNavigation.replace({
94+
...appNavigation.path,
95+
pathname,
96+
});
9197
} else {
9298
// update the path of the portal navigation, preserving search and hash
9399
navigation.replace({ ...navigation.path, pathname });

packages/modules/context/src/ContextConfigBuilder.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,25 @@ export class ContextConfigBuilder<
7474
this.config.resolveContext = fn;
7575
}
7676

77+
/**
78+
* Sets the function responsible for extracting the context ID from a given path.
79+
*
80+
* @param fn - A function that defines how to extract the context ID from a path.
81+
* This function should match the type defined in `ContextModuleConfig['extractContextIdFromPath']`.
82+
*/
83+
setContextPathExtractor(fn: ContextModuleConfig['extractContextIdFromPath']) {
84+
this.config.extractContextIdFromPath = fn;
85+
}
86+
87+
/**
88+
* Sets the function responsible for generating a path from the context.
89+
*
90+
* @param fn - A function that takes a context and generates a corresponding path.
91+
*/
92+
setContextPathGenerator(fn: ContextModuleConfig['generatePathFromContext']) {
93+
this.config.generatePathFromContext = fn;
94+
}
95+
7796
setResolveInitialContext(fn: ContextModuleConfig['resolveInitialContext']) {
7897
this.config.resolveInitialContext = fn;
7998
}

packages/modules/context/src/ContextProvider.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,23 @@ export interface IContextProvider {
138138
context: ContextItem<Record<string, unknown>> | null,
139139
opt?: { validate?: boolean; resolve?: boolean },
140140
): Promise<ContextItem<Record<string, unknown>> | null>;
141+
142+
/**
143+
* Method for extracting context id from a path.
144+
*
145+
* @param path path to resolve context from
146+
* @returns the resolved context item id
147+
*/
148+
extractContextIdFromPath?: (path: string) => string | undefined;
149+
150+
/**
151+
* Method for generating path from a context item.
152+
*
153+
* @param context context item to generate path from
154+
* @param path current path
155+
* @returns path for the context item
156+
*/
157+
generatePathFromContext?: (context: ContextItem, path: string) => string | undefined;
141158
}
142159

143160
export class ContextProvider implements IContextProvider {
@@ -205,6 +222,15 @@ export class ContextProvider implements IContextProvider {
205222
config.resolveContext && (this.resolveContext = config.resolveContext?.bind(this));
206223
config.validateContext && (this.validateContext = config.validateContext?.bind(this));
207224

225+
if (config.extractContextIdFromPath) {
226+
// @ts-ignore
227+
this.extractContextIdFromPath = config.extractContextIdFromPath;
228+
}
229+
if (config.generatePathFromContext) {
230+
// @ts-ignore
231+
this.generatePathFromContext = config.generatePathFromContext;
232+
}
233+
208234
this.#contextType = config.contextType;
209235
this.#contextFilter = config.contextFilter;
210236

packages/modules/context/src/configurator.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ export interface ContextModuleConfig {
4040
/** set initial context from parent, will await resolve */
4141
skipInitialContext?: boolean;
4242

43+
extractContextIdFromPath?: (path: string) => string | undefined;
44+
generatePathFromContext?: (context: ContextItem, path: string) => string | undefined;
45+
4346
/**
4447
* Method for generating context query parameters.
4548
*/
@@ -105,7 +108,14 @@ export class ContextModuleConfigurator implements IContextModuleConfigurator {
105108
Promise.resolve({} as Partial<ContextModuleConfig>),
106109
);
107110

108-
config.resolveInitialContext ??= resolveInitialContext();
111+
console.log(999, 'createConfig', config);
112+
113+
config.resolveInitialContext ??= resolveInitialContext({
114+
path: {
115+
extract: config.extractContextIdFromPath,
116+
validate: config.extractContextIdFromPath ? () => true : undefined,
117+
},
118+
});
109119

110120
// TODO - make less lazy
111121
config.client ??= await (async (): Promise<ContextModuleConfig['client']> => {

0 commit comments

Comments
 (0)