Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 40 additions & 52 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
@@ -1,69 +1,63 @@
name: Checks

on:
push:
branches:
- main
pull_request:
branches:
- main
types:
- opened
- synchronize
- reopened
- ready_for_review

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
prep:
if: github.event.pull_request.draft == false
runs-on: ubuntu-latest

steps:
- name: Cancel Previous Runs
uses: styfle/cancel-workflow-action@0.8.0
with:
access_token: ${{ github.token }}

- name: Checkout
uses: actions/checkout@v2

- name: Read .nvmrc
run: echo "NVMRC=$(cat .nvmrc)" >> $GITHUB_ENV
uses: actions/checkout@v6

- name: Use Node.js ${{ env.NVMRC }}
uses: actions/setup-node@v1
- name: Use Node.js
uses: actions/setup-node@v6
with:
node-version: ${{ env.NVMRC }}
node-version-file: '.nvmrc'

- name: Cache node_modules
uses: actions/cache@v2
uses: actions/cache@v5
id: cache-node-modules
with:
path: node_modules
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package.json') }}
key: ${{ runner.os }}-build-${{ hashFiles('**/package.json') }}

- name: Install
run: yarn install
run: corepack enable && yarn install

lint:
needs: prep
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v6

- name: Read .nvmrc
run: echo "NVMRC=$(cat .nvmrc)" >> $GITHUB_ENV

- name: Use Node.js ${{ env.NVMRC }}
uses: actions/setup-node@v1
- name: Use Node.js
uses: actions/setup-node@v6
with:
node-version: ${{ env.NVMRC }}
node-version-file: '.nvmrc'

- name: Cache node_modules
uses: actions/cache@v2
uses: actions/cache@v5
id: cache-node-modules
with:
path: node_modules
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package.json') }}
key: ${{ runner.os }}-build-${{ hashFiles('**/package.json') }}

- name: Install
run: yarn install
run: corepack enable && yarn install

- name: Lint
run: yarn lint
Expand All @@ -74,54 +68,48 @@ jobs:

steps:
- name: Checkout
uses: actions/checkout@v2

- name: Read .nvmrc
run: echo "NVMRC=$(cat .nvmrc)" >> $GITHUB_ENV
uses: actions/checkout@v6

- name: Use Node.js ${{ env.NVMRC }}
uses: actions/setup-node@v1
- name: Use Node.js
uses: actions/setup-node@v6
with:
node-version: ${{ env.NVMRC }}
node-version-file: '.nvmrc'

- name: Cache node_modules
uses: actions/cache@v2
uses: actions/cache@v5
id: cache-node-modules
with:
path: node_modules
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package.json') }}
key: ${{ runner.os }}-build-${{ hashFiles('**/package.json') }}

- name: Install
run: yarn install
run: corepack enable && yarn install

- name: Test
run: yarn test

build:
needs: test
needs: prep
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v2

- name: Read .nvmrc
run: echo "NVMRC=$(cat .nvmrc)" >> $GITHUB_ENV
uses: actions/checkout@v6

- name: Use Node.js ${{ env.NVMRC }}
uses: actions/setup-node@v1
- name: Use Node.js
uses: actions/setup-node@v6
with:
node-version: ${{ env.NVMRC }}
node-version-file: '.nvmrc'

- name: Cache node_modules
uses: actions/cache@v2
uses: actions/cache@v5
id: cache-node-modules
with:
path: node_modules
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package.json') }}
key: ${{ runner.os }}-build-${{ hashFiles('**/package.json') }}

- name: Install
run: yarn install
run: corepack enable && yarn install

