Skip to content
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
31 changes: 26 additions & 5 deletions core/modules/widgets/button.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,10 +151,30 @@ ButtonWidget.prototype.getBoundingClientRect = function() {
};

ButtonWidget.prototype.isSelected = function() {
return this.setTitle ? (this.setField ? this.wiki.getTiddler(this.setTitle).getFieldString(this.setField) === this.setTo :
(this.setIndex ? this.wiki.extractTiddlerDataItem(this.setTitle,this.setIndex) === this.setTo :
this.wiki.getTiddlerText(this.setTitle))) || this.defaultSetValue || this.getVariable("currentTiddler") :
this.wiki.getTextReference(this.set,this.defaultSetValue,this.getVariable("currentTiddler")) === this.setTo;
var currentValue;
if(this.setTitle) {
if(this.setField) {
var tiddler = this.wiki.getTiddler(this.setTitle);
currentValue = tiddler ? tiddler.getFieldString(this.setField) : undefined;
} else if(this.setIndex) {
currentValue = this.wiki.extractTiddlerDataItem(this.setTitle,this.setIndex);
} else {
currentValue = this.wiki.getTiddlerText(this.setTitle);
}
if(!currentValue) {
currentValue = this.defaultSetValue || this.getVariable("currentTiddler");
}
} else {
currentValue = this.wiki.getTextReference(this.set,this.defaultSetValue,this.getVariable("currentTiddler"));
}
// If validValues is specified, fall back to default when current value doesn't match any valid value
if(this.validValues && currentValue !== this.defaultSetValue) {
var validValuesList = this.wiki.filterTiddlers(this.validValues,this);
if(validValuesList.indexOf(currentValue) === -1) {
currentValue = this.defaultSetValue;
}
}
return currentValue === this.setTo;
};

