-
Notifications
You must be signed in to change notification settings - Fork 20
Animation on hover w/ Gleam Logo #2807
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 22 commits
52b2af2
6859dfc
175ee69
c864859
c4ab10f
ea04fe0
a20cf9f
6cc3d5b
0335cf1
72c9864
4ecb22e
0ac1be3
cf62de1
1d67c55
d007423
88fa7b8
257025c
e951505
d798652
8052924
8603f7e
5853d88
53f6401
711a877
733209d
63cdade
f6bfef5
a9d58af
662128f
1ea7dda
2b86774
ad8dbb2
75d66f3
9c91bd9
bac2acc
8e9e0d0
d8cfe02
cfe0f6a
bcf6a32
df4458e
88b1138
dd80705
092fcd5
3757657
6ad0f70
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
<div {{did-insert this.handleDidInsert}} {{will-destroy this.cleanupRive}} style={{this.containerStyle}} ...attributes> | ||
{{! The canvas will be inserted here by Rive }} | ||
</div> |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,114 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { action } from '@ember/object'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import Component from '@glimmer/component'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { tracked } from '@glimmer/tracking'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { Fit, Layout, Rive } from '@rive-app/canvas'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import * as Sentry from '@sentry/ember'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
interface GleamLogoSignature { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Element: HTMLDivElement; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Args: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
height: number; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's try to avoid needing a number to be passed in and see if we can just have it fit the container / parent element |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Blocks: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
default: []; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
export default class GleamLogoComponent extends Component<GleamLogoSignature> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
animationInterval: number | null = null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
container: HTMLElement | null = null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
@tracked riveInstance: Rive | null = null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
get containerStyle(): string { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Ensure minimum size on mobile while maintaining aspect ratio | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const minSize = Math.min(this.args.height, 200); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Arpan-206 where does the "200" number come from? |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return `height: ${minSize}px; width: ${minSize}px; max-width: 100%;`; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
@action | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
cleanupRive() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (this.animationInterval) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
clearInterval(this.animationInterval); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
this.animationInterval = null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (this.riveInstance) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
this.riveInstance.stop(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
this.riveInstance = null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
@action | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
handleDidInsert(element: HTMLDivElement) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
this.container = element; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const canvas = document.createElement('canvas'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Set initial canvas size for high quality | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const baseSize = 400; // Base size for quality | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Arpan-206 let's see if we can avoid this magic number too and just have it pick a sensible value (like based on pixel density & container height) |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
canvas.width = baseSize; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here are we assuming that all animations are square? Don't think we can do this, very possible to have animations that aren't square (even some of our language logos aren't square) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Arpan-206 to ensure that we aren't doing anything specific to Gleam, let's try this with a few different Rive animations (download random ones off the internet). Extract a |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
canvas.height = baseSize; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Let the canvas scale naturally within its container | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
canvas.style.width = '100%'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Arpan-206 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
canvas.style.height = '100%'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Arpan-206 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
canvas.style.display = 'block'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
canvas.style.maxWidth = '100%'; // Ensure it doesn't overflow on mobile | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
element.appendChild(canvas); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
this.riveInstance = new Rive({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
src: '/assets/animations/gleam_logo_animation.riv', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
canvas: canvas, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
stateMachines: 'State Machine 1', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is mandatory to pass in state machine names? Will this work without it? |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
autoplay: true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
automaticallyHandleEvents: true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
layout: new Layout({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
fit: Fit.Contain, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
onLoad: () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (this.riveInstance) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Initial resize | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
this.riveInstance.resizeDrawingSurfaceToCanvas(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Arpan-206 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const inputs = this.riveInstance.stateMachineInputs('State Machine 1'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Try to trigger hover state | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (inputs) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's move most logic to the animatino file itself, and maybe name the animation / state machine |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
inputs.forEach((input) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (input.name.toLowerCase().includes('hover')) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
input.value = true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Set timeout to trigger hover out after 1 second | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
setTimeout(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (this.riveInstance) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
input.value = false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}, 1000); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Store and clean up the hover timeout. The hover timeout is not stored or cleared when the component is destroyed, which could lead to errors if the component is removed before the timeout completes. @tracked riveInstance: Rive | null = null;
container: HTMLElement | null = null;
animationInterval: number | null = null;
+ hoverTimeout: number | null = null;
@action
cleanupRive() {
if (this.animationInterval) {
clearInterval(this.animationInterval);
this.animationInterval = null;
}
+ if (this.hoverTimeout) {
+ clearTimeout(this.hoverTimeout);
+ this.hoverTimeout = null;
+ }
if (this.riveInstance) {
this.riveInstance.stop();
this.riveInstance = null;
}
}
// Then when setting the timeout:
- setTimeout(() => {
+ this.hoverTimeout = window.setTimeout(() => {
if (this.riveInstance) {
input.value = false;
}
}, 1000); 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} catch (error: unknown) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
console.error('Error setting up Rive:', error); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's report this to sentry, don't just swallow and ignore |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Sentry.captureException(error, { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Arpan-206 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
tags: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
component: 'GleamLogo', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
action: 'handleDidInsert', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainImplement fallback UI for animation failures. The error handling reports to Sentry but doesn't provide any fallback UI when the animation fails to load. Consider adding a fallback to display a static image or message. @tracked riveInstance: Rive | null = null;
@tracked containerSize: number = 0;
+ @tracked hasLoadError: boolean = false;
// In the catch block:
} catch (error: unknown) {
console.error('Error setting up Rive:', error);
+ this.hasLoadError = true;
Sentry.captureException(error, {
tags: {
component: 'GleamLogo',
action: 'handleDidInsert',
},
});
} Then in your template file (gleam-logo.hbs), you would add: <div {{did-insert this.handleDidInsert}} {{will-destroy this.handleWillDestroy}} style={{this.containerStyle}}>
{{#if this.hasLoadError}}
<img src="/assets/images/gleam-logo.svg" alt="Gleam Logo" style="width: 100%; height: 100%;" />
{{/if}}
</div> Let's verify if a static fallback image exists: 🏁 Script executed: #!/bin/bash
# Check if a static Gleam logo exists
find public/assets -type f -name "*gleam*" | grep -i '.svg\|.png\|.jpg'
# Also check if there's a generic fallback image pattern in the codebase
grep -r "hasLoadError" --include="*.hbs" app/ Length of output: 353 Fallback UI for Rive animation failures Add a boolean flag to the component and render a static SVG when the animation fails: • In @tracked riveInstance: Rive | null = null;
@tracked containerSize: number = 0;
+ @tracked hasLoadError: boolean = false;
…
} catch (error: unknown) {
console.error('Error setting up Rive:', error);
+ this.hasLoadError = true;
Sentry.captureException(error, {
tags: {
component: 'GleamLogo',
action: 'handleDidInsert',
},
});
} • In <div
{{#if (not this.hasLoadError)}}
{{did-insert this.handleDidInsert}}
{{will-destroy this.handleWillDestroy}}
{{/if}}
style={{this.containerStyle}}
>
{{#if this.hasLoadError}}
<img
src="/assets/images/language-logos/gleam-color.svg"
alt="Gleam Logo"
style="width:100%; height:100%;"
/>
{{/if}}
</div> Note: Available static SVGs include
Pick the variant that best matches your design. 🧰 Tools🪛 GitHub Check: codecov/patch[warning] 102-103: app/components/gleam-logo.ts#L102-L103 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
declare module '@glint/environment-ember-loose/registry' { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
export default interface Registry { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
GleamLogo: typeof GleamLogoComponent; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,6 @@ | ||
{{#if (eq @variant "color")}} | ||
{{#if (and (eq @language.slug "gleam") (eq @variant "color"))}} | ||
<GleamLogo @height={{144}} ...attributes /> | ||
{{else if (eq @variant "color")}} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Arpan-206 I mentioned this before, let's move this out to the tracks page ![]() |
||
<img alt={{@language.name}} src="{{@language.colorLogoUrl}}" ...attributes /> | ||
{{else if (eq @variant "gray")}} | ||
<img alt={{@language.name}} src="{{@language.grayLogoUrl}}" ...attributes /> | ||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,134 @@ | ||||||||||||||||||||||||||||||||||
import { module, test } from 'qunit'; | ||||||||||||||||||||||||||||||||||
import { setupRenderingTest } from 'codecrafters-frontend/tests/helpers'; | ||||||||||||||||||||||||||||||||||
import { render, settled } from '@ember/test-helpers'; | ||||||||||||||||||||||||||||||||||
import { hbs } from 'ember-cli-htmlbars'; | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
// Mock Rive class for testing | ||||||||||||||||||||||||||||||||||
class MockRive { | ||||||||||||||||||||||||||||||||||
constructor(options) { | ||||||||||||||||||||||||||||||||||
this.canvas = options.canvas; | ||||||||||||||||||||||||||||||||||
this._listeners = new Map(); | ||||||||||||||||||||||||||||||||||
this._onLoadCallback = null; | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
off(event, callback) { | ||||||||||||||||||||||||||||||||||
const listeners = this._listeners.get(event); | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
if (listeners) { | ||||||||||||||||||||||||||||||||||
const index = listeners.indexOf(callback); | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
if (index > -1) { | ||||||||||||||||||||||||||||||||||
listeners.splice(index, 1); | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
on(event, callback) { | ||||||||||||||||||||||||||||||||||
if (!this._listeners.has(event)) { | ||||||||||||||||||||||||||||||||||
this._listeners.set(event, []); | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
this._listeners.get(event)?.push(callback); | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
resizeDrawingSurfaceToCanvas() { | ||||||||||||||||||||||||||||||||||
// No-op for test | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
simulateLoad() { | ||||||||||||||||||||||||||||||||||
if (this._onLoadCallback) { | ||||||||||||||||||||||||||||||||||
this._onLoadCallback(); | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
stateMachineInputs() { | ||||||||||||||||||||||||||||||||||
return [ | ||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||
name: 'Hover', | ||||||||||||||||||||||||||||||||||
value: false, | ||||||||||||||||||||||||||||||||||
type: 'boolean', | ||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||
]; | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
stop() { | ||||||||||||||||||||||||||||||||||
// No-op for test | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
triggerEvent(event, ...args) { | ||||||||||||||||||||||||||||||||||
const listeners = this._listeners.get(event); | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
if (listeners) { | ||||||||||||||||||||||||||||||||||
listeners.forEach((callback) => callback(event, ...args)); | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
module('Integration | Component | gleam-logo', function (hooks) { | ||||||||||||||||||||||||||||||||||
setupRenderingTest(hooks); | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
hooks.beforeEach(function () { | ||||||||||||||||||||||||||||||||||
this.originalRive = window.Rive; | ||||||||||||||||||||||||||||||||||
window.Rive = class MockRiveConstructor { | ||||||||||||||||||||||||||||||||||
constructor(options) { | ||||||||||||||||||||||||||||||||||
const instance = new MockRive(options); | ||||||||||||||||||||||||||||||||||
this._onLoadCallback = options.onLoad; | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
return instance; | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix the constructor return pattern in MockRiveConstructor The constructor is returning a value, which is problematic in JavaScript. Constructors should initialize the object being created, not return a different object. window.Rive = class MockRiveConstructor {
constructor(options) {
- const instance = new MockRive(options);
this._onLoadCallback = options.onLoad;
-
- return instance;
+ Object.assign(this, new MockRive(options));
}
}; This approach ensures that 📝 Committable suggestion
Suggested change
🧰 Tools🪛 Biome (1.9.4)[error] 77-77: The constructor should not return a value. The constructor is here: Returning a value from a constructor is ignored. (lint/correctness/noConstructorReturn) |
||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
hooks.afterEach(function () { | ||||||||||||||||||||||||||||||||||
window.Rive = this.originalRive; | ||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
test('it renders and initializes correctly', async function (assert) { | ||||||||||||||||||||||||||||||||||
await render(hbs`<GleamLogo @height={{200}} />`); | ||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We're passing in |
||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
// Check container dimensions | ||||||||||||||||||||||||||||||||||
const container = this.element.querySelector('div'); | ||||||||||||||||||||||||||||||||||
assert.ok(container, 'Container element exists'); | ||||||||||||||||||||||||||||||||||
assert.strictEqual(container?.style.height, '200px', 'Height is set correctly'); | ||||||||||||||||||||||||||||||||||
assert.strictEqual(container?.style.width, '200px', 'Width is set correctly'); | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
// Check canvas dimensions and styles | ||||||||||||||||||||||||||||||||||
const canvas = this.element.querySelector('canvas'); | ||||||||||||||||||||||||||||||||||
assert.ok(canvas, 'Canvas element exists'); | ||||||||||||||||||||||||||||||||||
assert.strictEqual(canvas?.width, 400, 'Canvas width is set to base size for quality'); | ||||||||||||||||||||||||||||||||||
assert.strictEqual(canvas?.height, 400, 'Canvas height is set to base size for quality'); | ||||||||||||||||||||||||||||||||||
assert.strictEqual(canvas?.style.width, '100%', 'Canvas width style is set correctly'); | ||||||||||||||||||||||||||||||||||
assert.strictEqual(canvas?.style.height, '100%', 'Canvas height style is set correctly'); | ||||||||||||||||||||||||||||||||||
assert.strictEqual(canvas?.style.display, 'block', 'Canvas display style is set correctly'); | ||||||||||||||||||||||||||||||||||
assert.strictEqual(canvas?.style.maxWidth, '100%', 'Canvas max-width style is set correctly'); | ||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
test('it handles hover state correctly', async function (assert) { | ||||||||||||||||||||||||||||||||||
await render(hbs`<GleamLogo />`); | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
const container = this.element.querySelector('div'); | ||||||||||||||||||||||||||||||||||
assert.ok(container, 'Container exists'); | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
// Create and attach mock Rive instance | ||||||||||||||||||||||||||||||||||
const mockRive = new MockRive({ canvas: container.querySelector('canvas') }); | ||||||||||||||||||||||||||||||||||
container.__riveInstance = mockRive; | ||||||||||||||||||||||||||||||||||
assert.ok(mockRive, 'Rive instance exists'); | ||||||||||||||||||||||||||||||||||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
// Simulate Rive load event | ||||||||||||||||||||||||||||||||||
mockRive.simulateLoad(); | ||||||||||||||||||||||||||||||||||
await settled(); | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
const inputs = mockRive.stateMachineInputs(); | ||||||||||||||||||||||||||||||||||
assert.ok(inputs, 'State machine inputs exist'); | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
const hoverInput = inputs.find((input) => input.name.toLowerCase().includes('hover')); | ||||||||||||||||||||||||||||||||||
assert.ok(hoverInput, 'Hover input exists'); | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
// Verify initial hover state | ||||||||||||||||||||||||||||||||||
assert.false(hoverInput.value, 'Hover input is initially false'); | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
// Wait for hover out timeout | ||||||||||||||||||||||||||||||||||
await settled(); | ||||||||||||||||||||||||||||||||||
assert.false(hoverInput.value, 'Hover input is set to false after timeout'); | ||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Arpan-206 Why is the naming convention inconsistent here?
did-insert
usesthis.handleDidInsert
(correct), andwill-destroy
usesthis.cleanupRive
instead ofthis.handleWillDestroy
?