Skip to content
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

Fix for hasFocus / checked binding if not applied via DataBindProvider #208

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions packages/binding.core/spec/checkedBehaviors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {

import {
applyBindings
, applyBindingsToNode
} from '@tko/bind'

import {
Expand All @@ -23,6 +24,8 @@ import { bindings as templateBindings } from '@tko/binding.template'

import '@tko/utils/helpers/jasmine-13-helper'

import $ from 'jquery'

describe('Binding: Checked', function () {
beforeEach(jasmine.prepareTestNode)

Expand All @@ -33,6 +36,45 @@ describe('Binding: Checked', function () {
provider.bindingHandlers.set(templateBindings)
})

it('Checked Binding should update observable value if checked binding is applied in another CustomBinding', function () {
const RadioGroupExample = {
init: (element: HTMLElement, valueAccessor: () => any) => {
let group = $(element);
let options = valueAccessor();
let items = options.items();

for (let i = 0; i < items.length; i++) {
let radioBox = $('<input type="radio">').appendTo(group);

applyBindingsToNode(radioBox[0], {
checked: items[i].checked,
attr:{
value: items[i].value
}
})
}
}
};

options.bindingProviderInstance.bindingHandlers.set("customBinding", RadioGroupExample)

var $decision = observable("")
var yesOption = { value: "True", checked: $decision }
var noOption = { value: "False", checked: $decision }

testNode.innerHTML = '<div data-bind="customBinding: radioBoxProp"></div>'
applyBindings({radioBoxProp: { items: observable([yesOption, noOption]) } }, testNode);

const yesRadio = testNode.children[0].children[0]
const noRadio = testNode.children[0].children[1]

expect($decision()).toEqual("")
triggerEvent(yesRadio, 'click')
expect($decision()).toEqual("True")
triggerEvent(noRadio, 'click')
expect($decision()).toEqual("False")
})

it('Triggering a click should toggle a checkbox\'s checked state before the event handler fires', function () {
// This isn't strictly to do with the checked binding, but if this doesn't work, the rest of the specs aren't meaningful
testNode.innerHTML = "<input type='checkbox' />"
Expand Down
29 changes: 29 additions & 0 deletions packages/binding.core/spec/hasfocusBehaviors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {

import {
applyBindings
, applyBindingsToNode
} from '@tko/bind'

import {
Expand All @@ -22,6 +23,7 @@ import * as coreBindings from '../dist'

import '@tko/utils/helpers/jasmine-13-helper'

import $ from 'jquery';

arrayForEach(['hasfocus', 'hasFocus'], binding => {
describe(`Binding: ${binding}`, function () {
Expand All @@ -42,6 +44,33 @@ arrayForEach(['hasfocus', 'hasFocus'], binding => {
beforeEach(function () { waits(100) })
}

it('Should set an observable value to be true on focus and false on blur even if the binding is applied through another binding', function () {
const createElementWithHasFocusBinding = {
init: (element: HTMLElement, valueAccessor: () => any) => {
let parent = $(element);
let $hasFocus = valueAccessor();

applyBindingsToNode(parent[0], { hasFocus: $hasFocus })
}
};
options.bindingProviderInstance.bindingHandlers.set("customBinding", createElementWithHasFocusBinding)

testNode.innerHTML = `<input data-bind='customBinding: customProps' /><input />`

var $myVal = observable(false)
applyBindings({customProps: $myVal}, testNode );
const input = testNode.children[0] as HTMLInputElement;

input.focus()
triggerEvent(input, 'focusin')
expect($myVal()).toEqual(true);

// Move the focus elsewhere
(testNode.childNodes[1] as HTMLElement).focus()
triggerEvent(input, 'focusout')
expect($myVal()).toEqual(false)
})

it('Should respond to changes on an observable value by blurring or focusing the element', function () {
var currentState
var model = { myVal: observable() }
Expand Down
6 changes: 5 additions & 1 deletion packages/binding.core/src/checked.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,11 @@ export var checked = {
elemValue = undefined
}
}
valueAccessor(elemValue, {onlyIfChanged: true})
// valueAccessor(elemValue, {onlyIfChanged: true})
var modelValue = valueAccessor(elemValue, {onlyIfChanged: true});
if (isWriteableObservable(modelValue) && (modelValue.peek() !== elemValue)) {
modelValue(elemValue);
}
}
};

Expand Down
16 changes: 12 additions & 4 deletions packages/binding.core/src/hasfocus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import {
} from '@tko/utils'

import {
unwrap, dependencyDetection
unwrap
, dependencyDetection
, isWriteableObservable
} from '@tko/observable'

var hasfocusUpdatingProperty = createSymbolOrString('__ko_hasfocusUpdating')
Expand All @@ -31,9 +33,15 @@ export var hasfocus = {
}
isFocused = (active === element)
}
// var modelValue = valueAccessor();
valueAccessor(isFocused, {onlyIfChanged: true})

var modelValue = valueAccessor(isFocused, {onlyIfChanged: true});
// In the scenario that hasFocus is binded not via the DataBindProvider
// but as an example as a LegacyBinding HasFocus value changes were not fired
// the fix was transfered from ko 3.5 (Focusout event was not fired)
// This only replies the value if there are changes
// it won't effect if already set by valueAccessor (Parser.convertToAccessors)
if (isWriteableObservable(modelValue) && (modelValue.peek() !== isFocused)) {
modelValue(isFocused);
}
// cache the latest value, so we can avoid unnecessarily calling focus/blur in the update function
element[hasfocusLastValue] = isFocused
element[hasfocusUpdatingProperty] = false
Expand Down
Loading