Skip to content

Commit

Permalink
Merge pull request #737 from omnifed/734-feat-create-usecanvascolors-…
Browse files Browse the repository at this point in the history
…hook

734 feat create usecanvascolors hook
  • Loading branch information
caseybaggz authored Nov 21, 2024
2 parents 8c8a5ee + d85de5d commit 394d7c4
Show file tree
Hide file tree
Showing 7 changed files with 242 additions and 1 deletion.
3 changes: 2 additions & 1 deletion docs/app/react/side-nav.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,12 @@ const hooksGroup: CategoriesList = {
'trap-focus',
'use-date',
'use-modal',
'use-root-colors',
'use-theme',
'use-theme-context',
'use-toggle',
],
next: [],
next: ['use-root-colors'],
new: ['use-date'],
},
}
Expand Down
38 changes: 38 additions & 0 deletions docs/app/react/use-root-colors/components/canvas-preview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
'use client'

import { useRootColors } from '@cerberus-design/react'
import { VStack } from '@cerberus-design/styled-system/jsx'
import { useEffect, useRef } from 'react'

const colorList = ['dataViz.diverging.50', 'dataViz.diverging.200']

export default function CanvasPreview() {
const colors = useRootColors(colorList)
const canvasRef = useRef<HTMLCanvasElement>(null)

useEffect(() => {
if (!canvasRef.current) return
if (Object.keys(colors).length !== colorList.length) return

const ctx = canvasRef.current.getContext('2d')
if (!ctx) return

ctx.fillStyle = colors['dataViz.diverging.50']
ctx.fillRect(0, 0, 100, 100)

ctx.fillStyle = colors['dataViz.diverging.200']
ctx.fillRect(100, 0, 100, 100)
}, [colors])

return (
<VStack>
<canvas
width="200"
height="100"
ref={canvasRef}
style={{ border: '1px solid black' }}
/>
<small>This is adding data-viz colors to a canvas element.</small>
</VStack>
)
}
90 changes: 90 additions & 0 deletions docs/app/react/use-root-colors/doc.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
---
heading: 'useRootColors'
description: 'Get Cerberus colors from the document root.'
npm: '@cerberus-design/react'
source: 'hooks/useRootColors.ts'
recipe: ''
a11y: 'utilities'
---

import CodePreview from '@/app/components/CodePreview'
import {
WhenToUseAdmonition
} from '@/app/components/Admonition'
import CanvasPreview from './components/canvas-preview'

<WhenToUseAdmonition description="When you want to use Cerberus colors within a `canvas` element." />

This hook allows you to access Cerberus colors from the document root which is useful when you want to use Cerberus colors within a `canvas` element.

## Usage

<CodePreview preview={<CanvasPreview />}>
```tsx title="some-chart.tsx" {18}
'use client'

import { useRootColors } from '@cerberus/react'
import { useEffect, useState, useRef } from 'react'

// This is a list of Cerberus colors to use in the chart
const colorList = [
'dataViz.diverging.50',
'dataViz.diverging.100',
'dataViz.diverging.200',
]

// This is settings for X chart library
const chartSettings = {}

function SomeChart() {
const [settings, setSettings] = useState(chartSettings)
const colors = useRootColors(colorList)
const hasSetColors = useRef<boolean>(false)

useEffect(() => {
if (Object.keys(colors).length && !hasSetColors.current) {
setSettings({
...settings,
something: {
bgColor: colors[colorList[0]],
},
somethingElse: {
bgColor: colors[colorList[1]],
},
})
hasSetColors.current = true
}
}, [colors])

return (
<SomeChartLib settings={settings} />
)
}
```
</CodePreview>

## API

```ts showLineNumbers=false
define function useRootColors(colors: string[] = []): Record<string, string>
```

### Arguments

The `useRootColors` hook accepts the following optional arguments:

| Name | Default | Description |
| ------------ | ---------- | ------------------------------------------------ |
| `colors` | `[]` | An array of Cerberus color keys to retrieve. |

