Skip to content

Commit

Permalink
Add FirebaseStorageProvider (#48)
Browse files Browse the repository at this point in the history
* init FirebaseStorageProvider

* test and impl storage instance should be accessible

* add storage to FirebaseSuiteProvider

* write docs for FirebaseStorageProvider

* write docs for FirebaseSuiteProvider.storage prop
  • Loading branch information
erayerdin authored Feb 1, 2024
1 parent 5244b66 commit 2a8cceb
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 2 deletions.
27 changes: 27 additions & 0 deletions docs/contexts-and-providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,31 @@ const functions = useContext(FirebaseFunctionsContext);
!!! tip
The type you get from `useContext` is `Functions | undefined`. You can simply use `functions!` (non-null assertion operator) as you guarantee it to be defined when you use `FirebaseFunctionsProvider`.

### Setting Up for Firebase Storage

Head to your entry point and wrap your app with `FirebaseStorageProvider`:

```typescript
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<FirebaseStorageProvider storage={storage}>
{/** the rest of your app */}
</FirebaseStorageProvider>
</React.StrictMode>
)
```

`FirebaseStorageProvider` only requires one parameter with signature `storage: FirebaseStorage`.

Now, you can get the instance of your `FirebaseStorage` anywhere in the component tree by simply doing:

```typescript
const storage = useContext(FirebaseStorageContext);
```

!!! tip
The type you get from `useContext` is `FirebaseStorage | undefined`. You can simply use `functions!` (non-null assertion operator) as you guarantee it to be defined when you use `FirebaseStorageProvider`.

### Setting Up for Multiple Instances

Sometimes, wrapping your entry point with multiple providers might look ugly. In that case, you can use `FirebaseSuiteProvider`, which lets you selectively choose what types of Firebase services should be injected into your global app context.
Expand All @@ -133,6 +158,7 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
firestore={firestore} {/** optional */}
auth={auth} {/** optional */}
functions={functions} {/** optional */}
storage={storage} {/** optional */}
>
{/** the rest of your app */}
</FirebaseSuiteProvider>
Expand All @@ -149,6 +175,7 @@ const app = useContext(FirebaseAppContext);
const firestore = useContext(FirestoreContext);
const auth = useContext(FirebaseAuthContext);
const functions = useContext(FirebaseFunctionsContext);
const storage = useContext(FirebaseStorageContext);
```

!!! warning
Expand Down
24 changes: 24 additions & 0 deletions src/providers/FirebaseStorageProvider.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) 2024 Eray Erdin
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT

import { render, screen } from "@testing-library/react";
import { useContext } from "react";
import { FirebaseStorageContext, FirebaseStorageProvider } from ".";
import { storage } from "../firebase";

const SampleComponent = () => {
const storage = useContext(FirebaseStorageContext);
return <div>{storage?.app.name}</div>;
};

it("storage instance should be accessible", async () => {
render(
<FirebaseStorageProvider storage={storage}>
<SampleComponent />
</FirebaseStorageProvider>,
);

expect(screen.getByText("[DEFAULT]")).not.toBeUndefined();
});
27 changes: 27 additions & 0 deletions src/providers/FirebaseStorageProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright (c) 2024 Eray Erdin
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT

import { FirebaseStorage } from "firebase/storage";
import { createContext } from "react";
import { NodeComponent } from "../types";

export const FirebaseStorageContext = createContext<
FirebaseStorage | undefined
>(undefined);

type FirebaseStorageProviderProps = {
storage: FirebaseStorage;
} & NodeComponent;

export const FirebaseStorageProvider = ({
storage,
children,
}: FirebaseStorageProviderProps) => {
return (
<FirebaseStorageContext.Provider value={storage}>
{children}
</FirebaseStorageContext.Provider>
);
};
9 changes: 8 additions & 1 deletion src/providers/FirebaseSuiteProvider.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ import {
FirebaseAppContext,
FirebaseAuthContext,
FirebaseFunctionsContext,
FirebaseStorageContext,
} from ".";
import app, { auth, firestore, functions } from "../firebase";
import app, { auth, firestore, functions, storage } from "../firebase";
import { FirebaseSuiteProvider } from "./FirebaseSuiteProvider";
import { FirestoreContext } from "./FirestoreProvider";

Expand All @@ -19,13 +20,15 @@ const SampleComponent = () => {
const firestore = useContext(FirestoreContext);
const auth = useContext(FirebaseAuthContext);
const functions = useContext(FirebaseFunctionsContext);
const storage = useContext(FirebaseStorageContext);

return (
<>
<div>Firebase app name: {app?.name}</div>
<div>Firestore app name: {firestore?.app.name}</div>
<div>Auth app name: {auth?.app.name}</div>
<div>Functions app name: {functions?.app.name}</div>
<div>Storage app name: {storage?.app.name}</div>
</>
);
};
Expand All @@ -37,6 +40,7 @@ it("suite app instance should be accessible", async () => {
firestore={firestore}
auth={auth}
functions={functions}
storage={storage}
>
<SampleComponent />
</FirebaseSuiteProvider>,
Expand All @@ -54,4 +58,7 @@ it("suite app instance should be accessible", async () => {
expect(screen.getByText("Functions app name: [DEFAULT]").innerHTML).toBe(
"Functions app name: [DEFAULT]",
);
expect(screen.getByText("Storage app name: [DEFAULT]").innerHTML).toBe(
"Storage app name: [DEFAULT]",
);
});
15 changes: 14 additions & 1 deletion src/providers/FirebaseSuiteProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ import { FirebaseApp } from "firebase/app";
import { Auth } from "firebase/auth";
import { Firestore } from "firebase/firestore";
import { Functions } from "firebase/functions";
import { FirebaseStorage } from "firebase/storage";
import {
FirebaseAuthProvider,
FirebaseFunctionsProvider,
FirebaseProvider,
FirebaseStorageProvider,
FirestoreProvider,
} from ".";
import { NodeComponent } from "../types";
Expand All @@ -31,13 +33,15 @@ type FirebaseSuiteProviderProps = {
firestore?: Firestore;
auth?: Auth;
functions?: Functions;
storage?: FirebaseStorage;
} & NodeComponent;

export const FirebaseSuiteProvider = ({
app,
firestore,
auth,
functions,
storage,
children,
}: FirebaseSuiteProviderProps) => {
return (
Expand Down Expand Up @@ -65,7 +69,16 @@ export const FirebaseSuiteProvider = ({
</FirebaseFunctionsProvider>
)}
>
{children}
<ConditionalWrap
condition={storage !== undefined}
wrap={(c) => (
<FirebaseStorageProvider storage={storage!}>
{c}
</FirebaseStorageProvider>
)}
>
{children}
</ConditionalWrap>
</ConditionalWrap>
</ConditionalWrap>
</ConditionalWrap>
Expand Down
2 changes: 2 additions & 0 deletions src/providers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ export * from "./FirestoreProvider";
export * from "./FirebaseAuthProvider";

export * from "./FirebaseFunctionsProvider";

export * from "./FirebaseStorageProvider";

0 comments on commit 2a8cceb

Please sign in to comment.