- name: Test
run: yarn build
run: yarn test
31 changes: 13 additions & 18 deletions docs/MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,6 @@ if (error && error.status === 404) {
**After:**

```typescript
import type { ApiErrorType } from 'stac-react';

const { error } = useCollection('my-collection');

if (error?.status === 404) {
Expand All @@ -176,13 +174,11 @@ if (error?.status === 404) {
**What's different?**

- Errors are now proper class instances with `ApiError`
- Better TypeScript support with `ApiErrorType`
- Includes HTTP status codes and response details
- Consistent across all hooks

**Migration checklist:**

- [ ] Import `ApiErrorType` from 'stac-react'
- [ ] Update error checks to use typed properties
- [ ] Test error scenarios (404, 500, network failures)

Expand All @@ -196,7 +192,7 @@ The most significant changes are in `useStacSearch`:

#### Before: Manual State Management

```typescript
```tsx
function MySearch() {
const {
state,
Expand Down Expand Up @@ -226,7 +222,7 @@ function MySearch() {

#### After: TanStack Query Integration

```typescript
```tsx
function MySearch() {
const {
isLoading,
Expand Down Expand Up @@ -347,7 +343,7 @@ You now must configure TanStack Query. The library doesn't do this for you (to a

#### Before

```jsx
```tsx
import { StacApiProvider } from 'stac-react';

function App() {
Expand All @@ -357,7 +353,7 @@ function App() {

#### After

```jsx
```tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { StacApiProvider } from 'stac-react';

Expand Down Expand Up @@ -426,7 +422,7 @@ const { collection: col2 } = useCollection('landsat-8');

### Request Deduplication

```typescript
```tsx
// Multiple components request same collection
// Only ONE network request is made, even if 5 components use the hook

Expand All @@ -438,7 +434,7 @@ const { collection: col2 } = useCollection('landsat-8');

### Invalidation on API Change

```typescript
```tsx
// If API URL changes, all queries are automatically invalidated
<StacApiProvider apiUrl="https://old-api.com" />
// Switch to new API:
Expand Down Expand Up @@ -469,7 +465,7 @@ test('loads collections', async () => {

### After

```typescript
```tsx
import { renderHook, waitFor } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { useCollections } from 'stac-react';
Expand Down Expand Up @@ -518,13 +514,13 @@ If you're using TypeScript, new types are available:

```typescript
import type {
ApiErrorType, // Error response
FetchRequest, // Request types for useStacSearch
SearchRequestPayload, // Search parameters
} from 'stac-react';

// Your hook usage
const { error }: { error?: ApiErrorType } = useCollection('id');
// Error is of type ApiError
const { error } = useCollection('id');

if (error) {
console.log(error.status); // number
Expand All @@ -541,15 +537,15 @@ if (error) {

**Before:**

```typescript
```tsx
{state === 'LOADING' && <Skeleton />}
{state === 'IDLE' && results && <Results items={results.features} />}
{error && <Error message={error.message} />}
```

**After:**

```typescript
```tsx
{isLoading && <Skeleton />}
{!isLoading && results && <Results items={results.features} />}
{error && <Error message={error.statusText} />}
Expand All @@ -559,15 +555,15 @@ if (error) {

**Before:**

```typescript
```tsx
<button onClick={search} disabled={state === 'LOADING'}>
Search
</button>
```

**After:**

```typescript
```tsx
<button onClick={search} disabled={isLoading || isFetching}>
{isFetching ? 'Searching...' : 'Search'}
</button>
Expand Down Expand Up @@ -678,7 +674,6 @@ const testClient = new QueryClient({
- [ ] Replace `state` with `isLoading`/`isFetching` in all components
- [ ] Rename `reload` to `refetch` in all components
- [ ] Replace context data access with individual hooks
- [ ] Update error handling to use typed `ApiErrorType`
- [ ] Update tests to use test QueryClient
- [ ] Remove context data subscriptions
- [ ] Review caching strategy for your app
Expand Down
1 change: 1 addition & 0 deletions example/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ function App() {

// Debug: Verify QueryClient configuration
if (isDevelopment && typeof window !== 'undefined') {
// eslint-disable-next-line no-console
console.log('[App] QueryClient defaults:', queryClient.getDefaultOptions());
}

Expand Down
2 changes: 2 additions & 0 deletions example/src/pages/Main/ItemDetails.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import { H2 } from '../../components/headers';
import Panel from '../../layout/Panel';
import { Button } from '../../components/buttons';

// eslint-disable-next-line react/prop-types
function ItemDetails({ item, onClose }) {
// eslint-disable-next-line react/prop-types
const itemUrl = item.links.find((r) => r.rel === 'self')?.href;
const { item: newItem, isLoading, error, reload } = useItem(itemUrl);

Expand Down
5 changes: 5 additions & 0 deletions jest.utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export function makeMockResponse(responseData: string, url: string, init?: ResponseInit): Response {
const response = new Response(responseData, init);
Object.defineProperty(response, 'url', { value: url });
return response;
}
14 changes: 4 additions & 10 deletions src/context/StacApiProvider.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,13 @@ import { render, screen, waitFor } from '@testing-library/react';
import { QueryClient, QueryClientProvider, useQueryClient } from '@tanstack/react-query';
import { StacApiProvider } from './index';
import { useStacApiContext } from './useStacApiContext';
import { makeMockResponse } from '../../jest.utils';

// Mock fetch for testing - returns a successful response
beforeEach(() => {
(global.fetch as jest.Mock) = jest.fn((url: string) =>
Promise.resolve({
ok: true,
url, // Return the requested URL
json: () =>
Promise.resolve({
links: [],
}),
})
);
(global.fetch as jest.Mock) = jest.fn((url: string) => {
return Promise.resolve(makeMockResponse(JSON.stringify({ links: [] }), url));
});
});

// Component to test that hooks work inside StacApiProvider
Expand Down
Loading