Skip to content

Commit f4b17cd

Browse files
committed
♻️ update example
Signed-off-by: w01fgang <[email protected]>
1 parent 3d3a398 commit f4b17cd

8 files changed

Lines changed: 111 additions & 48 deletions

File tree

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { ChangeEvent, ReactElement } from 'react';
2+
import { useFeature } from 'feature-gate';
3+
4+
const removeUndefined = (val: Record<string, string | undefined>) => JSON.parse(JSON.stringify(val));
5+
6+
type Props = {
7+
onChange: (flags: Record<string, string>) => void,
8+
};
9+
10+
const FeatureSelector = (props: Props): ReactElement => {
11+
const { featureFlags, features } = useFeature("users");
12+
console.log("🚀 ~ file: FeatureSelector.tsx ~ line 10 ~ FeatureSelector ~ featureFlags", featureFlags)
13+
14+
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
15+
const value = e.target.getAttribute('data-value');
16+
if (e.target.checked) {
17+
const newFlags = { ...featureFlags, [e.target.name]: value };
18+
props.onChange(newFlags);
19+
} else {
20+
const newFlags = { ...featureFlags, [e.target.name]: undefined };
21+
props.onChange(removeUndefined(newFlags));
22+
}
23+
};
24+
25+
return (
26+
<div id="feature-selector">
27+
<span>Select features:</span>
28+
<div className="container">
29+
{Object.entries(features).map(([feature, flag]) => (
30+
<div key={feature}>
31+
<input
32+
type="checkbox"
33+
id={feature}
34+
name={feature}
35+
data-value={flag}
36+
checked={featureFlags[feature] === flag}
37+
onChange={handleChange}
38+
/>
39+
<label htmlFor={feature}>{feature}</label>
40+
</div>
41+
))}
42+
43+
<div>
44+
<input
45+
type="checkbox"
46+
id="ABtest"
47+
name="ABtest"
48+
data-value={'B'}
49+
checked={featureFlags['ABtest'] === 'B'}
50+
onChange={handleChange}
51+
/>
52+
<label htmlFor="ABtest">ABtest - B</label>
53+
</div>
54+
</div>
55+
<style jsx>{`
56+
#feature-selector {
57+
margin: 8px 0;
58+
}
59+
.container {
60+
display: flex;
61+
gap: 8px;
62+
padding: 8px;
63+
}
64+
`}</style>
65+
</div>
66+
)
67+
}
68+
69+
export default FeatureSelector

example/components/Layout.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, { ReactElement, ReactNode } from 'react'
2-
import { PermissionGate } from 'feature-gate';
2+
import { FeatureGate } from 'feature-gate';
33
import Link from 'next/link'
44
import Head from 'next/head'
55

@@ -25,18 +25,18 @@ const Layout = ({ children, title = 'This is the default title' }: Props): React
2525
<a>About</a>
2626
</Link>
2727

28-
<PermissionGate name="users">
28+
<FeatureGate name="users">
2929
<>
3030
{' | '}
3131
<Link href="/users">
3232
<a>Users List</a>
3333
</Link>
3434
</>
35-
</PermissionGate>
35+
</FeatureGate>
3636

37-
<PermissionGate name="users-api">
37+
<FeatureGate name="users-api">
3838
<span>{' | '}<a href="/api/users">Users API</a></span>
39-
</PermissionGate>
39+
</FeatureGate>
4040
</nav>
4141
</header>
4242
{children}

example/components/RoleSelector.tsx

Lines changed: 0 additions & 31 deletions
This file was deleted.

example/pages/_app.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,19 @@ import { useState, ReactElement } from 'react';
22
import { FeatureGateProvider } from 'feature-gate';
33
// import App from "next/app";
44
import type { AppProps /*, AppContext */ } from 'next/app';
5-
import RoleSelector from '../components/RoleSelector'
5+
import FeatureSelector from '../components/FeatureSelector'
66

77
const rules = Object.freeze({
8-
users: ['admin', 'user'],
9-
"users-api": ['admin'],
8+
feature1: 'true',
9+
ABtest: 'A',
1010
});
1111

