Skip to content

Commit

Permalink
Simplified typing and added setValueState to update the currentState
Browse files Browse the repository at this point in the history
  • Loading branch information
latin-panda committed Jan 22, 2025
1 parent 6132e38 commit 9599af1
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 47 deletions.
16 changes: 7 additions & 9 deletions packages/web-forms/src/components/controls/RankControl.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,8 @@ interface RankControlProps {
readonly question: RankNode;
}
interface DraggableOption {
label: string;
value: string;
}
const props = defineProps<RankControlProps>();
const options = ref<DraggableOption[]>([]);
const options = ref([]);
const touched = ref(false);
const submitPressed = inject<boolean>('submitPressed');
const highlight = {
Expand All @@ -24,15 +19,18 @@ const highlight = {
};
const transformOptions = (rankOptions: RankItem[]) => {
options.value = rankOptions.map((option: RankItem) => {
options.value = rankOptions.map((option) => {
const value = option.value;
const valueOption = props.question.getValueOption(value);
if (valueOption == null) {
throw new Error(`Failed to find option for value: ${value}`);
throw new RankFunctionalityError(`Failed to find option for value: ${value}`);
}
return { value, label: valueOption.label.asString };
return {
value,
label: valueOption.label.asString,
};
});
};
Expand Down
7 changes: 7 additions & 0 deletions packages/xforms-engine/src/error/RankValueTypeError.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { XFormsSpecViolationError } from './XFormsSpecViolationError.ts';
import type { RankDefinition } from '../client/RankNode.ts';
import type { UnsupportedRankValueType } from '../lib/codecs/select/BaseSelectCodec.ts';
import { XPathFunctionalityError, type XPathFunctionalityErrorCategory } from './XPathFunctionalityError.ts';

export class RankValueTypeError extends XFormsSpecViolationError {
constructor(definition: RankDefinition<UnsupportedRankValueType>) {
Expand All @@ -13,3 +14,9 @@ export class RankValueTypeError extends XFormsSpecViolationError {
);
}
}

export class RankFunctionalityError extends XPathFunctionalityError {
constructor(functionalityMessagePrefix: string, category: XPathFunctionalityErrorCategory) {
super(functionalityMessagePrefix, category);
}
}
48 changes: 10 additions & 38 deletions packages/xforms-engine/src/instance/RankControl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import type { GeneralParentNode } from './hierarchy.ts';
import type { EvaluationContext } from './internal-api/EvaluationContext.ts';
import type { ValidationContext } from './internal-api/ValidationContext.ts';
import type { ClientReactiveSubmittableValueNode } from './internal-api/submission/ClientReactiveSubmittableValueNode.ts';
import { RankValueTypeError } from '../error/RankValueTypeError.ts';
import { RankFunctionalityError, RankValueTypeError } from '../error/RankValueTypeError.ts';
import { ItemCollectionCodec } from '../lib/codecs/ItemCollectionCodec.ts';
import { sharedValueCodecs } from '../lib/codecs/getSharedValueCodec.ts';
import type { AnyNodeDefinition } from '../parse/model/NodeDefinition.ts';
Expand Down Expand Up @@ -135,51 +135,23 @@ export class RankControl
}

/**
* Filters {@link values} to include only those values which are currently
* available in the mapping produced by {@link mapOptionsByValue}, i.e. within
* a potentially filtered itemset.
*
* Note: this method effectively produces an intersection of
* {@link sourceValues} and {@link values}. **Importantly**, ordering of the
* results is deterministic, preserving the order of values as yielded _by
* {@link sourceValues}_.
*
* At time of writing, there are several tests (in `@getodk/scenario`, ported
* from JavaRosa) which expect the values of a `<select>` to match the order
* they appear in the control's (potentially filtered) `<itemset>` (or list of
* `<item>`s, for forms defining those inline).
*
* @todo The `<odk:rank>` control, having semantics very similar to
* `<select>`, will likely perform similar filtering logic. However, one of
* the important distinctions between these controls is that `<odk:rank>`
* exists explicitly to control the order of values. It's quite likely that
* would be achieved by invoking the same logic with the parameter order
* reversed.
* This method is a client-facing convenience API for reading state,
* so it **MUST** read from client-reactive state!
* @param value
*/
private filterValues(
sourceValues: Iterable<string>,
values: Iterable<string>
): readonly string[] {
const selectedValues = new Set(values);

return Array
.from(sourceValues)
.filter(sourceValue => selectedValues.has(sourceValue));
}

// RankNode
getValueOption(value: string): RankItem | null {
// Note: this method is a client-facing convenience API for reading state,
// so it **MUST** read from client-reactive state!
const valueOption = this.currentState.valueOptions.find(item => item.value === value);
return valueOption ?? null;
}

setValues(values: readonly string[]): Root {
setValues(valuesInOrder: readonly string[]): Root {
const sourceValues = this.mapOptionsByValue().keys();
const effectiveValues = this.filterValues(sourceValues, values);
const hasAllValues = !sourceValues.some((sourceValue) => valuesInOrder.includes(sourceValue));
if (hasAllValues) {
throw new RankFunctionalityError('There are missing options. Rank should have all options');
}

this.setValueState(effectiveValues);
this.setValueState(valuesInOrder);

return this.root;
}
Expand Down

0 comments on commit 9599af1

Please sign in to comment.