Skip to content

Commit

Permalink
feat(*): revise environment provider
Browse files Browse the repository at this point in the history
  • Loading branch information
cschroeter committed May 17, 2024
1 parent e072245 commit d79adbc
Show file tree
Hide file tree
Showing 29 changed files with 92 additions and 84 deletions.
4 changes: 4 additions & 0 deletions packages/react/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ description: All notable changes will be documented in this file.

## [Unreleased]

### Changed

- Renamed `Environment` to `EnvironmentProvider` for consistency with other providers.

## [3.0.0-2] - 2024-05-15

### Changed
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { getDocument, getWindow } from '@zag-js/dom-query'
import { type ReactNode, useMemo, useState } from 'react'
import { runIfFn } from '../../utils/run-if-fn'
import { EnvironmentProvider, type RootNode } from './use-environment-context'
import { EnvironmentContextProvider, type RootNode } from './use-environment-context'

export interface EnvironmentProps {
export interface EnvironmentProviderProps {
children?: ReactNode
value?: RootNode | (() => RootNode)
}

export const Environment = (props: EnvironmentProps) => {
export const EnvironmentProvider = (props: EnvironmentProviderProps) => {
const { value, children } = props
const [spanRef, setSpanRef] = useState<HTMLSpanElement | null>()

Expand All @@ -26,9 +26,9 @@ export const Environment = (props: EnvironmentProps) => {
)

return (
<EnvironmentProvider value={environment}>
<EnvironmentContextProvider value={environment}>
{children}
{!value && <span hidden ref={setSpanRef} />}
</EnvironmentProvider>
</EnvironmentContextProvider>
)
}
8 changes: 4 additions & 4 deletions packages/react/src/providers/environment/environment.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { render, screen } from '@testing-library/react'
import { Environment } from './'
import { EnvironmentProvider } from './'
import { useEnvironmentContext } from './use-environment-context'

const PrintEnvironment = () => {
Expand All @@ -8,12 +8,12 @@ const PrintEnvironment = () => {
return <pre data-testid="output">{JSON.stringify(getRootNode(), null, 2)}</pre>
}

describe('Environment', () => {
describe('EnvironmentProvider', () => {
it('should have access to the environment values', async () => {
render(
<Environment value={() => document}>
<EnvironmentProvider value={() => document}>
<PrintEnvironment />
</Environment>,
</EnvironmentProvider>,
)
expect(screen.getByTestId('output').innerHTML).not.toBe('""')
})
Expand Down
15 changes: 5 additions & 10 deletions packages/react/src/providers/environment/examples/basic.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
import Frame from 'react-frame-component'
import { Environment, useEnvironmentContext } from '../'
import { EnvironmentProvider } from '../'
import { Usage } from './usage'

export const Basic = () => {
return (
<Frame title="IFrame Context" width="100%" height="300px">
<Environment>
<PrintEnvironment />
</Environment>
<EnvironmentProvider>
<Usage />
</EnvironmentProvider>
</Frame>
)
}

const PrintEnvironment = () => {
const { getRootNode } = useEnvironmentContext()

return <pre>{JSON.stringify(getRootNode(), null, 2)}</pre>
}
4 changes: 2 additions & 2 deletions packages/react/src/providers/environment/examples/setup.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import Frame from 'react-frame-component'
import { Environment } from '../'
import { EnvironmentProvider } from '../'

export const App = () => {
return (
<Frame title="IFrame Context">
<Environment>{/* Your App */}</Environment>
<EnvironmentProvider>{/* Your App */}</EnvironmentProvider>
</Frame>
)
}
2 changes: 1 addition & 1 deletion packages/react/src/providers/environment/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export { Environment, type EnvironmentProps } from './environment'
export { EnvironmentProvider, type EnvironmentProviderProps } from './environment-provider'
export {
useEnvironmentContext,
type EnvironmentContext,
Expand Down
23 changes: 12 additions & 11 deletions packages/react/src/providers/environment/use-environment-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,15 @@ export interface EnvironmentContext {
getWindow(): Window & typeof globalThis
}

export const [EnvironmentProvider, useEnvironmentContext] = createContext<EnvironmentContext>({
name: 'EnvironmentContext',
hookName: 'useEnvironmentContext',
providerName: '<EnvironmentProvider />',
strict: false,
defaultValue: {
getRootNode: () => document,
getDocument: () => document,
getWindow: () => window,
},
})
export const [EnvironmentContextProvider, useEnvironmentContext] =
createContext<EnvironmentContext>({
name: 'EnvironmentContext',
hookName: 'useEnvironmentContext',
providerName: '<EnvironmentProvider />',
strict: false,
defaultValue: {
getRootNode: () => document,
getDocument: () => document,
getWindow: () => window,
},
})
4 changes: 4 additions & 0 deletions packages/solid/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ description: All notable changes will be documented in this file.

## [Unreleased]

### Changed

- Renamed `Environment` to `EnvironmentProvider` for consistency with other providers.

## [3.0.0-5] - 2024-05-15

### Added
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { getDocument, getWindow } from '@zag-js/dom-query'
import { type JSX, Show, createMemo, createSignal } from 'solid-js'
import { runIfFn } from '../../utils/run-if-fn'
import { EnvironmentProvider, type RootNode } from './use-environment-context'
import { EnvironmentContextProvider, type RootNode } from './use-environment-context'

export interface EnvironmentProps {
export interface EnvironmentProviderProps {
children?: JSX.Element
value?: RootNode | (() => RootNode)
}

export const Environment = (props: EnvironmentProps) => {
export const EnvironmentProvider = (props: EnvironmentProviderProps) => {
const [spanRef, setSpanRef] = createSignal<HTMLSpanElement>()
const getRootNode = () => runIfFn(props.value) ?? spanRef()?.ownerDocument ?? document

Expand All @@ -19,11 +19,11 @@ export const Environment = (props: EnvironmentProps) => {
}))

return (
<EnvironmentProvider value={environment}>
<EnvironmentContextProvider value={environment}>
{props.children}
<Show when={!props.value}>
<span hidden ref={setSpanRef} />
</Show>
</EnvironmentProvider>
</EnvironmentContextProvider>
)
}
8 changes: 4 additions & 4 deletions packages/solid/src/providers/environment/environment.test.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import { render, screen } from '@solidjs/testing-library'
import { Environment, useEnvironmentContext } from './'
import { EnvironmentProvider, useEnvironmentContext } from './'

const PrintEnvironment = () => {
const environment = useEnvironmentContext()

return <pre>{environment().getRootNode().toString()}</pre>
}

describe('Environment', () => {
describe('EnvironmentPrvovider', () => {
it('should have access to the environment values', async () => {
render(() => (
<Environment value={() => document}>
<EnvironmentProvider value={() => document}>
<PrintEnvironment />
</Environment>
</EnvironmentProvider>
))
expect(screen.getByText('[object Document]')).toBeInTheDocument()
})
Expand Down
15 changes: 5 additions & 10 deletions packages/solid/src/providers/environment/examples/basic.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
import { Environment, useEnvironmentContext } from '../..'
import { EnvironmentProvider } from '../..'
import { Usage } from './usage'

export const Basic = () => (
<Environment>
<PrintEnvironment />
</Environment>
<EnvironmentProvider>
<Usage />
</EnvironmentProvider>
)

const PrintEnvironment = () => {
const environment = useEnvironmentContext()

return <pre>{JSON.stringify(environment().getRootNode(), null, 2)}</pre>
}
4 changes: 2 additions & 2 deletions packages/solid/src/providers/environment/examples/setup.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Environment } from '../..'
import { EnvironmentProvider } from '../..'

export const App = () => {
return (
<iframe title="IFrame Context">
<Environment>{/* Your App */}</Environment>
<EnvironmentProvider>{/* Your App */}</EnvironmentProvider>
</iframe>
)
}
2 changes: 1 addition & 1 deletion packages/solid/src/providers/environment/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export { Environment, type EnvironmentProps } from './environment'
export { EnvironmentProvider, type EnvironmentProviderProps } from './environment-provider'
export {
useEnvironmentContext,
type EnvironmentContext,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export interface EnvironmentContext {
getWindow(): Window & typeof globalThis
}

export const [EnvironmentProvider, useEnvironmentContext] = createContext<
export const [EnvironmentContextProvider, useEnvironmentContext] = createContext<
Accessor<EnvironmentContext>
>({
hookName: 'useEnvironmentContext',
Expand Down
4 changes: 4 additions & 0 deletions packages/vue/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ description: All notable changes will be documented in this file.

## [Unreleased]

### Changed

- Renamed `Environment` to `EnvironmentProvider` for consistency with other providers.

## [1.0.0-5] - 2024-05-15

### Fixed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
import { getDocument, getWindow } from '@zag-js/dom-query'
import { computed, ref } from 'vue'
import { runIfFn } from '../../utils'
import { EnvironmentProvider, type RootNode } from './use-environment-context'
import { EnvironmentContextProvider, type RootNode } from './use-environment-context'
export interface EnvironmentProps {
export interface EnvironmentProviderProps {
value?: RootNode | (() => RootNode)
}
const props = defineProps<EnvironmentProps>()
const props = defineProps<EnvironmentProviderProps>()
const spanRef = ref<HTMLSpanElement | null>(null)
const getRootNode = () => runIfFn(props.value) ?? spanRef.value?.ownerDocument ?? document
Expand All @@ -19,7 +19,7 @@ const environment = computed(() => ({
getWindow: () => getWindow(getRootNode()),
}))
EnvironmentProvider(environment)
EnvironmentContextProvider(environment)
</script>

<template>
Expand Down
6 changes: 3 additions & 3 deletions packages/vue/src/providers/environment/examples/basic.vue
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<script setup lang="ts">
import Environment from '../environment.vue'
import { EnvironmentProvider } from '..'
import Usage from './usage.vue'
</script>

<template>
<Environment>
<EnvironmentProvider>
<Usage />
</Environment>
</EnvironmentProvider>
</template>
4 changes: 2 additions & 2 deletions packages/vue/src/providers/environment/examples/setup.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<script setup lang="ts">
import { Environment } from '../'
import { EnvironmentProvider } from '../'
</script>

<template>
<iframe title="IFrame Context">
<Environment><!-- Your App --></Environment>
<EnvironmentProvider><!-- Your App --></EnvironmentProvider>
</iframe>
</template>
5 changes: 4 additions & 1 deletion packages/vue/src/providers/environment/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
export { default as Environment, type EnvironmentProps } from './environment.vue'
export {
default as EnvironmentProvider,
type EnvironmentProviderProps,
} from './environment-provider.vue'
export {
useEnvironmentContext,
type EnvironmentContext,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@ export interface EnvironmentContext {
getWindow(): Window & typeof globalThis
}

export const [EnvironmentProvider, useEnvironmentContext] =
export const [EnvironmentContextProvider, useEnvironmentContext] =
createContext<ComputedRef<EnvironmentContext> | null>('EnvironmentContext')
4 changes: 3 additions & 1 deletion scripts/src/generate-type-docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,9 @@ const extractTypesForFramework = async (framework: string) => {
)

const newName =
voca.isEmpty(shortName) || shortName === 'Emits' ? x[0] : shortName
voca.isEmpty(shortName) || shortName === 'Emits' || shortName === 'Provider'
? x[0]
: shortName

return [newName, { props: Object.fromEntries(Object.entries(x[1])) }]
})
Expand Down
17 changes: 9 additions & 8 deletions website/src/content/pages/providers/environment.mdx
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
---
id: environment
title: Environment
description: Setup components for custom environments like IFrames, Shadow DOM, or Electron contexts.
description: Set up components for custom environments like iframes, Shadow DOM, or Electron.
---

## Motivation

We use [Zag.js](https://zagjs.com/overview/composition#custom-window-environment) internally,
which relies on DOM query methods like `document.querySelectorAll` and `document.getElementById` to locate elements.
In custom environments such as iframes, Shadow DOM, or Electron, these methods might not work as expected because `document` may not be available.
We use [Zag.js](https://zagjs.com/overview/composition#custom-window-environment) internally, which relies on DOM query methods like `document.querySelectorAll` and `document.getElementById`.
In custom environments like iframes, Shadow DOM, or Electron, these methods might not work as expected.

To handle this, you can set a default using the `Environment` component. This ensures the correct root node or document is referenced.
To handle this, Ark UI includes the `EnvironmentProvider`, allowing you to set the appropriate root node or document, ensuring correct DOM queries.

## Setup

To target custom environments like an iframe, Shadow DOM, or Electron, render the `Environment` component to provide the environment context to all Ark UI components.
To support custom environments like an iframe, Shadow DOM or Electron, render the `EnvironmentProvider` component to provide the environment context to all Ark UI components.

<Example id="setup" />

The `Environment` component will automatically detect the current environment and set the correct environment context.
The `EnvironmentProvider` component will automatically detect the current environment and set the correct environment context.
However, you can also manually set the `Document` like shown in this React example below:

```jsx
Expand All @@ -28,7 +27,9 @@ import { Environment } from '@ark-ui/react'
export const App = () => (
<Frame title="IFrame Context">
<FrameContextConsumer>
{({ document }) => <Environment value={document}>{/* Your App */}</Environment>}
{({ document }) => (
<EnvironmentProvider value={document}>{/* Your App */}</EnvironmentProvider>
)}
</FrameContextConsumer>
</Frame>
)
Expand Down
3 changes: 1 addition & 2 deletions website/src/content/pages/providers/locale.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ The `LocaleProvider` component sets the locale for your app, formatting dates, n

> **Note:** If no `LocaleProvider` is setup, the default locale for the app will be `en-US` and therefore the direction will be `ltr`.

<Example id="setup" />

## Usage
Expand All @@ -21,4 +20,4 @@ To access the current locale and direction settings, use the `useLocaleContext`

## API Reference

<ComponentTypes id="locale" />
<ComponentTypes id="locale" />
2 changes: 1 addition & 1 deletion website/src/content/types/react/environment.types.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"Environment": {
"EnvironmentProvider": {
"props": { "value": { "type": "RootNode | (() => RootNode)", "isRequired": false } }
}
}

0 comments on commit d79adbc

Please sign in to comment.