1212
function MyApp({ Component, pageProps }: AppProps): ReactElement {
13-
const [role, setRole] = useState("user");
13+
const [featureFlags, setFlags] = useState({});
1414
return (
15-
<FeatureGateProvider role={role} rulesMap={rules}>
15+
<FeatureGateProvider featureFlags={featureFlags} features={rules}>
1616
<>
17-
<RoleSelector onChange={setRole} />
17+
<FeatureSelector onChange={setFlags} />
1818
<Component {...pageProps} />
1919
</>
2020
</FeatureGateProvider>

example/pages/about.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { ReactElement } from 'react';
22
import Link from 'next/link'
33
import Layout from '../components/Layout'
4+
import { FeatureGate } from 'feature-gate';
45

56
const AboutPage = (): ReactElement => (
67
<Layout title="About | Next.js + TypeScript Example">
@@ -11,6 +12,15 @@ const AboutPage = (): ReactElement => (
1112
<a>Go home</a>
1213
</Link>
1314
</p>
15+
16+
<FeatureGate name="feature1">
17+
<>
18+
<p>This is an experimental feature 1</p>
19+
<p>This is an experimental feature 1</p>
20+
<p>This is an experimental feature 1</p>
21+
<p>This is an experimental feature 1</p>
22+
</>
23+
</FeatureGate>
1424
</Layout>
1525
)
1626

example/pages/index.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { ReactNode } from 'react';
22
import Link from 'next/link'
33
import Layout from '../components/Layout'
4+
import { FeatureGate, FeatureSwitch } from 'feature-gate';
45

56
const IndexPage = (): ReactNode => {
67
return (
@@ -10,7 +11,21 @@ const IndexPage = (): ReactNode => {
1011
<Link href="/about">
1112
<a>About</a>
1213
</Link>
14+
1315
</p>
16+
17+
<FeatureGate name="feature1">
18+
<>
19+
<p>This is an experimental feature 1</p>
20+
<p>This is an experimental feature 1</p>
21+
<p>This is an experimental feature 1</p>
22+
<p>This is an experimental feature 1</p>
23+
</>
24+
</FeatureGate>
25+
26+
<FeatureSwitch fallback={<p>This is an A/B testing: Variant B</p>} name="ABtest">
27+
<p>This is an A/B testing: Variant A</p>
28+
</FeatureSwitch>
1429
</Layout>
1530
)
1631
}

example/yarn.lock

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1094,6 +1094,11 @@ fastq@^1.6.0:
10941094
dependencies:
10951095
reusify "^1.0.4"
10961096

1097+
feature-gate@latest:
1098+
version "1.0.1"
1099+
resolved "https://registry.yarnpkg.com/feature-gate/-/feature-gate-1.0.1.tgz#696209c05b9e9060cc10ecd61ddfcaa47a208a0c"
1100+
integrity sha512-yczfCFGgIBOP3Mfp6ID9Jbpvgo3XHyi2978cs6h7p8VQzZ/AJ+08pm8Pgzv/YZT6DqpmH/z++NIeULlzKXRwPw==
1101+
10971102
fill-range@^7.0.1:
10981103
version "7.0.1"
10991104
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
@@ -2030,11 +2035,6 @@ pbkdf2@^3.0.3:
20302035
safe-buffer "^5.0.1"
20312036
sha.js "^2.4.8"
20322037

2033-
permission-gate@latest:
2034-
version "0.0.9"
2035-
resolved "https://registry.yarnpkg.com/permission-gate/-/permission-gate-0.0.9.tgz#eea63b4f779292b9af401f74708d90020352ab29"
2036-
integrity sha512-I9s/ToPbSVYEYl/QId3hmZkVaZs6zDOjeHW/4fLX21XR6XRaaTKVKAfF36z9YXALCUbo9AWEevo7Nuzp7vL/nA==
2037-
20382038
picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3:
20392039
version "2.3.0"
20402040
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972"

tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"compilerOptions": {
33
"outDir": "lib/esm",
44
"module": "esnext",
5-
"target": "es5",
5+
"target": "esnext",
66
"lib": [
77
"es6",
88
"dom",

0 commit comments

Comments
 (0)