### Return

The `useRootColors` hook returns a memoized object with the same properties as the options object passed in.

```ts
{
'dataViz.diverging.50': '#F7F7F7',
'dataViz.diverging.100': '#E5E5E5',
'dataViz.diverging.200': '#C6C6C6',
}
```
28 changes: 28 additions & 0 deletions docs/app/react/use-root-colors/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import ApiLinks from '@/app/components/ApiLinks'
import OnThisPage from '../../components/OnThisPage'
import { PageMainContent, PageSections } from '../../components/PageLayout'
import UseThemeDoc, { frontmatter } from './doc.mdx'
import FeatureHeader from '@/app/components/FeatureHeader'
import type { MatchFeatureKind } from '@/app/components/MatchFeatureImg'

export default function UseRootColorsPage() {
return (
<>
<PageMainContent>
<FeatureHeader
heading={frontmatter.heading}
description={frontmatter.description}
a11y={frontmatter.a11y as MatchFeatureKind}
/>
<ApiLinks {...frontmatter} />
<main>
<UseThemeDoc />
</main>
</PageMainContent>

<PageSections>
<OnThisPage />
</PageSections>
</>
)
}
49 changes: 49 additions & 0 deletions packages/react/src/hooks/useRootColors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
'use client'

import { useEffect, useReducer } from 'react'

/**
* This module provides a hook to get Cerberus colors from the document root.
* @module useRootColors
*/

export type RootColorsResult = Record<string, string>

/**
* This hook returns a record of Cerberus colors from the document root.
* This is useful when you are working with a component that uses the `<canvas>`
* element.
* @param colors - An array of Cerberus tokens to get from the document root (i.e. `['dataViz.diverging.50', 'dataViz.diverging.200']`).
* @returns A record of Cerberus colors where the key is the token name provided and the value is the color hex.
*/
export function useRootColors(colors: string[] = []): RootColorsResult {
const [state, dispatch] = useReducer(rootColorsReducer, {})

useEffect(() => {
if (Object.keys(state).length === colors.length) return

const rootStyles = getComputedStyle(document.body)
const rootColors = colors.reduce((acc, color) => {
const formattedColor = color
.replace(/([a-z])([A-Z])/g, '$1-$2')
.toLowerCase()
.replaceAll('.', '-')
acc[color as keyof typeof acc] = rootStyles
.getPropertyValue(`--cerberus-colors-${formattedColor}`)
.trim()
return acc
}, {} as RootColorsResult)

dispatch(rootColors)
}, [colors])

// reducer is already memoized
return state
}

function rootColorsReducer(
state: Record<string, string>,
action: Record<string, string>,
): Record<string, string> {
return { ...state, ...action }
}
1 change: 1 addition & 0 deletions packages/react/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export * from './hooks/useDate'
export * from './hooks/useModal'
export * from './hooks/useTheme'
export * from './hooks/useToggle'
export * from './hooks/useRootColors'

// aria-helpers

Expand Down
34 changes: 34 additions & 0 deletions tests/react/hooks/useRootColors.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { describe, test, expect, afterEach } from 'bun:test'
import { render, screen, cleanup } from '@testing-library/react'
import { useRootColors } from '@cerberus-design/react'
import { setupStrictMode } from '@/utils'

describe('useRootColors', () => {
setupStrictMode()
afterEach(cleanup)

function Test() {
const colors = useRootColors([
'dataViz.diverging.50',
'dataViz.diverging.200',
])

return (
<div>
{Object.keys(colors).map((color) => (
<div key={color}>
<span>{color}</span>
</div>
))}
</div>
)
}

test('should return a record of colors', () => {
render(<Test />)
// There's no document to store styles in a test environment, so we can't
// test this hook to the full extent.
expect(screen.getByText(/dataViz.diverging.50/i)).toBeTruthy()
expect(screen.getByText(/dataViz.diverging.200/i)).toBeTruthy()
})
})

0 comments on commit 394d7c4

Please sign in to comment.