ButtonWidget.prototype.isPoppedUp = function() {
Expand Down Expand Up @@ -240,6 +260,7 @@ ButtonWidget.prototype.execute = function() {
this.popupAbsCoords = this.getAttribute("popupAbsCoords", "no");
this.tabIndex = this.getAttribute("tabindex");
this.isDisabled = this.getAttribute("disabled","no");
this.validValues = this.getAttribute("validValues");
// Make child widgets
this.makeChildWidgets();
};
Expand Down Expand Up @@ -267,7 +288,7 @@ Selectively refreshes the widget if needed. Returns true if the widget or any of
*/
ButtonWidget.prototype.refresh = function(changedTiddlers) {
var changedAttributes = this.computeAttributes();
if(changedAttributes.tooltip || changedAttributes.actions || changedAttributes.to || changedAttributes.message || changedAttributes.param || changedAttributes.set || changedAttributes.setTo || changedAttributes.popup || changedAttributes.hover || changedAttributes.selectedClass || changedAttributes.style || changedAttributes.dragFilter || changedAttributes.dragTiddler || (this.set && changedTiddlers[this.set]) || (this.popup && changedTiddlers[this.popup]) || (this.popupTitle && changedTiddlers[this.popupTitle]) || changedAttributes.popupAbsCoords || changedAttributes.setTitle || changedAttributes.setField || changedAttributes.setIndex || changedAttributes.popupTitle || changedAttributes.disabled || changedAttributes["default"]) {
if(changedAttributes.tooltip || changedAttributes.actions || changedAttributes.to || changedAttributes.message || changedAttributes.param || changedAttributes.set || changedAttributes.setTo || changedAttributes.popup || changedAttributes.hover || changedAttributes.selectedClass || changedAttributes.style || changedAttributes.dragFilter || changedAttributes.dragTiddler || (this.set && changedTiddlers[this.set]) || (this.setTitle && changedTiddlers[this.setTitle]) || (this.popup && changedTiddlers[this.popup]) || (this.popupTitle && changedTiddlers[this.popupTitle]) || changedAttributes.popupAbsCoords || changedAttributes.setTitle || changedAttributes.setField || changedAttributes.setIndex || changedAttributes.popupTitle || changedAttributes.disabled || changedAttributes["default"] || changedAttributes.validValues) {
this.refreshSelf();
return true;
} else {
Expand Down
10 changes: 9 additions & 1 deletion core/modules/widgets/reveal.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ RevealWidget.prototype.execute = function() {
this.stateTitle = this.getAttribute("stateTitle");
this.stateField = this.getAttribute("stateField");
this.stateIndex = this.getAttribute("stateIndex");
this.validValues = this.getAttribute("validValues");
this.readState();
// Construct the child widgets
var childNodes = this.isOpen ? this.parseTreeNode.children : [];
Expand Down Expand Up @@ -180,6 +181,13 @@ RevealWidget.prototype.readState = function() {
if(state === null) {
state = this["default"];
}
// If validValues is specified, fall back to default when state doesn't match any valid value
if(this.validValues && state !== defaultState) {
var validValuesList = this.wiki.filterTiddlers(this.validValues,this);
if(validValuesList.indexOf(state) === -1) {
state = defaultState;
}
}
switch(this.type) {
case "popup":
this.readPopupState(state);
Expand Down Expand Up @@ -232,7 +240,7 @@ Selectively refreshes the widget if needed. Returns true if the widget or any of
*/
RevealWidget.prototype.refresh = function(changedTiddlers) {
var changedAttributes = this.computeAttributes();
if(changedAttributes.state || changedAttributes.type || changedAttributes.text || changedAttributes.position || changedAttributes.positionAllowNegative || changedAttributes["default"] || changedAttributes.animate || changedAttributes.stateTitle || changedAttributes.stateField || changedAttributes.stateIndex) {
if(changedAttributes.state || changedAttributes.type || changedAttributes.text || changedAttributes.position || changedAttributes.positionAllowNegative || changedAttributes["default"] || changedAttributes.animate || changedAttributes.stateTitle || changedAttributes.stateField || changedAttributes.stateIndex || changedAttributes.validValues) {
this.refreshSelf();
return true;
} else {
Expand Down
3 changes: 2 additions & 1 deletion core/wiki/macros/tabs.tid
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ code-body: yes
set=<<tabsState>>
setTo=<<currentTab>>
default=<<__default__>>
validValues=<<__tabsList__>>
selectedClass="tc-tab-selected"
selectedAria="aria-selected"
tooltip={{!!tooltip}}
Expand Down Expand Up @@ -46,7 +47,7 @@ code-body: yes
\define tabs-tab-body()
\whitespace trim
<$list filter=<<__tabsList__>> variable="currentTab">
<$reveal type="match" state=<<tabsState>> text=<<currentTab>> default=<<__default__>> retain=<<__retain__>> tag="div">
<$reveal type="match" state=<<tabsState>> text=<<currentTab>> default=<<__default__>> validValues=<<__tabsList__>> retain=<<__retain__>> tag="div">
<$transclude tiddler=<<__template__>> mode="block">
<$transclude tiddler=<<currentTab>> mode="block"/>
</$transclude>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
title: Button/ValidValues/FilterValidValues
description: Button validValues — filter expression as validValues
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]

title: Narrative

This test verifies that the `validValues` attribute accepts filter expressions, not just literal space-separated lists. The valid values are defined by the filter `[tag[options]]`, which resolves to the tiddlers `OptionA` and `OptionB` (both tagged `options`).

The state tiddler contains `InvalidValue`, which is not in the filter results. The button widget falls back to `default` (`OptionA`), so button ''A'' should be highlighted with a red background.
+
title: OptionA
tags: options
text: A
+
title: OptionB
tags: options
text: B
+
title: $:/state/test-btn
text: InvalidValue
+
title: Output

\whitespace trim

<style>
.selected { background-color: red; color: white; }
</style>

<$button set="$:/state/test-btn"
setTo="OptionA"
default="OptionA"
validValues="[tag[options]]"
selectedClass="selected"
>
A
</$button>
<$button set="$:/state/test-btn"
setTo="OptionB"
default="OptionA"
validValues="[tag[options]]"
selectedClass="selected"
>
B
</$button>
+
title: ExpectedResult

<p><style>.selected { background-color: red; color: white; }</style></p><p><button aria-checked="true" class=" selected">A</button><button aria-checked="false" class="">B</button></p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
title: Button/ValidValues/InvalidState
description: Button validValues — invalid state falls back to default
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]

title: Narrative

This test verifies the core fix for [[issue #6548|https://github.com/TiddlyWiki/TiddlyWiki5/issues/6548]]. The state tiddler `$:/state/test-btn` contains `InvalidValue`, which does not match any of the values listed in the `validValues` attribute (`OptionA OptionB OptionC`).

Without the `validValues` attribute, no button would be highlighted because the state doesn't match any `setTo` value. With `validValues`, the button widget detects that the current state is not in the valid list and falls back to the `default` value (`OptionA`), so button ''A'' should be highlighted with a red background.
+
title: $:/state/test-btn
text: InvalidValue
+
title: Output

\whitespace trim

<style>
.selected { background-color: red; color: white; }
</style>

<$button set="$:/state/test-btn"
setTo="OptionA"
default="OptionA"
validValues="OptionA OptionB OptionC"
selectedClass="selected"
>
A
</$button>
<$button set="$:/state/test-btn"
setTo="OptionB"
default="OptionA"
validValues="OptionA OptionB OptionC"
selectedClass="selected"
>
B
</$button>
+
title: ExpectedResult

<p><style>.selected { background-color: red; color: white; }</style></p><p><button aria-checked="true" class=" selected">A</button><button aria-checked="false" class="">B</button></p>
37 changes: 37 additions & 0 deletions editions/test/tiddlers/tests/data/button-validvalues/NoState.tid
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
title: Button/ValidValues/NoState
description: Button validValues — missing state falls back to default
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]

title: Narrative

This test verifies that `validValues` does not change the existing default behaviour when the state tiddler does not exist. The button widget already falls back to `default` when there is no state tiddler — adding `validValues` should not break this. Button ''B'' should be highlighted with a red background because it matches the `default` value (`OptionB`).
+
title: Output

\whitespace trim

<style>
.selected { background-color: red; color: white; }
</style>

<$button set="$:/state/test-btn"
setTo="OptionA"
default="OptionB"
validValues="OptionA OptionB"
selectedClass="selected"
>
A
</$button>
<$button set="$:/state/test-btn"
setTo="OptionB"
default="OptionB"
validValues="OptionA OptionB"
selectedClass="selected"
>
B
</$button>
+
title: ExpectedResult

<p><style>.selected { background-color: red; color: white; }</style></p><p><button aria-checked="false" class="">A</button><button aria-checked="true" class=" selected">B</button></p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
title: Button/ValidValues/NoValidValues
description: Button without validValues — no regression
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]

title: Narrative

This is a regression test. The buttons here do ''not'' use the `validValues` attribute. The state tiddler contains `OptionB`, which matches button ''B''. This test ensures that existing button behaviour is completely unchanged when `validValues` is not specified — button ''B'' should be highlighted with a red background.
+
title: $:/state/test-btn
text: OptionB
+
title: Output

\whitespace trim

<style>
.selected { background-color: red; color: white; }
</style>

<$button set="$:/state/test-btn"
setTo="OptionA"
default="OptionA"
selectedClass="selected"
>
A
</$button>
<$button set="$:/state/test-btn"
setTo="OptionB"
default="OptionA"
selectedClass="selected"
>
B
</$button>
+
title: ExpectedResult

<p><style>.selected { background-color: red; color: white; }</style></p><p><button aria-checked="false" class="">A</button><button aria-checked="true" class=" selected">B</button></p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
title: Button/ValidValues/NoValidValuesInvalidState
description: Button without validValues and invalid state — no button selected
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]

title: Narrative

This test documents the default behaviour without `validValues`. The state tiddler contains `InvalidValue`, which does not match any button's `setTo`. Since there is no `validValues` attribute, no fallback occurs — neither button should be highlighted. This is the behaviour that [[issue #6548|https://github.com/TiddlyWiki/TiddlyWiki5/issues/6548]] reports as a problem, and which the `validValues` attribute is designed to fix.
+
title: $:/state/test-btn
text: InvalidValue
+
title: Output

\whitespace trim

<style>
.selected { background-color: red; color: white; }
</style>

<$button set="$:/state/test-btn"
setTo="OptionA"
default="OptionA"
selectedClass="selected"
>
A
</$button>
<$button set="$:/state/test-btn"
setTo="OptionB"
default="OptionA"
selectedClass="selected"
>
B
</$button>
+
title: ExpectedResult

<p><style>.selected { background-color: red; color: white; }</style></p><p><button aria-checked="false" class="">A</button><button aria-checked="false" class="">B</button></p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
title: Button/ValidValues/NoValidValuesNoState
description: Button without validValues and missing state — default button selected
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]

title: Narrative

This test documents the baseline default behaviour without `validValues`. The state tiddler does not exist, so the button widget falls back to the `default` value (`OptionB`). Button ''B'' should be highlighted with a red background. This is existing behaviour that must not change when `validValues` is added to the widget.
+
title: Output

\whitespace trim

<style>
.selected { background-color: red; color: white; }
</style>

<$button set="$:/state/test-btn"
setTo="OptionA"
default="OptionB"
selectedClass="selected"
>
A
</$button>
<$button set="$:/state/test-btn"
setTo="OptionB"
default="OptionB"
selectedClass="selected"
>
B
</$button>
+
title: ExpectedResult

<p><style>.selected { background-color: red; color: white; }</style></p><p><button aria-checked="false" class="">A</button><button aria-checked="true" class=" selected">B</button></p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
title: Button/ValidValues/SetFieldInvalidState
description: Button validValues with setTitle+setField — invalid state falls back to default
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]

title: Narrative

This test verifies that `validValues` works with `setTitle` + `setField`. The state is read from the `myfield` field of the state tiddler, which contains `InvalidValue`. Since this is not in `validValues`, the button matching `default` (`OptionA`) should be highlighted with a red background.
+
title: $:/state/test-btn
myfield: InvalidValue
+
title: Output

\whitespace trim

<style>
.selected { background-color: red; color: white; }
</style>

<$button setTitle="$:/state/test-btn"
setField="myfield"
setTo="OptionA"
default="OptionA"
validValues="OptionA OptionB"
selectedClass="selected"
>
A
</$button>
<$button setTitle="$:/state/test-btn"
setField="myfield"
setTo="OptionB"
default="OptionA"
validValues="OptionA OptionB"
selectedClass="selected"
>
B
</$button>
+
title: ExpectedResult

<p><style>.selected { background-color: red; color: white; }</style></p><p><button aria-checked="true" class=" selected">A</button><button aria-checked="false" class="">B</button></p>
Loading
Loading