Skip to content

Commit 314ef24

Browse files
authored
Merge pull request #520 from seamapi/init-hook
2 parents 66abe57 + 3a58c00 commit 314ef24

File tree

5 files changed

+75
-12
lines changed

5 files changed

+75
-12
lines changed

README.md

+45
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,51 @@ export function App() {
9494

9595
> Update the version in the script tag above with the exact version of this package you would like to use.
9696
97+
#### Web component attributes and properties
98+
99+
Each React component is defined as a custom element:
100+
101+
- The element name is in in kebab-case,
102+
e.g., `<DeviceTable>` becomes `<seam-device-table>`.
103+
- Each element is wrapped in a `<SeamProvider />`.
104+
- An attribute and custom property is defined for each `<SeamProvider />` prop and component prop.
105+
- Attributes are in kebab-case and properties are in snakeCase.
106+
107+
Attributes map directly to component props.
108+
All attributes are passed as strings, thus non-string props have some limitations:
109+
110+
- Number props will be parsed using `parseFloat`.
111+
- Boolean props should be passed as `true` or `false`, e.g., `disable-css-injection="true"` or `disable-css-injection="false"`.
112+
- Array props may be passed as JSON, e.g., `device-ids="["foo", "bar"]"`,
113+
or CSV, e.g., `device-ids="foo,bar"`.
114+
- Function and object props should not be passed as attributes.
115+
Set them as properties instead.
116+
117+
Use custom properties to work directly with JavaScript objects and primitives.
118+
119+
- This will avoid any issues with string parsing and serialization.
120+
- Use the `onSessionUpdate` prop to maintain a reference to the internal Seam client.
121+
122+
For example,
123+
124+
```js
125+
globalThis.customElements.whenDefined('seam-device-table').then(() => {
126+
const elements = globalThis.document.getElementsByTagName('seam-device-table')
127+
const element = elements[0]
128+
if (element == null) {
129+
throw new Error('Cannot find seam-device-table in document')
130+
}
131+
let seam
132+
element.onSessionUpdate = (client) => {
133+
seam = client
134+
}
135+
element.onDeviceClick = (deviceId) => {
136+
if (seam == null) return
137+
seam.devices.get({ device_id: deviceId }).then(console.log)
138+
}
139+
})
140+
```
141+
97142
[Seam Console]: https://console.seam.co/
98143

99144
### React Hooks

package-lock.json

+8-8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@
133133
"@emotion/styled": "^11.10.6",
134134
"@mui/icons-material": "^5.11.16",
135135
"@mui/material": "^5.12.2",
136-
"@rxfork/r2wc-react-to-web-component": "^2.3.0",
136+
"@rxfork/r2wc-react-to-web-component": "^2.4.0",
137137
"@seamapi/fake-seam-connect": "^1.17.0",
138138
"@storybook/addon-designs": "^7.0.1",
139139
"@storybook/addon-essentials": "^7.0.2",

src/lib/element.tsx

+4-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export type ElementProps<T> = R2wcProps<Omit<T, keyof CommonProps>>
3030

3131
type R2wcProps<T> = Record<
3232
keyof T,
33-
'string' | 'number' | 'boolean' | 'array' | 'function' | 'json' | 'object'
33+
'string' | 'number' | 'boolean' | 'array' | 'object'
3434
>
3535

3636
type ProviderProps = Omit<
@@ -59,6 +59,7 @@ const providerProps: R2wcProps<ProviderProps> = {
5959
disableCssInjection: 'boolean',
6060
disableFontInjection: 'boolean',
6161
unminifiyCss: 'boolean',
62+
onSessionUpdate: 'object',
6263
}
6364

6465
export const defineCustomElement = ({
@@ -90,6 +91,7 @@ function withProvider<P extends JSX.IntrinsicAttributes>(
9091
disableCssInjection,
9192
disableFontInjection,
9293
unminifiyCss,
94+
onSessionUpdate,
9395
container: _container,
9496
...props
9597
}: ProviderProps & { container: Container } & P): JSX.Element | null {
@@ -107,6 +109,7 @@ function withProvider<P extends JSX.IntrinsicAttributes>(
107109
disableFontInjection ?? globalThis.disableSeamFontInjection
108110
}
109111
unminifiyCss={unminifiyCss ?? globalThis.unminifiySeamCss}
112+
onSessionUpdate={onSessionUpdate}
110113
>
111114
<Component {...(props as P)} />
112115
</SeamProvider>

src/lib/seam/SeamProvider.tsx

+17-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
createContext,
44
type PropsWithChildren,
55
useContext,
6+
useEffect,
67
useMemo,
78
} from 'react'
89
import type { Seam, SeamClientOptions } from 'seamapi'
@@ -16,6 +17,8 @@ import {
1617
import { useSeamFont } from 'lib/seam/use-seam-font.js'
1718
import { useSeamStyles } from 'lib/seam/use-seam-styles.js'
1819

20+
import { useSeamClient } from './use-seam-client.js'
21+
1922
declare global {
2023
// eslint-disable-next-line no-var
2124
var seam: SeamProviderProps | undefined
@@ -62,6 +65,7 @@ interface SeamProviderBaseProps extends PropsWithChildren {
6265
unminifiyCss?: boolean | undefined
6366
queryClient?: QueryClient | undefined
6467
telemetryClient?: TelemetryClient | undefined
68+
onSessionUpdate?: (client: Seam) => void
6569
}
6670

6771
export type SeamProviderClientOptions = Pick<SeamClientOptions, 'endpoint'>
@@ -76,6 +80,7 @@ export function SeamProvider({
7680
disableCssInjection = false,
7781
disableFontInjection = false,
7882
unminifiyCss = false,
83+
onSessionUpdate = () => {},
7984
queryClient,
8085
telemetryClient,
8186
...props
@@ -122,16 +127,26 @@ export function SeamProvider({
122127
}
123128
>
124129
<Provider value={value}>
125-
<Wrapper>{children}</Wrapper>
130+
<Wrapper onSessionUpdate={onSessionUpdate}>{children}</Wrapper>
126131
</Provider>
127132
</QueryClientProvider>
128133
</TelemetryProvider>
129134
</div>
130135
)
131136
}
132137

133-
function Wrapper({ children }: PropsWithChildren): JSX.Element | null {
138+
function Wrapper({
139+
onSessionUpdate,
140+
children,
141+
}: Required<Pick<SeamProviderProps, 'onSessionUpdate'>> &
142+
PropsWithChildren): JSX.Element | null {
134143
useUserTelemetry()
144+
145+
const { client } = useSeamClient()
146+
useEffect(() => {
147+
if (client != null) onSessionUpdate(client)
148+
}, [onSessionUpdate, client])
149+
135150
return <>{children}</>
136151
}
137152

0 commit comments

Comments
 (0)