Skip to content

Commit

Permalink
feat(smooth-corners): enhance to inject window as argument when activ…
Browse files Browse the repository at this point in the history
…ating smooth corners feature (#1642)

<!--
  How to write a good PR title:
- Follow [the Conventional Commits
specification](https://www.conventionalcommits.org/en/v1.0.0/).
  - Give as much context as necessary and as little as possible
  - Prefix it with [WIP] while it’s a work in progress
-->

## Self Checklist

- [x] I wrote a PR title in **English** and added an appropriate
**label** to the PR.
- [x] I wrote the commit message in **English** and to follow [**the
Conventional Commits
specification**](https://www.conventionalcommits.org/en/v1.0.0/).
- [x] I [added the
**changeset**](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md)
about the changes that needed to be released. (or didn't have to)
- [x] I wrote or updated **documentation** related to the changes. (or
didn't have to)
- [x] I wrote or updated **tests** related to the changes. (or didn't
have to)
- [x] I tested the changes in various browsers. (or didn't have to)
  - Windows: Chrome, Edge, (Optional) Firefox
  - macOS: Chrome, Edge, Safari, (Optional) Firefox

## Summary
<!-- Please brief explanation of the changes made -->

`SmoothCornersFeature.activate` 메서드에 매개변수로 globalObject를 추가합니다. 필요한 경우
FeatureProvider를 사용하지 않고`activate` 함수를 manually하게 실행하여 새로운 window에
smooth corner 스크립트를 추가할 수 있도록 합니다.

## Details
<!-- Please elaborate description of the changes -->

- 채널 데스크 앱에서 새 창을 이용해 만들어질 새로운 기능에 대한 대응 작업입니다.
- activate 함수의 재실행 조건을 변경합니다. 기존에 `activated === true` 조건 체크를
globalObject가 이전에 함수가 실행되었을 때 globalObject 인자와 같은지 체크하는 로직으로 변경합니다. 새로운
globalObject를 주입 시 다시 스크립트를 실행할 수 있도록 하고, 같은 globalObject라면 같은
PaintWorklet 글로벌 영역을 가지고 있을 것이기 때문에 기존 로직보다 더 명시적인 조건이라고 판단했습니다.
- 리팩토링: 클래스 상속을 인터페이스 기반으로 변경합니다.
- 리팩토링: 타입 단언을 타입 선언 오버로딩으로 변경합니다. 타입은 공식 스펙 문서를 참고했습니다.
- 유닛 테스트 추가

### Breaking change? (Yes/No)
<!-- If Yes, please describe the impact and migration path for users -->

No

## References
<!-- Please list any other resources or points the reviewer should be
aware of -->

- Type:
https://html.spec.whatwg.org/multipage/worklets.html#worklets-worklet
  • Loading branch information
sungik-choi authored Sep 26, 2023
1 parent 5b83d10 commit e503246
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 34 deletions.
9 changes: 9 additions & 0 deletions .changeset/heavy-bikes-clap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@channel.io/bezier-react": patch
---

Implement the feature to add scripts by injecting a window object into the `activate` function of `SmoothCornersFeatures`, if needed.

```tsx
SmoothCornersFeature.activate(window)
```
13 changes: 3 additions & 10 deletions packages/bezier-react/src/features/Feature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,7 @@ export enum FeatureType {
SmoothCorners = 'smooth-corners',
}

export class Feature {
name: FeatureType

constructor(name: FeatureType) {
this.name = name
}

async activate() {
return Promise.resolve(false)
}
export interface Feature {
readonly name: FeatureType
activate: () => Promise<boolean>
}
Original file line number Diff line number Diff line change
@@ -1,49 +1,80 @@
// eslint-disable-next-line no-restricted-imports
import {
Feature,
type Feature,
FeatureType,
} from '../Feature'

import { smoothCornersScript } from './smoothCornersScript'

/**
* @see {@link https://fetch.spec.whatwg.org/#requestcredentials}
*/
type RequestCredentials = 'omit' | 'same-origin' | 'include'

type WorkletOptions = {
credentials: RequestCredentials
}

/**
* @see {@link https://html.spec.whatwg.org/multipage/worklets.html#worklets-worklet}
*/
interface Worklet {
addModule: (moduleURL: string, options?: WorkletOptions) => Promise<void>
}

declare global {
namespace CSS {
export const paintWorklet: Worklet
}
}

/**
* 🚨 This is an experimental feature! It may not be suitable for use in production.
*
* Instead of CSS border-radius, Use *Superellipse* masking using the CSS Houdini API.
* When enabled, the feature will be applied to components with the `smoothCorners` property set to `true`.
*/
class SmoothCornersFeature extends Feature {
class SmoothCornersFeature implements Feature {
readonly name = FeatureType.SmoothCorners

private globalObject: typeof globalThis | null = null

/**
* @deprecated (@ed) Upcoming improvements
* Changed to private property, and will be referenced via the useFeatureFlag context rather than directly externally.
*/
activated = false

async activate() {
if (
!this.activated
&& typeof CSS !== 'undefined'
&& 'paintWorklet' in CSS
) {
activated: WeakMap<typeof globalThis, boolean> = new WeakMap([[globalThis, false]])

private updateCurrentGlobalObject(globalObject: typeof globalThis) {
this.globalObject = globalObject
}

private isGlobalObjectIdentical(globalObject: typeof globalThis) {
return Object.is(this.globalObject, globalObject)
}

private supportCSSPaintWorklet(globalObject: typeof globalThis) {
return typeof globalObject.CSS !== 'undefined' && 'paintWorklet' in globalObject.CSS
}

async activate(globalObject: typeof globalThis = globalThis) {
if (!this.isGlobalObjectIdentical(globalObject) && this.supportCSSPaintWorklet(globalObject)) {
const workletURL = URL.createObjectURL(
new Blob([smoothCornersScript], { type: 'application/javascript' }),
)

// @ts-ignore
const promise = CSS.paintWorklet.addModule(workletURL) as Promise<void>

return promise
.then(() => {
this.activated = true
return this.activated
}).catch(() => {
this.activated = false
return this.activated
})
try {
await globalObject.CSS.paintWorklet.addModule(workletURL)
this.activated.set(globalObject, true)
} catch {
this.activated.set(globalObject, false)
}
}

return this.activated
this.updateCurrentGlobalObject(globalObject)

return this.activated.get(globalObject) as boolean
}
}

export default new SmoothCornersFeature(FeatureType.SmoothCorners)
export default new SmoothCornersFeature()
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import SmoothCornersFeature from './SmoothCornersFeature'

describe('SmoothCornersFeature', () => {
let addModule: jest.Mock

beforeEach(() => {
addModule = jest.fn(() => Promise.resolve())

Object.defineProperty(global, 'URL', {
value: {
createObjectURL: jest.fn(),
},
})

Object.defineProperty(global, 'CSS', {
value: {
paintWorklet: { addModule },
},
})
})

it('If the global objects are the same, activate should only be executed once.', async () => {
await SmoothCornersFeature.activate(globalThis)
await SmoothCornersFeature.activate(globalThis)
expect(addModule).toHaveBeenCalledTimes(1)
})

it('If the global object has changed, activate should be executed again.', async () => {
await SmoothCornersFeature.activate(globalThis)
expect(addModule).toHaveBeenCalledTimes(0)
await SmoothCornersFeature.activate({ ...globalThis })
expect(addModule).toHaveBeenCalledTimes(1)
})
})
2 changes: 1 addition & 1 deletion packages/bezier-react/src/foundation/Mixins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ export const smoothCorners = ({
border-radius: ${borderRadius};
`}
${SmoothCornersFeature.activated && css`
${SmoothCornersFeature.activated.get(globalThis) && css`
@supports (background: paint(smooth-corners)) {
padding: ${shadowBlur * 2}px;
margin: ${-(shadowBlur * 2) + margin}px;
Expand Down

0 comments on commit e503246

Please sign in to comment.