-
Notifications
You must be signed in to change notification settings - Fork 40
feat(react): add FeatureFlag component #1164
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
weyert
wants to merge
56
commits into
main
Choose a base branch
from
add-feature-flag-component
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
56 commits
Select commit
Hold shift + click to select a range
c487cd1
feat(react): add FeatureFlag component
weyert 0213bed
feat: add polyfill for react use hook (#1157)
beeme1mr 0f8e277
chore: use server src not dist in nest tests (#1166)
toddbaert daf458d
chore(main): release core 1.8.0 (#1155)
openfeaturebot 1ada096
feat(angular): add option for initial context injection
lukas-reining cd10e6f
chore(main): release angular-sdk 0.0.11 (#1167)
openfeaturebot db70768
chore(deps): update angular-eslint monorepo to v19 (major) (#1140)
renovate[bot] 04b76e2
chore(deps): update dependency esbuild to ^0.25.0 [security] (#1145)
renovate[bot] d48c683
feat(angular): add docs for setting evaluation context in angular (#1…
lukas-reining d403fae
chore(main): release angular-sdk 0.0.12 (#1171)
openfeaturebot 20978c1
chore(main): release nestjs-sdk 0.2.3 (#1144)
openfeaturebot dc79d47
chore(main): release server-sdk 1.18.0 (#1153)
openfeaturebot 646c879
chore(main): release web-sdk 1.5.0 (#1156)
openfeaturebot b4118cb
chore(main): release react-sdk 1.0.0 (#1154)
openfeaturebot fa719b6
docs: fix readme typo (#1174)
lukas-reining da1f737
chore: use publish env
toddbaert 0428b0d
chore(nest): allow nestjs version 11 (#1176)
lukas-reining af4c8d5
chore(main): release nestjs-sdk 0.2.4 (#1177)
openfeaturebot f5b6788
chore(main): release angular-sdk 0.0.13 (#1175)
openfeaturebot 8867f35
docs: Clarify the behavior of setProviderAndWait (#1180)
beeme1mr 5501404
feat: adds RequireFlagsEnabled decorator (#1159)
kaushalkapasi 5c86989
chore: regenerate package lock (#1184)
beeme1mr a9e837c
chore(deps): update dependency @types/node to v20.17.31 (#1138)
renovate[bot] 7e2a753
chore(deps): update dependency @types/node to v22 (#1067)
renovate[bot] 51037db
chore(deps): update dependency @types/supertest to v6.0.3 (#1169)
renovate[bot] e9f2c41
chore(deps): update dependency rollup to v4.40.2 (#1131)
renovate[bot] 7bbde6e
chore(deps): update dependency @rollup/plugin-typescript to v12 (#1059)
renovate[bot] 3a3eb8b
chore(deps): update dependency esbuild to v0.25.4 (#1185)
renovate[bot] fadecb1
chore(deps): update dependency @types/node to v22.15.17 (#1190)
renovate[bot] 9c5f14d
chore(deps): update dependency eslint-plugin-jsdoc to v50.6.14 (#1191)
renovate[bot] b03c27c
fix(angular): add license and url field to package.json
lukas-reining ba2c761
chore(main): release angular-sdk 0.0.14 (#1178)
openfeaturebot 4e335ac
chore(deps): update dependency rxjs to v7.8.2 (#1193)
renovate[bot] 7d2fb3a
chore(deps): update dependency jest-preset-angular to v14.5.5 (#1192)
renovate[bot] 5e276c9
fix(angular): update docs (#1200)
lukas-reining 81ce66a
chore(main): release angular-sdk 0.0.15 (#1201)
openfeaturebot 763c513
fix: environment for npm publish (#1202)
lukas-reining 461d9dc
chore(main): release nestjs-sdk 0.2.5 (#1183)
openfeaturebot 4fa96df
chore: update node to v20+ (#1203)
toddbaert d92ffcd
chore(deps): update dependency prettier to v3.5.3 (#1195)
renovate[bot] 5c1b3d6
chore(deps): update dependency @types/node to v22.15.23 (#1198)
renovate[bot] b475a6e
refactor(telemetry): update telemetry attributes and remove unused ev…
beeme1mr 7f95d1e
chore(deps): update dependency esbuild to v0.25.5 (#1204)
renovate[bot] a806eac
chore(deps): update dependency rollup to v4.41.1 (#1205)
renovate[bot] 035e377
chore(deps): update dependency eslint-plugin-jsdoc to v50.7.0 (#1199)
renovate[bot] ec0cffa
chore(deps): update dependency rollup-plugin-dts to v6.2.1 (#1196)
renovate[bot] f7a18fb
chore: update workflows to node 20 (#1209)
toddbaert 06fd1e4
chore(deps): update dependency eslint-plugin-jsdoc to v50.7.1 (#1211)
renovate[bot] 5b9d59c
chore(main): release core 1.8.1 (#1208)
openfeaturebot d00fc09
feat: add evaluation-scoped hook data (#1216)
beeme1mr b0fd0d7
feat\!: improve FeatureFlag component API with predicate support
weyert c24b83f
test: update FeatureFlag tests for new API
weyert 17d0c95
chore: update package-lock.json
weyert 0aa2db9
chore: Merge branch 'main' into add-feature-flag-component
weyert 7398a9b
fix: improve the README file of the React package
weyert 70e6407
feat(react): add function-based fallback support to FeatureFlag compo…
weyert File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
import React from 'react'; | ||
import { useFlag } from '../evaluation'; | ||
import type { FlagQuery } from '../query'; | ||
import type { FlagValue, EvaluationDetails } from '@openfeature/core'; | ||
|
||
/** | ||
* Default predicate function that checks if the expected value equals the actual flag value. | ||
* @param {T} expected The expected value to match against | ||
* @param {EvaluationDetails<T>} actual The evaluation details containing the actual flag value | ||
* @returns {boolean} true if the values match, false otherwise | ||
*/ | ||
function equals<T extends FlagValue>(expected: T, actual: EvaluationDetails<T>): boolean { | ||
return expected === actual.value; | ||
} | ||
|
||
/** | ||
* Props for the FeatureFlag component that conditionally renders content based on feature flag state. | ||
* @interface FeatureFlagProps | ||
*/ | ||
interface FeatureFlagProps<T extends FlagValue = FlagValue> { | ||
/** | ||
* The key of the feature flag to evaluate. | ||
*/ | ||
flagKey: string; | ||
|
||
/** | ||
* Optional value to match against the feature flag value. | ||
* If provided, the component will only render children when the flag value matches this value. | ||
* By default, strict equality (===) is used for comparison. | ||
* If a boolean, it will check if the flag is enabled (true) or disabled (false). | ||
* If a string, it will check if the flag variant equals this string. | ||
*/ | ||
match?: T; | ||
|
||
/** | ||
* Optional predicate function for custom matching logic. | ||
* If provided, this function will be used instead of the default equality check. | ||
* @param expected The expected value (from match prop) | ||
* @param actual The evaluation details | ||
* @returns true if the condition is met, false otherwise | ||
*/ | ||
predicate?: (expected: T | undefined, actual: EvaluationDetails<T>) => boolean; | ||
|
||
/** | ||
* Default value to use when the feature flag is not found. | ||
*/ | ||
defaultValue: T; | ||
|
||
/** | ||
* Content to render when the feature flag condition is met. | ||
* Can be a React node or a function that receives flag query details and returns a React node. | ||
*/ | ||
children: React.ReactNode | ((details: FlagQuery<T>) => React.ReactNode); | ||
|
||
/** | ||
* Optional content to render when the feature flag condition is not met. | ||
* Can be a React node or a function that receives evaluation details and returns a React node. | ||
*/ | ||
fallback?: React.ReactNode | ((details: EvaluationDetails<T>) => React.ReactNode); | ||
} | ||
|
||
/** | ||
* FeatureFlag component that conditionally renders its children based on the evaluation of a feature flag. | ||
* @param {FeatureFlagProps} props The properties for the FeatureFlag component. | ||
* @returns {React.ReactElement | null} The rendered component or null if the feature is not enabled. | ||
*/ | ||
export function FeatureFlag<T extends FlagValue = FlagValue>({ | ||
flagKey, | ||
match, | ||
predicate, | ||
defaultValue, | ||
children, | ||
fallback = null, | ||
}: FeatureFlagProps<T>): React.ReactElement | null { | ||
const details = useFlag(flagKey, defaultValue, { | ||
updateOnContextChanged: true, | ||
}); | ||
|
||
// If the flag evaluation failed, we render the fallback | ||
if (details.reason === 'ERROR') { | ||
const fallbackNode: React.ReactNode = typeof fallback === 'function' ? fallback(details.details as EvaluationDetails<T>) : fallback; | ||
return <>{fallbackNode}</>; | ||
} | ||
|
||
// Use custom predicate if provided, otherwise use default matching logic | ||
let shouldRender = false; | ||
if (predicate) { | ||
shouldRender = predicate(match, details.details as EvaluationDetails<T>); | ||
} else if (match !== undefined) { | ||
// Default behavior: check if match value equals flag value | ||
shouldRender = equals(match, details.details as EvaluationDetails<T>); | ||
} else { | ||
// If no match value is provided, render if flag is truthy | ||
shouldRender = Boolean(details.value); | ||
} | ||
|
||
if (shouldRender) { | ||
const childNode: React.ReactNode = typeof children === 'function' ? children(details as FlagQuery<T>) : children; | ||
return <>{childNode}</>; | ||
} | ||
|
||
const fallbackNode: React.ReactNode = typeof fallback === 'function' ? fallback(details.details as EvaluationDetails<T>) : fallback; | ||
return <>{fallbackNode}</>; | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './FeatureFlag'; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please specify here exactly what comparison is used\ (
==
vs===
, etc).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if we do the default mentioned below, we can just describe it as
By default, strict equality is used
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added in the jsdoc comment