Skip to content

Commit

Permalink
feat!: react support (#145)
Browse files Browse the repository at this point in the history
  • Loading branch information
nicklasl committed May 31, 2024
1 parent ae6bd43 commit 0493005
Show file tree
Hide file tree
Showing 26 changed files with 1,271 additions and 279 deletions.
95 changes: 95 additions & 0 deletions api/react.api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
## API Report File for "@spotify-confidence/react"

> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts

import { Closer } from '@spotify-confidence/sdk';
import { Confidence } from '@spotify-confidence/sdk';
import { Configuration } from '@spotify-confidence/sdk';
import { Context } from '@spotify-confidence/sdk';
import { EventSender } from '@spotify-confidence/sdk';
import { FC } from 'react';
import { FlagEvaluation } from '@spotify-confidence/sdk';
import { FlagResolver } from '@spotify-confidence/sdk';
import { PropsWithChildren } from 'react';
import { State } from '@spotify-confidence/sdk';
import { StateObserver } from '@spotify-confidence/sdk';
import { Trackable } from '@spotify-confidence/sdk';
import { Value } from '@spotify-confidence/sdk';

// Warning: (ae-missing-release-tag) "ConfidenceProvider" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
// Warning: (ae-missing-release-tag) "ConfidenceProvider" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export type ConfidenceProvider = FC<PropsWithChildren<{
confidence: Confidence;
}>> & {
WithContext: FC<PropsWithChildren<{
context: Context;
}>>;
};

// @public (undocumented)
export const ConfidenceProvider: ConfidenceProvider;

// Warning: (ae-missing-release-tag) "ConfidenceReact" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export class ConfidenceReact implements EventSender, Trackable, FlagResolver {
constructor(delegate: Confidence);
// (undocumented)
clearContext(): void;
// (undocumented)
get config(): Configuration;
// @internal (undocumented)
readonly delegate: Confidence;
// (undocumented)
evaluateFlag<T extends Value>(path: string, defaultValue: T): FlagEvaluation<Value.Widen<T>>;
// (undocumented)
getContext(): Context;
// (undocumented)
getFlag<T extends Value>(path: string, defaultValue: T): Promise<Value.Widen<T>>;
// (undocumented)
setContext(context: Context): void;
// @internal (undocumented)
get state(): State;
// (undocumented)
subscribe(onStateChange?: StateObserver | undefined): () => void;
// (undocumented)
track(name: string, message?: Value.Struct): void;
// (undocumented)
track(manager: Trackable.Manager): Closer;
// (undocumented)
useEvaluateFlag<T extends Value>(path: string, defaultValue: T): FlagEvaluation<Value.Widen<T>>;
// (undocumented)
useFlag<T extends Value>(path: string, defaultValue: T): Value.Widen<T>;
// (undocumented)
useWithContext(context: Context): ConfidenceReact;
// (undocumented)
withContext(context: Context): ConfidenceReact;
}

// Warning: (ae-missing-release-tag) "useConfidence" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const useConfidence: () => ConfidenceReact;

// Warning: (ae-missing-release-tag) "useEvaluateFlag" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export function useEvaluateFlag<T extends Value>(path: string, defaultValue: T, confidence?: ConfidenceReact): FlagEvaluation<Value.Widen<T>>;

// Warning: (ae-missing-release-tag) "useFlag" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export function useFlag<T extends Value>(path: string, defaultValue: T, confidence?: ConfidenceReact): Value.Widen<T>;

// Warning: (ae-missing-release-tag) "useWithContext" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export function useWithContext(context: Context, confidence?: ConfidenceReact): ConfidenceReact;

// (No @packageDocumentation comment for this package)

```
70 changes: 64 additions & 6 deletions api/sdk.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,25 @@
```ts

// Warning: (ae-forgotten-export) The symbol "Trackable" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "Closer" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
// Warning: (ae-missing-release-tag) "Closer" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export namespace Closer {
// (undocumented)
export function combine(...closers: Closer[]): Closer;
}

// @public (undocumented)
export type Closer = () => void;

// Warning: (ae-missing-release-tag) "Confidence" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export class Confidence implements EventSender, Trackable, FlagResolver {
constructor(config: Configuration, parent?: Confidence);
// (undocumented)
clearContext(): void;
// Warning: (ae-forgotten-export) The symbol "Configuration" needs to be exported by the entry point index.d.ts
//
// (undocumented)
readonly config: Configuration;
// Warning: (ae-forgotten-export) The symbol "Subscribe" needs to be exported by the entry point index.d.ts
Expand All @@ -27,17 +36,21 @@ export class Confidence implements EventSender, Trackable, FlagResolver {
// (undocumented)
evaluateFlag<T extends Value>(path: string, defaultValue: T): FlagEvaluation<Value.Widen<T>>;
// (undocumented)
get flagState(): State;
// (undocumented)
getContext(): Context;
// (undocumented)
getFlag<T extends Value>(path: string, defaultValue: T): Promise<Value.Widen<T>>;
// Warning: (ae-forgotten-export) The symbol "AccessiblePromise" needs to be exported by the entry point index.d.ts
//
// (undocumented)
setContext(context: Context): void;
protected resolveFlags(): AccessiblePromise<void>;
// (undocumented)
setContext(context: Context): boolean;
// (undocumented)
subscribe(onStateChange?: StateObserver): () => void;
// (undocumented)
track(name: string, data?: EventData): void;
// Warning: (ae-forgotten-export) The symbol "Closer" needs to be exported by the entry point index.d.ts
//
// (undocumented)
track(manager: Trackable.Manager): Closer;
// (undocumented)
Expand Down Expand Up @@ -68,6 +81,26 @@ export interface ConfidenceOptions {
timeout: number;
}

// Warning: (ae-missing-release-tag) "Configuration" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export interface Configuration {
// (undocumented)
readonly environment: 'client' | 'backend';
// Warning: (ae-forgotten-export) The symbol "EventSenderEngine" needs to be exported by the entry point index.d.ts
//
// @internal (undocumented)
readonly eventSenderEngine: EventSenderEngine;
// Warning: (ae-forgotten-export) The symbol "FlagResolverClient" needs to be exported by the entry point index.d.ts
//
// @internal (undocumented)
readonly flagResolverClient: FlagResolverClient;
// (undocumented)
readonly logger: Logger;
// (undocumented)
readonly timeout: number;
}

// Warning: (ae-missing-release-tag) "Context" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
Expand Down Expand Up @@ -187,6 +220,27 @@ export type State = 'NOT_READY' | 'READY' | 'STALE' | 'ERROR';
// @public (undocumented)
export type StateObserver = (state: State) => void;

// Warning: (ae-missing-release-tag) "Trackable" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
// Warning: (ae-missing-release-tag) "Trackable" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export namespace Trackable {
// (undocumented)
export type Cleanup = void | Closer;
// (undocumented)
export type Controller = Pick<Confidence, 'setContext' | 'track' | 'config'>;
// (undocumented)
export type Manager = (controller: Controller) => Cleanup;
// (undocumented)
export function setup(controller: Controller, manager: Manager): Closer;
}

// @public (undocumented)
export interface Trackable {
// (undocumented)
track(manager: Trackable.Manager): Closer;
}

// Warning: (ae-missing-release-tag) "Value" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
// Warning: (ae-missing-release-tag) "Value" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
Expand All @@ -209,6 +263,8 @@ export namespace Value {
// (undocumented)
export function clone<T extends Value>(value: T): T;
// (undocumented)
export function deserialize(data: string): Value;
// (undocumented)
export function equal(value1: Value, value2: Value): boolean;
// (undocumented)
export function get(struct: Struct | undefined, path: string): Value;
Expand All @@ -225,6 +281,8 @@ export namespace Value {
// (undocumented)
export type Primitive = number | string | boolean;
// (undocumented)
export function serialize(value: Value): string;
// (undocumented)
export type Struct = {
readonly [key: string]: Value;
};
Expand Down
7 changes: 1 addition & 6 deletions examples/react18/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,14 @@
"@babel/core": "^7.23.2",
"@babel/plugin-syntax-flow": "^7.22.5",
"@babel/plugin-transform-react-jsx": "^7.22.15",
"@openfeature/core": "^1.1.0",
"@openfeature/react-sdk": "^0.3.3",
"@openfeature/web-sdk": "^1.0.3",
"@spotify-confidence/openfeature-web-provider": "0.2.5",
"@spotify-confidence/react": "0.0.1",
"@spotify-confidence/sdk": "0.0.7",
"@testing-library/dom": "^7.29.6",
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^13.0.0",
"@testing-library/user-event": "^13.2.1",
"@types/jest": "^27.0.1",
"@types/node": "^16.7.13",
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
Expand Down
21 changes: 9 additions & 12 deletions examples/react18/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,30 @@
import React from 'react';
import { OpenFeature, OpenFeatureProvider } from '@openfeature/react-sdk';
import TestComponent from './TestComponent';
import { createConfidenceWebProvider } from '@spotify-confidence/openfeature-web-provider';
import { Confidence, pageViews } from '@spotify-confidence/sdk';
import { ConfidenceProvider } from './ConfidenceContext';
import { ConfidenceProvider } from '@spotify-confidence/react';

const confidence = Confidence.create({
clientSecret: 'RxDVTrXvc6op1XxiQ4OaR31dKbJ39aYV',
region: 'eu',
environment: 'client',
timeout: 1000,
logger: console,
});

confidence.track(pageViews());

const webProvider = createConfidenceWebProvider(confidence);
OpenFeature.setProvider(webProvider);
function App() {
return (
<ConfidenceProvider confidence={confidence}>
<h1>React 18 Example</h1>
<OpenFeatureProvider>
<div style={{ height: 2000 }}>
<React.Suspense fallback={<p>Loading... </p>}>
<div style={{ height: 2000 }}>
<React.Suspense fallback={<p>Loading... </p>}>
<ConfidenceProvider.WithContext context={{ targeting_key: 'user-a' }}>
<TestComponent />
</React.Suspense>
</div>
<p>bottom</p>
</OpenFeatureProvider>
</ConfidenceProvider.WithContext>
</React.Suspense>
</div>
<p>bottom</p>
</ConfidenceProvider>
);
}
Expand Down
22 changes: 0 additions & 22 deletions examples/react18/src/ConfidenceContext.tsx

This file was deleted.

45 changes: 36 additions & 9 deletions examples/react18/src/TestComponent.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,54 @@
import { useState } from 'react';
import { useStringFlagValue } from '@openfeature/react-sdk';
import { useConfidence } from './ConfidenceContext';
import { OpenFeature } from '@openfeature/web-sdk';
import { useConfidence } from '@spotify-confidence/react';
import { createContext, useContext, useState } from 'react';

const fakeContext = createContext(undefined);

const TestComponent = () => {
const str = useStringFlagValue('web-sdk-e2e-flag.str', 'default');
const [clickCount, setClickCount] = useState(0);
const confidence = useConfidence({ component: 'Test' });
const confidence = useConfidence();
// const details = useFlagEvaluation('web-sdk-e2e-flag.str', 'default');
// const details = confidence.evaluateFlag('web-sdk-e2e-flag.str', 'default');
const details = confidence.useFlag('web-sdk-e2e-flag.str', 'default');
// const details = useFlagValue('web-sdk-e2e-flag.str', 'default');
return (
<>
<p>The string flag value is: {str}</p>
<p>The flag is: </p>
<pre>{JSON.stringify(details, null, ' ')}</pre>
<p>Click count is: {clickCount}</p>
<button
onClick={() => {
OpenFeature.setContext({ targetingKey: `user-${Math.random()}` });
confidence.track('click');
setClickCount(value => value + 1);
}}
>
Randomise OpenFeature Context
Click
</button>
<button
onClick={() => {
let { targeting_key } = confidence.getContext();
console.log('got targeting key:', targeting_key);
if (targeting_key === 'user-a') {
targeting_key = 'user-b';
} else {
targeting_key = 'user-a';
}
confidence.setContext({ targeting_key });
}}
>
Randomise Context
</button>
</>
);
};

export default TestComponent;

function isRendering(): boolean {
try {
// eslint-disable-next-line
useContext(fakeContext);
return true;
} catch (e) {
return false;
}
}
Empty file added packages/react/CHANGELOG.md
Empty file.
Loading

0 comments on commit 0493005

Please sign in to comment.