cx 26.1.3 → 26.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.mocharc.json +5 -5
- package/LICENSE-THIRD-PARTY.md +91 -91
- package/LICENSE.md +7 -7
- package/README.md +46 -46
- package/build/ui/exprHelpers.d.ts +12 -0
- package/build/ui/exprHelpers.js +17 -0
- package/build/ui/tpl.d.ts +5 -3
- package/build/ui/tpl.js +11 -4
- package/build.js +133 -133
- package/dist/manifest.js +782 -779
- package/dist/ui.js +26 -4
- package/package.json +100 -100
- package/src/charts/Bar.scss +28 -28
- package/src/charts/Bar.ts +114 -114
- package/src/charts/BarGraph.scss +28 -28
- package/src/charts/BarGraph.tsx +145 -145
- package/src/charts/BubbleGraph.scss +31 -31
- package/src/charts/BubbleGraph.tsx +165 -165
- package/src/charts/Chart.ts +108 -108
- package/src/charts/ColorMap.ts +150 -150
- package/src/charts/Column.scss +28 -28
- package/src/charts/Column.ts +124 -124
- package/src/charts/ColumnBarBase.tsx +284 -284
- package/src/charts/ColumnBarGraphBase.tsx +229 -229
- package/src/charts/ColumnGraph.scss +29 -29
- package/src/charts/ColumnGraph.tsx +153 -153
- package/src/charts/Grid.ts +5 -5
- package/src/charts/Gridlines.scss +24 -24
- package/src/charts/Gridlines.tsx +88 -88
- package/src/charts/Legend.scss +50 -50
- package/src/charts/Legend.tsx +270 -270
- package/src/charts/LegendEntry.scss +29 -29
- package/src/charts/LegendEntry.tsx +197 -197
- package/src/charts/LineGraph.scss +24 -24
- package/src/charts/LineGraph.tsx +455 -455
- package/src/charts/Marker.scss +43 -43
- package/src/charts/Marker.tsx +483 -483
- package/src/charts/MarkerLine.scss +19 -19
- package/src/charts/MarkerLine.tsx +214 -214
- package/src/charts/MouseTracker.tsx +112 -112
- package/src/charts/Pie.ts +8 -8
- package/src/charts/PieChart.scss +28 -28
- package/src/charts/PieChart.tsx +717 -717
- package/src/charts/PieLabel.tsx +106 -106
- package/src/charts/PieLabelsContainer.ts +68 -68
- package/src/charts/Range.scss +19 -19
- package/src/charts/Range.tsx +318 -318
- package/src/charts/RangeMarker.scss +17 -17
- package/src/charts/RangeMarker.tsx +233 -233
- package/src/charts/ScatterGraph.scss +23 -23
- package/src/charts/ScatterGraph.tsx +245 -245
- package/src/charts/Swimlane.scss +16 -16
- package/src/charts/Swimlane.tsx +195 -195
- package/src/charts/Swimlanes.scss +16 -16
- package/src/charts/Swimlanes.tsx +179 -179
- package/src/charts/axis/Axis.scss +22 -22
- package/src/charts/axis/Axis.tsx +444 -444
- package/src/charts/axis/CategoryAxis.scss +34 -34
- package/src/charts/axis/CategoryAxis.tsx +313 -313
- package/src/charts/axis/NumericAxis.scss +34 -34
- package/src/charts/axis/NumericAxis.tsx +437 -437
- package/src/charts/axis/Stack.ts +61 -61
- package/src/charts/axis/TimeAxis.scss +33 -33
- package/src/charts/axis/TimeAxis.tsx +718 -718
- package/src/charts/axis/index.scss +5 -5
- package/src/charts/axis/index.ts +3 -3
- package/src/charts/axis/variables.scss +2 -2
- package/src/charts/helpers/MinMaxFinder.ts +66 -66
- package/src/charts/helpers/PointReducer.ts +135 -135
- package/src/charts/helpers/SnapPointFinder.ts +136 -136
- package/src/charts/helpers/ValueAtFinder.ts +72 -72
- package/src/charts/helpers/index.ts +4 -4
- package/src/charts/index.scss +22 -22
- package/src/charts/index.ts +34 -34
- package/src/charts/palette.scss +97 -97
- package/src/charts/shapes.tsx +86 -86
- package/src/charts/variables.scss +22 -22
- package/src/core.d.ts +182 -182
- package/src/data/AggregateFunction.ts +171 -171
- package/src/data/ArrayElementView.spec.ts +88 -88
- package/src/data/ArrayElementView.ts +91 -91
- package/src/data/ArrayRef.ts +34 -34
- package/src/data/AugmentedViewBase.ts +88 -88
- package/src/data/Binding.spec.ts +69 -69
- package/src/data/Binding.ts +104 -104
- package/src/data/ExposedRecordView.ts +95 -95
- package/src/data/ExposedValueView.ts +89 -89
- package/src/data/Expression.spec.ts +229 -229
- package/src/data/Expression.ts +233 -233
- package/src/data/Grouper.spec.ts +57 -57
- package/src/data/Grouper.ts +158 -158
- package/src/data/NestedDataView.ts +43 -43
- package/src/data/ReadOnlyDataView.ts +39 -39
- package/src/data/Ref.spec.ts +79 -79
- package/src/data/Ref.ts +104 -104
- package/src/data/Selector.ts +10 -10
- package/src/data/Store.spec.ts +22 -22
- package/src/data/Store.ts +52 -52
- package/src/data/StoreProxy.ts +19 -19
- package/src/data/StoreRef.spec.ts +24 -24
- package/src/data/StoreRef.ts +66 -66
- package/src/data/StringTemplate.spec.ts +132 -132
- package/src/data/StringTemplate.ts +93 -93
- package/src/data/StructuredSelector.spec.ts +113 -113
- package/src/data/StructuredSelector.ts +146 -146
- package/src/data/SubscribableView.ts +63 -63
- package/src/data/View.spec.ts +60 -60
- package/src/data/View.ts +289 -289
- package/src/data/ZoomIntoPropertyView.spec.ts +64 -64
- package/src/data/ZoomIntoPropertyView.ts +45 -45
- package/src/data/comparer.spec.ts +60 -60
- package/src/data/comparer.ts +78 -78
- package/src/data/computable.spec.ts +87 -87
- package/src/data/computable.ts +69 -69
- package/src/data/createAccessorModelProxy.spec.tsx +145 -145
- package/src/data/createAccessorModelProxy.ts +66 -66
- package/src/data/createStructuredSelector.spec.ts +45 -45
- package/src/data/createStructuredSelector.ts +62 -62
- package/src/data/defaultCompare.ts +14 -14
- package/src/data/diff/diffArrays.ts +49 -49
- package/src/data/diff/diffs.spec.ts +49 -49
- package/src/data/diff/index.ts +1 -1
- package/src/data/enableFatArrowExpansion.ts +6 -6
- package/src/data/getAccessor.spec.ts +11 -11
- package/src/data/getAccessor.ts +74 -74
- package/src/data/getSelector.spec.ts +43 -43
- package/src/data/getSelector.ts +66 -66
- package/src/data/index.ts +30 -30
- package/src/data/isSelector.ts +26 -26
- package/src/data/ops/append.spec.ts +28 -28
- package/src/data/ops/append.ts +5 -5
- package/src/data/ops/filter.spec.ts +35 -35
- package/src/data/ops/filter.ts +9 -9
- package/src/data/ops/findTreeNode.spec.ts +23 -23
- package/src/data/ops/findTreeNode.ts +14 -14
- package/src/data/ops/findTreePath.ts +23 -23
- package/src/data/ops/index.ts +11 -11
- package/src/data/ops/insertElement.ts +3 -3
- package/src/data/ops/merge.spec.ts +27 -27
- package/src/data/ops/merge.ts +13 -13
- package/src/data/ops/moveElement.ts +21 -21
- package/src/data/ops/removeTreeNodes.spec.ts +37 -37
- package/src/data/ops/removeTreeNodes.ts +15 -15
- package/src/data/ops/updateArray.spec.ts +69 -69
- package/src/data/ops/updateArray.ts +31 -31
- package/src/data/ops/updateTree.spec.ts +54 -54
- package/src/data/ops/updateTree.ts +23 -23
- package/src/data/test-types.ts +7 -7
- package/src/global.scss +13 -13
- package/src/hooks/createLocalStorageRef.ts +21 -21
- package/src/hooks/index.ts +8 -8
- package/src/hooks/invokeCallback.spec.tsx +59 -59
- package/src/hooks/invokeCallback.ts +8 -8
- package/src/hooks/resolveCallback.spec.tsx +71 -71
- package/src/hooks/resolveCallback.ts +16 -16
- package/src/hooks/store.spec.tsx +67 -67
- package/src/hooks/store.ts +46 -46
- package/src/hooks/useEffect.ts +14 -14
- package/src/hooks/useInterval.ts +8 -8
- package/src/hooks/useState.ts +20 -20
- package/src/hooks/useTrigger.spec.tsx +105 -105
- package/src/hooks/useTrigger.ts +26 -26
- package/src/index.scss +6 -6
- package/src/index.ts +7 -7
- package/src/jsx-dev-runtime.ts +4 -4
- package/src/jsx-runtime.spec.tsx +431 -431
- package/src/jsx-runtime.ts +79 -79
- package/src/locale/de-de.ts +76 -76
- package/src/locale/en-us.ts +75 -75
- package/src/locale/es-es.ts +76 -76
- package/src/locale/fr-fr.ts +76 -76
- package/src/locale/nl-nl.ts +76 -76
- package/src/locale/pt-pt.ts +76 -76
- package/src/locale/sr-latn-ba.ts +76 -76
- package/src/svg/BoundedObject.ts +101 -101
- package/src/svg/ClipRect.tsx +29 -29
- package/src/svg/Ellipse.tsx +68 -68
- package/src/svg/Line.tsx +58 -58
- package/src/svg/NonOverlappingRect.ts +26 -26
- package/src/svg/NonOverlappingRectGroup.ts +49 -49
- package/src/svg/Rectangle.tsx +90 -90
- package/src/svg/Svg.scss +27 -27
- package/src/svg/Svg.tsx +241 -241
- package/src/svg/Text.tsx +134 -134
- package/src/svg/TextualBoundedObject.ts +35 -35
- package/src/svg/index.scss +8 -8
- package/src/svg/index.ts +17 -17
- package/src/svg/util/Rect.ts +105 -105
- package/src/ui/CSS.ts +87 -87
- package/src/ui/CSSHelper.ts +17 -17
- package/src/ui/Container.tsx +216 -216
- package/src/ui/ContentResolver.spec.tsx +585 -585
- package/src/ui/ContentResolver.ts +132 -132
- package/src/ui/Controller.spec.tsx +574 -574
- package/src/ui/Controller.ts +202 -202
- package/src/ui/Culture.ts +159 -159
- package/src/ui/Cx.spec.tsx +210 -210
- package/src/ui/Cx.tsx +386 -386
- package/src/ui/DataProxy.spec.tsx +337 -337
- package/src/ui/DataProxy.ts +55 -55
- package/src/ui/DetachedScope.tsx +159 -159
- package/src/ui/FocusManager.ts +171 -171
- package/src/ui/Format.ts +108 -108
- package/src/ui/HoverSync.tsx +189 -189
- package/src/ui/Instance.ts +866 -866
- package/src/ui/IsolatedScope.spec.tsx +62 -62
- package/src/ui/IsolatedScope.ts +50 -50
- package/src/ui/Localization.ts +70 -70
- package/src/ui/Prop.ts +140 -140
- package/src/ui/PureContainer.spec.tsx +232 -232
- package/src/ui/PureContainer.tsx +19 -19
- package/src/ui/RenderingContext.ts +99 -99
- package/src/ui/Repeater.spec.tsx +143 -143
- package/src/ui/Repeater.ts +194 -194
- package/src/ui/Rescope.spec.tsx +199 -199
- package/src/ui/Rescope.ts +49 -49
- package/src/ui/ResizeManager.ts +30 -30
- package/src/ui/Restate.spec.tsx +422 -422
- package/src/ui/Restate.tsx +217 -217
- package/src/ui/StaticText.ts +11 -11
- package/src/ui/StructuredInstanceDataAccessor.ts +32 -32
- package/src/ui/Text.ts +49 -49
- package/src/ui/VDOM.ts +1 -1
- package/src/ui/Widget.spec.tsx +53 -53
- package/src/ui/Widget.tsx +301 -301
- package/src/ui/ZIndexManager.ts +11 -11
- package/src/ui/adapter/ArrayAdapter.spec.ts +55 -55
- package/src/ui/adapter/ArrayAdapter.ts +229 -229
- package/src/ui/adapter/DataAdapter.ts +52 -52
- package/src/ui/adapter/GroupAdapter.ts +235 -235
- package/src/ui/adapter/TreeAdapter.spec.ts +76 -76
- package/src/ui/adapter/TreeAdapter.ts +185 -185
- package/src/ui/adapter/index.ts +4 -4
- package/src/ui/app/History.ts +133 -133
- package/src/ui/app/Url.spec.ts +50 -50
- package/src/ui/app/Url.ts +102 -102
- package/src/ui/app/index.ts +5 -5
- package/src/ui/app/startAppLoop.tsx +71 -71
- package/src/ui/app/startHotAppLoop.ts +41 -41
- package/src/ui/batchUpdates.ts +77 -77
- package/src/ui/bind.ts +10 -10
- package/src/ui/createFunctionalComponent.spec.tsx +454 -454
- package/src/ui/createFunctionalComponent.ts +86 -86
- package/src/ui/expr.ts +47 -47
- package/src/ui/exprHelpers.spec.ts +412 -379
- package/src/ui/exprHelpers.ts +18 -0
- package/src/ui/flattenProps.ts +21 -21
- package/src/ui/index.scss +2 -2
- package/src/ui/index.ts +47 -47
- package/src/ui/keyboardShortcuts.ts +40 -40
- package/src/ui/layout/Content.ts +30 -30
- package/src/ui/layout/ContentPlaceholder.spec.tsx +599 -599
- package/src/ui/layout/ContentPlaceholder.ts +133 -133
- package/src/ui/layout/FirstVisibleChildLayout.spec.tsx +207 -207
- package/src/ui/layout/FirstVisibleChildLayout.ts +60 -60
- package/src/ui/layout/LabelsLeftLayout.scss +46 -46
- package/src/ui/layout/LabelsLeftLayout.tsx +76 -76
- package/src/ui/layout/LabelsTopLayout.scss +64 -64
- package/src/ui/layout/LabelsTopLayout.tsx +156 -156
- package/src/ui/layout/UseParentLayout.ts +8 -8
- package/src/ui/layout/exploreChildren.ts +38 -38
- package/src/ui/layout/index.scss +3 -3
- package/src/ui/layout/index.ts +10 -10
- package/src/ui/layout/variables.scss +2 -2
- package/src/ui/selection/KeySelection.ts +153 -153
- package/src/ui/selection/PropertySelection.ts +87 -87
- package/src/ui/selection/Selection.ts +128 -128
- package/src/ui/selection/index.ts +3 -3
- package/src/ui/tpl.spec.ts +69 -0
- package/src/ui/tpl.ts +17 -4
- package/src/ui/variables.scss +1 -1
- package/src/util/Component.spec.ts +411 -411
- package/src/util/Component.ts +301 -301
- package/src/util/Console.ts +13 -13
- package/src/util/DOM.ts +88 -88
- package/src/util/Debug.ts +71 -71
- package/src/util/Format.spec.ts +69 -69
- package/src/util/Format.ts +267 -267
- package/src/util/GlobalCacheIdentifier.ts +11 -11
- package/src/util/KeyCode.ts +21 -21
- package/src/util/SubscriberList.ts +86 -86
- package/src/util/Timing.ts +58 -58
- package/src/util/TraversalStack.spec.ts +53 -53
- package/src/util/TraversalStack.ts +47 -47
- package/src/util/addEventListenerWithOptions.ts +41 -41
- package/src/util/browserSupportsPassiveEventHandlers.ts +20 -20
- package/src/util/calculateNaturalElementHeight.ts +22 -22
- package/src/util/call-once.scss +6 -6
- package/src/util/capitalize.ts +4 -4
- package/src/util/coalesce.ts +6 -6
- package/src/util/color/hslToRgb.ts +34 -34
- package/src/util/color/index.ts +4 -4
- package/src/util/color/parseColor.ts +173 -173
- package/src/util/color/rgbToHex.ts +14 -14
- package/src/util/color/rgbToHsl.ts +35 -35
- package/src/util/date/dateDiff.ts +9 -9
- package/src/util/date/diff.ts +13 -13
- package/src/util/date/encodeDate.ts +8 -8
- package/src/util/date/encodeDateWithTimezoneOffset.ts +18 -18
- package/src/util/date/index.ts +11 -11
- package/src/util/date/lowerBoundCheck.ts +13 -13
- package/src/util/date/maxDate.ts +14 -14
- package/src/util/date/minDate.ts +14 -14
- package/src/util/date/monthStart.ts +8 -8
- package/src/util/date/parseDateInvariant.ts +20 -20
- package/src/util/date/sameDate.ts +11 -11
- package/src/util/date/upperBoundCheck.spec.ts +30 -30
- package/src/util/date/upperBoundCheck.ts +13 -13
- package/src/util/date/zeroTime.ts +9 -9
- package/src/util/debounce.ts +26 -26
- package/src/util/dummyCallback.ts +1 -1
- package/src/util/escapeSpecialRegexCharacters.ts +8 -8
- package/src/util/eventCallbacks.ts +12 -12
- package/src/util/expandFatArrows.ts +118 -118
- package/src/util/findScrollableParent.ts +15 -15
- package/src/util/getActiveElement.ts +4 -4
- package/src/util/getParentFrameBoundingClientRect.ts +13 -13
- package/src/util/getScrollerBoundingClientRect.ts +12 -12
- package/src/util/getSearchQueryPredicate.spec.ts +40 -40
- package/src/util/getSearchQueryPredicate.ts +58 -58
- package/src/util/getTopLevelBoundingClientRect.ts +7 -7
- package/src/util/getVendorPrefix.ts +33 -33
- package/src/util/hasKey.ts +18 -18
- package/src/util/index.scss +10 -10
- package/src/util/index.ts +55 -55
- package/src/util/innerTextTrim.ts +10 -10
- package/src/util/isArray.ts +3 -3
- package/src/util/isDataRecord.ts +5 -5
- package/src/util/isDefined.ts +3 -3
- package/src/util/isDigit.ts +8 -8
- package/src/util/isFunction.ts +3 -3
- package/src/util/isNonEmptyArray.ts +3 -3
- package/src/util/isNumber.ts +3 -3
- package/src/util/isObject.ts +3 -3
- package/src/util/isPromise.ts +6 -6
- package/src/util/isString.ts +3 -3
- package/src/util/isTextInputElement.ts +2 -2
- package/src/util/isTouchDevice.ts +7 -7
- package/src/util/isTouchEvent.ts +64 -64
- package/src/util/isUndefined.ts +3 -3
- package/src/util/isValidIdentifierName.spec.ts +33 -33
- package/src/util/isValidIdentifierName.ts +5 -5
- package/src/util/onIdleCallback.ts +10 -10
- package/src/util/parseStyle.ts +29 -29
- package/src/util/quote.ts +6 -6
- package/src/util/reverseSlice.ts +9 -9
- package/src/util/routeAppend.spec.ts +19 -19
- package/src/util/routeAppend.ts +15 -15
- package/src/util/scrollElementIntoView.ts +40 -40
- package/src/util/scss/add-rules.scss +40 -40
- package/src/util/scss/calc.scss +44 -44
- package/src/util/scss/call-once.scss +12 -12
- package/src/util/scss/clockwise.scss +49 -49
- package/src/util/scss/colors.scss +9 -9
- package/src/util/scss/deep-get.scss +11 -11
- package/src/util/scss/deep-merge.scss +21 -21
- package/src/util/scss/divide.scss +3 -3
- package/src/util/scss/include.scss +48 -48
- package/src/util/scss/index.scss +9 -9
- package/src/util/shallowEquals.ts +43 -43
- package/src/util/test/createTestRenderer.tsx +19 -19
- package/src/util/throttle.ts +21 -21
- package/src/util/validatedDebounce.ts +23 -23
- package/src/variables.scss +217 -217
- package/src/widgets/AccessorBindings.spec.tsx +90 -90
- package/src/widgets/Button.scss +119 -119
- package/src/widgets/Button.tsx +189 -189
- package/src/widgets/Button.variables.scss +117 -117
- package/src/widgets/CxCredit.scss +39 -39
- package/src/widgets/CxCredit.tsx +46 -46
- package/src/widgets/DocumentTitle.ts +95 -95
- package/src/widgets/FlexBox.scss +148 -148
- package/src/widgets/FlexBox.tsx +168 -168
- package/src/widgets/Heading.scss +40 -40
- package/src/widgets/Heading.ts +42 -42
- package/src/widgets/HighlightedSearchText.scss +20 -20
- package/src/widgets/HighlightedSearchText.tsx +54 -54
- package/src/widgets/HtmlElement.spec.helpers.tsx +108 -108
- package/src/widgets/HtmlElement.spec.tsx +89 -89
- package/src/widgets/HtmlElement.tsx +407 -407
- package/src/widgets/Icon.scss +22 -22
- package/src/widgets/Icon.ts +64 -64
- package/src/widgets/List.scss +93 -93
- package/src/widgets/List.tsx +793 -793
- package/src/widgets/ProgressBar.scss +50 -50
- package/src/widgets/ProgressBar.tsx +66 -66
- package/src/widgets/ReactElementWrapper.spec.tsx +452 -452
- package/src/widgets/ReactElementWrapper.tsx +108 -108
- package/src/widgets/Resizer.scss +43 -43
- package/src/widgets/Resizer.tsx +200 -200
- package/src/widgets/Sandbox.ts +103 -103
- package/src/widgets/Section.scss +55 -55
- package/src/widgets/Section.tsx +187 -187
- package/src/widgets/animations.scss +10 -10
- package/src/widgets/autoFocus.ts +9 -9
- package/src/widgets/cx.ts +63 -63
- package/src/widgets/drag-drop/DragClone.scss +35 -35
- package/src/widgets/drag-drop/DragHandle.scss +18 -18
- package/src/widgets/drag-drop/DragHandle.tsx +47 -47
- package/src/widgets/drag-drop/DragSource.scss +26 -26
- package/src/widgets/drag-drop/DragSource.tsx +237 -237
- package/src/widgets/drag-drop/DropZone.scss +76 -76
- package/src/widgets/drag-drop/DropZone.tsx +353 -353
- package/src/widgets/drag-drop/index.scss +3 -3
- package/src/widgets/drag-drop/index.ts +4 -4
- package/src/widgets/drag-drop/ops.tsx +422 -422
- package/src/widgets/drag-drop/variables.scss +14 -14
- package/src/widgets/enableAllInternalDependencies.ts +11 -11
- package/src/widgets/form/Calendar.scss +199 -199
- package/src/widgets/form/Calendar.tsx +760 -760
- package/src/widgets/form/Calendar.variables.scss +63 -63
- package/src/widgets/form/Checkbox.scss +129 -129
- package/src/widgets/form/Checkbox.tsx +287 -287
- package/src/widgets/form/Checkbox.variables.scss +39 -39
- package/src/widgets/form/ColorField.scss +98 -98
- package/src/widgets/form/ColorField.tsx +497 -497
- package/src/widgets/form/ColorPicker.scss +285 -285
- package/src/widgets/form/ColorPicker.tsx +544 -544
- package/src/widgets/form/ColorPicker.variables.scss +22 -22
- package/src/widgets/form/DateField.ts +21 -21
- package/src/widgets/form/DateTimeField.scss +92 -92
- package/src/widgets/form/DateTimeField.tsx +726 -726
- package/src/widgets/form/DateTimePicker.scss +46 -46
- package/src/widgets/form/DateTimePicker.tsx +429 -429
- package/src/widgets/form/Field.scss +164 -164
- package/src/widgets/form/Field.tsx +608 -608
- package/src/widgets/form/FieldGroup.ts +10 -10
- package/src/widgets/form/FieldIcon.ts +61 -61
- package/src/widgets/form/HelpText.scss +24 -24
- package/src/widgets/form/HelpText.ts +15 -15
- package/src/widgets/form/Label.scss +37 -37
- package/src/widgets/form/Label.tsx +117 -117
- package/src/widgets/form/LabeledContainer.ts +77 -77
- package/src/widgets/form/LookupField.scss +221 -221
- package/src/widgets/form/LookupField.spec.tsx +93 -93
- package/src/widgets/form/LookupField.tsx +1421 -1421
- package/src/widgets/form/MonthField.scss +100 -100
- package/src/widgets/form/MonthField.tsx +670 -670
- package/src/widgets/form/MonthPicker.scss +125 -125
- package/src/widgets/form/MonthPicker.tsx +822 -822
- package/src/widgets/form/NumberField.scss +63 -63
- package/src/widgets/form/NumberField.tsx +543 -543
- package/src/widgets/form/Radio.scss +123 -123
- package/src/widgets/form/Radio.tsx +249 -249
- package/src/widgets/form/Radio.variables.scss +45 -45
- package/src/widgets/form/Select.scss +101 -101
- package/src/widgets/form/Select.tsx +327 -327
- package/src/widgets/form/Slider.scss +121 -121
- package/src/widgets/form/Slider.tsx +473 -473
- package/src/widgets/form/Switch.scss +142 -142
- package/src/widgets/form/Switch.tsx +176 -176
- package/src/widgets/form/TextArea.scss +45 -45
- package/src/widgets/form/TextArea.tsx +229 -229
- package/src/widgets/form/TextField.scss +57 -57
- package/src/widgets/form/TextField.tsx +407 -407
- package/src/widgets/form/TimeField.ts +10 -10
- package/src/widgets/form/TimeList.tsx +105 -105
- package/src/widgets/form/UploadButton.scss +49 -49
- package/src/widgets/form/UploadButton.tsx +307 -307
- package/src/widgets/form/ValidationError.scss +22 -22
- package/src/widgets/form/ValidationError.tsx +72 -72
- package/src/widgets/form/ValidationGroup.spec.tsx +147 -147
- package/src/widgets/form/ValidationGroup.ts +141 -141
- package/src/widgets/form/Validator.ts +60 -60
- package/src/widgets/form/Wheel.scss +152 -152
- package/src/widgets/form/Wheel.tsx +314 -314
- package/src/widgets/form/index.scss +24 -24
- package/src/widgets/form/index.ts +28 -28
- package/src/widgets/form/variables.scss +355 -355
- package/src/widgets/grid/Grid.scss +640 -640
- package/src/widgets/grid/Grid.tsx +4243 -4243
- package/src/widgets/grid/GridCell.ts +143 -143
- package/src/widgets/grid/GridCellEditor.tsx +58 -58
- package/src/widgets/grid/GridRow.ts +302 -302
- package/src/widgets/grid/GridRowLine.ts +49 -49
- package/src/widgets/grid/Pagination.scss +115 -115
- package/src/widgets/grid/Pagination.tsx +126 -126
- package/src/widgets/grid/TreeNode.scss +90 -90
- package/src/widgets/grid/TreeNode.tsx +154 -154
- package/src/widgets/grid/createGridCellEditor.tsx +18 -18
- package/src/widgets/grid/index.scss +3 -3
- package/src/widgets/grid/index.ts +16 -16
- package/src/widgets/grid/variables.scss +137 -137
- package/src/widgets/icons/arrow-down.svg +3 -3
- package/src/widgets/icons/arrow-right.svg +2 -2
- package/src/widgets/icons/base.svg +104 -104
- package/src/widgets/icons/calendar-old.svg +169 -169
- package/src/widgets/icons/calendar.svg +187 -187
- package/src/widgets/icons/calendar.tsx +22 -22
- package/src/widgets/icons/check.tsx +14 -14
- package/src/widgets/icons/clear.svg +74 -74
- package/src/widgets/icons/clear.tsx +16 -16
- package/src/widgets/icons/close.svg +74 -74
- package/src/widgets/icons/close.tsx +20 -20
- package/src/widgets/icons/cx.tsx +39 -39
- package/src/widgets/icons/drop-down.tsx +16 -16
- package/src/widgets/icons/dropdown-arrow.svg +61 -61
- package/src/widgets/icons/file.svg +4 -4
- package/src/widgets/icons/file.tsx +14 -14
- package/src/widgets/icons/folder-open.svg +5 -5
- package/src/widgets/icons/folder-open.tsx +16 -16
- package/src/widgets/icons/folder.svg +58 -58
- package/src/widgets/icons/folder.tsx +14 -14
- package/src/widgets/icons/forward.svg +67 -67
- package/src/widgets/icons/forward.tsx +23 -23
- package/src/widgets/icons/index.ts +14 -14
- package/src/widgets/icons/loading.svg +4 -4
- package/src/widgets/icons/loading.tsx +25 -25
- package/src/widgets/icons/menu.tsx +18 -18
- package/src/widgets/icons/pixel-picker.tsx +18 -18
- package/src/widgets/icons/registry.ts +56 -56
- package/src/widgets/icons/search.svg +107 -107
- package/src/widgets/icons/search.tsx +14 -14
- package/src/widgets/icons/sort-asc.svg +3 -3
- package/src/widgets/icons/sort-asc.tsx +15 -15
- package/src/widgets/icons/square.tsx +19 -19
- package/src/widgets/index.d.ts +55 -55
- package/src/widgets/index.scss +16 -16
- package/src/widgets/index.ts +58 -58
- package/src/widgets/nav/Link.scss +20 -20
- package/src/widgets/nav/Link.ts +11 -11
- package/src/widgets/nav/LinkButton.ts +176 -176
- package/src/widgets/nav/Menu.scss +76 -76
- package/src/widgets/nav/Menu.tsx +489 -489
- package/src/widgets/nav/Menu.variables.scss +25 -25
- package/src/widgets/nav/MenuItem.scss +130 -130
- package/src/widgets/nav/MenuItem.tsx +525 -525
- package/src/widgets/nav/MenuSpacer.ts +18 -18
- package/src/widgets/nav/RedirectRoute.ts +49 -49
- package/src/widgets/nav/Route.spec.tsx +24 -24
- package/src/widgets/nav/Route.ts +142 -142
- package/src/widgets/nav/Scroller.scss +148 -148
- package/src/widgets/nav/Scroller.tsx +252 -252
- package/src/widgets/nav/Submenu.ts +6 -6
- package/src/widgets/nav/Tab.scss +82 -82
- package/src/widgets/nav/Tab.ts +120 -120
- package/src/widgets/nav/Tab.variables.scss +84 -84
- package/src/widgets/nav/cover.scss +22 -22
- package/src/widgets/nav/index.scss +5 -5
- package/src/widgets/nav/index.ts +10 -10
- package/src/widgets/nav/variables.scss +27 -27
- package/src/widgets/overlay/ContextMenu.ts +42 -42
- package/src/widgets/overlay/Dropdown.scss +186 -186
- package/src/widgets/overlay/Dropdown.tsx +762 -762
- package/src/widgets/overlay/FlyweightTooltipTracker.ts +57 -57
- package/src/widgets/overlay/MsgBox.tsx +141 -141
- package/src/widgets/overlay/Overlay.scss +68 -68
- package/src/widgets/overlay/Overlay.tsx +947 -947
- package/src/widgets/overlay/Toast.scss +163 -163
- package/src/widgets/overlay/Toast.ts +111 -111
- package/src/widgets/overlay/Tooltip.scss +177 -177
- package/src/widgets/overlay/Tooltip.tsx +393 -393
- package/src/widgets/overlay/Window.scss +129 -129
- package/src/widgets/overlay/Window.tsx +299 -299
- package/src/widgets/overlay/Window.variables.scss +62 -62
- package/src/widgets/overlay/alerts.ts +46 -46
- package/src/widgets/overlay/captureMouse.scss +13 -13
- package/src/widgets/overlay/captureMouse.ts +195 -195
- package/src/widgets/overlay/createHotPromiseWindowFactory.ts +71 -71
- package/src/widgets/overlay/index.d.ts +11 -11
- package/src/widgets/overlay/index.scss +15 -15
- package/src/widgets/overlay/index.ts +11 -11
- package/src/widgets/overlay/tooltip-ops.ts +173 -173
- package/src/widgets/overlay/variables.scss +85 -85
- package/src/widgets/variables.scss +146 -146
- package/tsconfig.compile.json +4 -4
- package/tsconfig.json +34 -34
- package/tsconfig.mocha.json +14 -14
|
@@ -1,585 +1,585 @@
|
|
|
1
|
-
import { ContentResolver } from "./ContentResolver";
|
|
2
|
-
import { Store } from "../data/Store";
|
|
3
|
-
import { createTestRenderer, act } from "../util/test/createTestRenderer";
|
|
4
|
-
import assert from "assert";
|
|
5
|
-
import { bind } from "./bind";
|
|
6
|
-
import { createAccessorModelProxy } from "../data/createAccessorModelProxy";
|
|
7
|
-
import { Prop } from "./Prop";
|
|
8
|
-
import { stringTemplate } from "../data/StringTemplate";
|
|
9
|
-
|
|
10
|
-
interface TestModel {
|
|
11
|
-
user: {
|
|
12
|
-
name: string;
|
|
13
|
-
age: number;
|
|
14
|
-
active: boolean;
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
describe("ContentResolver", () => {
|
|
19
|
-
it("resolves content based on params", async () => {
|
|
20
|
-
let widget = (
|
|
21
|
-
<cx>
|
|
22
|
-
<div>
|
|
23
|
-
<ContentResolver params={{ name: bind("name") }} onResolve={(params) => <span text={params.name} />} />
|
|
24
|
-
</div>
|
|
25
|
-
</cx>
|
|
26
|
-
);
|
|
27
|
-
|
|
28
|
-
let store = new Store({
|
|
29
|
-
data: {
|
|
30
|
-
name: "Test",
|
|
31
|
-
},
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
const component = await createTestRenderer(store, widget);
|
|
35
|
-
|
|
36
|
-
let tree = component.toJSON();
|
|
37
|
-
assert.deepEqual(tree, {
|
|
38
|
-
type: "div",
|
|
39
|
-
props: {},
|
|
40
|
-
children: [
|
|
41
|
-
{
|
|
42
|
-
type: "span",
|
|
43
|
-
props: {},
|
|
44
|
-
children: ["Test"],
|
|
45
|
-
},
|
|
46
|
-
],
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it("re-resolves when params change", async () => {
|
|
51
|
-
let resolveCount = 0;
|
|
52
|
-
|
|
53
|
-
let widget = (
|
|
54
|
-
<cx>
|
|
55
|
-
<div>
|
|
56
|
-
<ContentResolver
|
|
57
|
-
params={{ value: bind("value") }}
|
|
58
|
-
onResolve={(params) => {
|
|
59
|
-
resolveCount++;
|
|
60
|
-
return <span text={String(params.value)} />;
|
|
61
|
-
}}
|
|
62
|
-
/>
|
|
63
|
-
</div>
|
|
64
|
-
</cx>
|
|
65
|
-
);
|
|
66
|
-
|
|
67
|
-
let store = new Store({
|
|
68
|
-
data: {
|
|
69
|
-
value: 1,
|
|
70
|
-
},
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
const component = await createTestRenderer(store, widget);
|
|
74
|
-
|
|
75
|
-
let tree = component.toJSON();
|
|
76
|
-
assert.deepEqual(tree, {
|
|
77
|
-
type: "div",
|
|
78
|
-
props: {},
|
|
79
|
-
children: [
|
|
80
|
-
{
|
|
81
|
-
type: "span",
|
|
82
|
-
props: {},
|
|
83
|
-
children: ["1"],
|
|
84
|
-
},
|
|
85
|
-
],
|
|
86
|
-
});
|
|
87
|
-
assert.equal(resolveCount, 1);
|
|
88
|
-
|
|
89
|
-
await act(async () => {
|
|
90
|
-
store.set("value", 2);
|
|
91
|
-
});
|
|
92
|
-
tree = component.toJSON();
|
|
93
|
-
assert.deepEqual(tree, {
|
|
94
|
-
type: "div",
|
|
95
|
-
props: {},
|
|
96
|
-
children: [
|
|
97
|
-
{
|
|
98
|
-
type: "span",
|
|
99
|
-
props: {},
|
|
100
|
-
children: ["2"],
|
|
101
|
-
},
|
|
102
|
-
],
|
|
103
|
-
});
|
|
104
|
-
assert.equal(resolveCount, 2);
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it("does not re-resolve if params are unchanged", async () => {
|
|
108
|
-
let resolveCount = 0;
|
|
109
|
-
|
|
110
|
-
let widget = (
|
|
111
|
-
<cx>
|
|
112
|
-
<div>
|
|
113
|
-
<ContentResolver
|
|
114
|
-
params={{ value: bind("value") }}
|
|
115
|
-
onResolve={(params) => {
|
|
116
|
-
resolveCount++;
|
|
117
|
-
return <span text={String(params.value)} />;
|
|
118
|
-
}}
|
|
119
|
-
/>
|
|
120
|
-
</div>
|
|
121
|
-
</cx>
|
|
122
|
-
);
|
|
123
|
-
|
|
124
|
-
let store = new Store({
|
|
125
|
-
data: {
|
|
126
|
-
value: 1,
|
|
127
|
-
other: "a",
|
|
128
|
-
},
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
const component = await createTestRenderer(store, widget);
|
|
132
|
-
assert.equal(resolveCount, 1);
|
|
133
|
-
|
|
134
|
-
// Change unrelated data
|
|
135
|
-
store.set("other", "b");
|
|
136
|
-
component.toJSON();
|
|
137
|
-
assert.equal(resolveCount, 1);
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
it("supports literal values in params", async () => {
|
|
141
|
-
let receivedParams: any = null;
|
|
142
|
-
|
|
143
|
-
let widget = (
|
|
144
|
-
<cx>
|
|
145
|
-
<div>
|
|
146
|
-
<ContentResolver
|
|
147
|
-
params={{ count: 42, name: "test" }}
|
|
148
|
-
onResolve={(params) => {
|
|
149
|
-
receivedParams = params;
|
|
150
|
-
return <span text={`${params.name}: ${params.count}`} />;
|
|
151
|
-
}}
|
|
152
|
-
/>
|
|
153
|
-
</div>
|
|
154
|
-
</cx>
|
|
155
|
-
);
|
|
156
|
-
|
|
157
|
-
let store = new Store();
|
|
158
|
-
|
|
159
|
-
const component = await createTestRenderer(store, widget);
|
|
160
|
-
|
|
161
|
-
let tree = component.toJSON();
|
|
162
|
-
assert.deepEqual(tree, {
|
|
163
|
-
type: "div",
|
|
164
|
-
props: {},
|
|
165
|
-
children: [
|
|
166
|
-
{
|
|
167
|
-
type: "span",
|
|
168
|
-
props: {},
|
|
169
|
-
children: ["test: 42"],
|
|
170
|
-
},
|
|
171
|
-
],
|
|
172
|
-
});
|
|
173
|
-
assert.equal(receivedParams.count, 42);
|
|
174
|
-
assert.equal(receivedParams.name, "test");
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
it("supports mode=prepend", async () => {
|
|
178
|
-
let widget = (
|
|
179
|
-
<cx>
|
|
180
|
-
<div>
|
|
181
|
-
<ContentResolver mode="prepend" params={{ show: true }} onResolve={() => <span>Prepended</span>}>
|
|
182
|
-
<span>Original</span>
|
|
183
|
-
</ContentResolver>
|
|
184
|
-
</div>
|
|
185
|
-
</cx>
|
|
186
|
-
);
|
|
187
|
-
|
|
188
|
-
let store = new Store();
|
|
189
|
-
|
|
190
|
-
const component = await createTestRenderer(store, widget);
|
|
191
|
-
|
|
192
|
-
let tree = component.toJSON();
|
|
193
|
-
assert.deepEqual(tree, {
|
|
194
|
-
type: "div",
|
|
195
|
-
props: {},
|
|
196
|
-
children: [
|
|
197
|
-
{
|
|
198
|
-
type: "span",
|
|
199
|
-
props: {},
|
|
200
|
-
children: ["Prepended"],
|
|
201
|
-
},
|
|
202
|
-
{
|
|
203
|
-
type: "span",
|
|
204
|
-
props: {},
|
|
205
|
-
children: ["Original"],
|
|
206
|
-
},
|
|
207
|
-
],
|
|
208
|
-
});
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
it("supports mode=append", async () => {
|
|
212
|
-
let widget = (
|
|
213
|
-
<cx>
|
|
214
|
-
<div>
|
|
215
|
-
<ContentResolver mode="append" params={{ show: true }} onResolve={() => <span>Appended</span>}>
|
|
216
|
-
<span>Original</span>
|
|
217
|
-
</ContentResolver>
|
|
218
|
-
</div>
|
|
219
|
-
</cx>
|
|
220
|
-
);
|
|
221
|
-
|
|
222
|
-
let store = new Store();
|
|
223
|
-
|
|
224
|
-
const component = await createTestRenderer(store, widget);
|
|
225
|
-
|
|
226
|
-
let tree = component.toJSON();
|
|
227
|
-
assert.deepEqual(tree, {
|
|
228
|
-
type: "div",
|
|
229
|
-
props: {},
|
|
230
|
-
children: [
|
|
231
|
-
{
|
|
232
|
-
type: "span",
|
|
233
|
-
props: {},
|
|
234
|
-
children: ["Original"],
|
|
235
|
-
},
|
|
236
|
-
{
|
|
237
|
-
type: "span",
|
|
238
|
-
props: {},
|
|
239
|
-
children: ["Appended"],
|
|
240
|
-
},
|
|
241
|
-
],
|
|
242
|
-
});
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
it("supports mode=replace (default)", async () => {
|
|
246
|
-
let widget = (
|
|
247
|
-
<cx>
|
|
248
|
-
<div>
|
|
249
|
-
<ContentResolver params={{ show: true }} onResolve={() => <span>Replaced</span>}>
|
|
250
|
-
<span>Original</span>
|
|
251
|
-
</ContentResolver>
|
|
252
|
-
</div>
|
|
253
|
-
</cx>
|
|
254
|
-
);
|
|
255
|
-
|
|
256
|
-
let store = new Store();
|
|
257
|
-
|
|
258
|
-
const component = await createTestRenderer(store, widget);
|
|
259
|
-
|
|
260
|
-
let tree = component.toJSON();
|
|
261
|
-
assert.deepEqual(tree, {
|
|
262
|
-
type: "div",
|
|
263
|
-
props: {},
|
|
264
|
-
children: [
|
|
265
|
-
{
|
|
266
|
-
type: "span",
|
|
267
|
-
props: {},
|
|
268
|
-
children: ["Replaced"],
|
|
269
|
-
},
|
|
270
|
-
],
|
|
271
|
-
});
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
it("handles async resolution with Promise", async () => {
|
|
275
|
-
let resolvePromise: ((value: any) => void) | null = null;
|
|
276
|
-
|
|
277
|
-
let widget = (
|
|
278
|
-
<cx>
|
|
279
|
-
<div>
|
|
280
|
-
<ContentResolver
|
|
281
|
-
params={{ id: bind("id") }}
|
|
282
|
-
loading={bind("loading")}
|
|
283
|
-
onResolve={(params) => {
|
|
284
|
-
return new Promise((resolve) => {
|
|
285
|
-
resolvePromise = resolve;
|
|
286
|
-
});
|
|
287
|
-
}}
|
|
288
|
-
/>
|
|
289
|
-
</div>
|
|
290
|
-
</cx>
|
|
291
|
-
);
|
|
292
|
-
|
|
293
|
-
let store = new Store({
|
|
294
|
-
data: {
|
|
295
|
-
id: 1,
|
|
296
|
-
loading: false,
|
|
297
|
-
},
|
|
298
|
-
});
|
|
299
|
-
|
|
300
|
-
const component = await createTestRenderer(store, widget);
|
|
301
|
-
|
|
302
|
-
// Initially loading should be true
|
|
303
|
-
assert.equal(store.get("loading"), true);
|
|
304
|
-
|
|
305
|
-
// Resolve the promise
|
|
306
|
-
resolvePromise!(<span>Loaded Content</span>);
|
|
307
|
-
|
|
308
|
-
// Wait for promise resolution
|
|
309
|
-
await new Promise((r) => setTimeout(r, 10));
|
|
310
|
-
|
|
311
|
-
// After resolution, loading should be false
|
|
312
|
-
assert.equal(store.get("loading"), false);
|
|
313
|
-
|
|
314
|
-
let tree = component.toJSON();
|
|
315
|
-
assert.deepEqual(tree, {
|
|
316
|
-
type: "div",
|
|
317
|
-
props: {},
|
|
318
|
-
children: [
|
|
319
|
-
{
|
|
320
|
-
type: "span",
|
|
321
|
-
props: {},
|
|
322
|
-
children: ["Loaded Content"],
|
|
323
|
-
},
|
|
324
|
-
],
|
|
325
|
-
});
|
|
326
|
-
});
|
|
327
|
-
|
|
328
|
-
it("supports typed params with accessor chains", async () => {
|
|
329
|
-
const model = createAccessorModelProxy<TestModel>();
|
|
330
|
-
|
|
331
|
-
// This test verifies type inference works correctly.
|
|
332
|
-
// AccessorChain<T> resolves to T in the onResolve params.
|
|
333
|
-
let widget = (
|
|
334
|
-
<cx>
|
|
335
|
-
<div>
|
|
336
|
-
<ContentResolver
|
|
337
|
-
params={{
|
|
338
|
-
name: model.user.name,
|
|
339
|
-
age: model.user.age,
|
|
340
|
-
active: model.user.active,
|
|
341
|
-
}}
|
|
342
|
-
onResolve={(params) => {
|
|
343
|
-
// TypeScript infers types from AccessorChain<T>:
|
|
344
|
-
// params.name is string (from AccessorChain<string>)
|
|
345
|
-
// params.age is number (from AccessorChain<number>)
|
|
346
|
-
// params.active is boolean (from AccessorChain<boolean>)
|
|
347
|
-
const name: string = params.name;
|
|
348
|
-
const age: number = params.age;
|
|
349
|
-
const active: boolean = params.active;
|
|
350
|
-
return <span text={`${name} is ${age} years old and ${active ? "active" : "inactive"}`} />;
|
|
351
|
-
}}
|
|
352
|
-
/>
|
|
353
|
-
</div>
|
|
354
|
-
</cx>
|
|
355
|
-
);
|
|
356
|
-
|
|
357
|
-
let store = new Store({
|
|
358
|
-
data: {
|
|
359
|
-
user: {
|
|
360
|
-
name: "John",
|
|
361
|
-
age: 30,
|
|
362
|
-
active: true,
|
|
363
|
-
},
|
|
364
|
-
},
|
|
365
|
-
});
|
|
366
|
-
|
|
367
|
-
const component = await createTestRenderer(store, widget);
|
|
368
|
-
|
|
369
|
-
let tree = component.toJSON();
|
|
370
|
-
assert.deepEqual(tree, {
|
|
371
|
-
type: "div",
|
|
372
|
-
props: {},
|
|
373
|
-
children: [
|
|
374
|
-
{
|
|
375
|
-
type: "span",
|
|
376
|
-
props: {},
|
|
377
|
-
children: ["John is 30 years old and active"],
|
|
378
|
-
},
|
|
379
|
-
],
|
|
380
|
-
});
|
|
381
|
-
});
|
|
382
|
-
|
|
383
|
-
it("infers types from literal params", async () => {
|
|
384
|
-
// When using literal values, TypeScript can infer their types
|
|
385
|
-
let widget = (
|
|
386
|
-
<cx>
|
|
387
|
-
<div>
|
|
388
|
-
<ContentResolver
|
|
389
|
-
params={{ count: 42, label: "items" }}
|
|
390
|
-
onResolve={(params) => {
|
|
391
|
-
// params.count is inferred as number
|
|
392
|
-
// params.label is inferred as string
|
|
393
|
-
const count: number = params.count;
|
|
394
|
-
const label: string = params.label;
|
|
395
|
-
return <span text={`${count} ${label}`} />;
|
|
396
|
-
}}
|
|
397
|
-
/>
|
|
398
|
-
</div>
|
|
399
|
-
</cx>
|
|
400
|
-
);
|
|
401
|
-
|
|
402
|
-
let store = new Store();
|
|
403
|
-
|
|
404
|
-
const component = await createTestRenderer(store, widget);
|
|
405
|
-
|
|
406
|
-
let tree = component.toJSON();
|
|
407
|
-
assert.deepEqual(tree, {
|
|
408
|
-
type: "div",
|
|
409
|
-
props: {},
|
|
410
|
-
children: [
|
|
411
|
-
{
|
|
412
|
-
type: "span",
|
|
413
|
-
props: {},
|
|
414
|
-
children: ["42 items"],
|
|
415
|
-
},
|
|
416
|
-
],
|
|
417
|
-
});
|
|
418
|
-
});
|
|
419
|
-
|
|
420
|
-
it("supports simple Prop params (non-structured)", async () => {
|
|
421
|
-
const model = createAccessorModelProxy<TestModel>();
|
|
422
|
-
|
|
423
|
-
// Test using a single AccessorChain as params instead of an object
|
|
424
|
-
let widget = (
|
|
425
|
-
<cx>
|
|
426
|
-
<div>
|
|
427
|
-
<ContentResolver
|
|
428
|
-
params={model.user.name}
|
|
429
|
-
onResolve={(name) => {
|
|
430
|
-
// name should be typed as string (from AccessorChain<string>)
|
|
431
|
-
const typedName: string = name;
|
|
432
|
-
return <span text={`Hello, ${typedName}!`} />;
|
|
433
|
-
}}
|
|
434
|
-
/>
|
|
435
|
-
</div>
|
|
436
|
-
</cx>
|
|
437
|
-
);
|
|
438
|
-
|
|
439
|
-
let store = new Store({
|
|
440
|
-
data: {
|
|
441
|
-
user: {
|
|
442
|
-
name: "Alice",
|
|
443
|
-
age: 25,
|
|
444
|
-
active: true,
|
|
445
|
-
},
|
|
446
|
-
},
|
|
447
|
-
});
|
|
448
|
-
|
|
449
|
-
const component = await createTestRenderer(store, widget);
|
|
450
|
-
|
|
451
|
-
let tree = component.toJSON();
|
|
452
|
-
assert.deepEqual(tree, {
|
|
453
|
-
type: "div",
|
|
454
|
-
props: {},
|
|
455
|
-
children: [
|
|
456
|
-
{
|
|
457
|
-
type: "span",
|
|
458
|
-
props: {},
|
|
459
|
-
children: ["Hello, Alice!"],
|
|
460
|
-
},
|
|
461
|
-
],
|
|
462
|
-
});
|
|
463
|
-
});
|
|
464
|
-
|
|
465
|
-
it("supports simple bind() as params", async () => {
|
|
466
|
-
let widget = (
|
|
467
|
-
<cx>
|
|
468
|
-
<div>
|
|
469
|
-
<ContentResolver
|
|
470
|
-
params={bind("count")}
|
|
471
|
-
onResolve={(count) => {
|
|
472
|
-
return <span text={`Count: ${count}`} />;
|
|
473
|
-
}}
|
|
474
|
-
/>
|
|
475
|
-
</div>
|
|
476
|
-
</cx>
|
|
477
|
-
);
|
|
478
|
-
|
|
479
|
-
let store = new Store({
|
|
480
|
-
data: {
|
|
481
|
-
count: 42,
|
|
482
|
-
},
|
|
483
|
-
});
|
|
484
|
-
|
|
485
|
-
const component = await createTestRenderer(store, widget);
|
|
486
|
-
|
|
487
|
-
let tree = component.toJSON();
|
|
488
|
-
assert.deepEqual(tree, {
|
|
489
|
-
type: "div",
|
|
490
|
-
props: {},
|
|
491
|
-
children: [
|
|
492
|
-
{
|
|
493
|
-
type: "span",
|
|
494
|
-
props: {},
|
|
495
|
-
children: ["Count: 42"],
|
|
496
|
-
},
|
|
497
|
-
],
|
|
498
|
-
});
|
|
499
|
-
});
|
|
500
|
-
|
|
501
|
-
it("resolves Selector<string> | null to string | null", async () => {
|
|
502
|
-
const text: string | null = "Hello {user.name}";
|
|
503
|
-
|
|
504
|
-
let widget = (
|
|
505
|
-
<cx>
|
|
506
|
-
<div>
|
|
507
|
-
<ContentResolver
|
|
508
|
-
params={{
|
|
509
|
-
text: text ? stringTemplate(text) : null,
|
|
510
|
-
}}
|
|
511
|
-
onResolve={(params) => {
|
|
512
|
-
// params.text should be string | null
|
|
513
|
-
const typedText: string | null = params.text;
|
|
514
|
-
// @ts-expect-error - params.text should not be 'any' or broader type
|
|
515
|
-
const shouldFail: number = params.text;
|
|
516
|
-
return <span text={typedText ?? "no text"} />;
|
|
517
|
-
}}
|
|
518
|
-
/>
|
|
519
|
-
</div>
|
|
520
|
-
</cx>
|
|
521
|
-
);
|
|
522
|
-
|
|
523
|
-
let store = new Store({
|
|
524
|
-
data: {
|
|
525
|
-
user: { name: "World", age: 30, active: true },
|
|
526
|
-
},
|
|
527
|
-
});
|
|
528
|
-
|
|
529
|
-
const component = await createTestRenderer(store, widget);
|
|
530
|
-
let tree = component.toJSON();
|
|
531
|
-
assert.deepEqual(tree, {
|
|
532
|
-
type: "div",
|
|
533
|
-
props: {},
|
|
534
|
-
children: [{ type: "span", props: {}, children: ["Hello World"] }],
|
|
535
|
-
});
|
|
536
|
-
});
|
|
537
|
-
|
|
538
|
-
it("resolves Prop<string> to string (not any)", async () => {
|
|
539
|
-
const model = createAccessorModelProxy<TestModel>();
|
|
540
|
-
|
|
541
|
-
// When params is typed as Prop<string>, onResolve should receive string (not any)
|
|
542
|
-
// This tests that the union type Prop<T> correctly extracts T
|
|
543
|
-
const typedParam: Prop<string> = model.user.name;
|
|
544
|
-
|
|
545
|
-
let widget = (
|
|
546
|
-
<cx>
|
|
547
|
-
<div>
|
|
548
|
-
<ContentResolver
|
|
549
|
-
params={typedParam}
|
|
550
|
-
onResolve={(name) => {
|
|
551
|
-
// name should be typed as string, not any
|
|
552
|
-
const typedName: string = name;
|
|
553
|
-
return <span text={`Hello, ${typedName}!`} />;
|
|
554
|
-
}}
|
|
555
|
-
/>
|
|
556
|
-
</div>
|
|
557
|
-
</cx>
|
|
558
|
-
);
|
|
559
|
-
|
|
560
|
-
let store = new Store({
|
|
561
|
-
data: {
|
|
562
|
-
user: {
|
|
563
|
-
name: "Bob",
|
|
564
|
-
age: 35,
|
|
565
|
-
active: false,
|
|
566
|
-
},
|
|
567
|
-
},
|
|
568
|
-
});
|
|
569
|
-
|
|
570
|
-
const component = await createTestRenderer(store, widget);
|
|
571
|
-
|
|
572
|
-
let tree = component.toJSON();
|
|
573
|
-
assert.deepEqual(tree, {
|
|
574
|
-
type: "div",
|
|
575
|
-
props: {},
|
|
576
|
-
children: [
|
|
577
|
-
{
|
|
578
|
-
type: "span",
|
|
579
|
-
props: {},
|
|
580
|
-
children: ["Hello, Bob!"],
|
|
581
|
-
},
|
|
582
|
-
],
|
|
583
|
-
});
|
|
584
|
-
});
|
|
585
|
-
});
|
|
1
|
+
import { ContentResolver } from "./ContentResolver";
|
|
2
|
+
import { Store } from "../data/Store";
|
|
3
|
+
import { createTestRenderer, act } from "../util/test/createTestRenderer";
|
|
4
|
+
import assert from "assert";
|
|
5
|
+
import { bind } from "./bind";
|
|
6
|
+
import { createAccessorModelProxy } from "../data/createAccessorModelProxy";
|
|
7
|
+
import { Prop } from "./Prop";
|
|
8
|
+
import { stringTemplate } from "../data/StringTemplate";
|
|
9
|
+
|
|
10
|
+
interface TestModel {
|
|
11
|
+
user: {
|
|
12
|
+
name: string;
|
|
13
|
+
age: number;
|
|
14
|
+
active: boolean;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
describe("ContentResolver", () => {
|
|
19
|
+
it("resolves content based on params", async () => {
|
|
20
|
+
let widget = (
|
|
21
|
+
<cx>
|
|
22
|
+
<div>
|
|
23
|
+
<ContentResolver params={{ name: bind("name") }} onResolve={(params) => <span text={params.name} />} />
|
|
24
|
+
</div>
|
|
25
|
+
</cx>
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
let store = new Store({
|
|
29
|
+
data: {
|
|
30
|
+
name: "Test",
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const component = await createTestRenderer(store, widget);
|
|
35
|
+
|
|
36
|
+
let tree = component.toJSON();
|
|
37
|
+
assert.deepEqual(tree, {
|
|
38
|
+
type: "div",
|
|
39
|
+
props: {},
|
|
40
|
+
children: [
|
|
41
|
+
{
|
|
42
|
+
type: "span",
|
|
43
|
+
props: {},
|
|
44
|
+
children: ["Test"],
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("re-resolves when params change", async () => {
|
|
51
|
+
let resolveCount = 0;
|
|
52
|
+
|
|
53
|
+
let widget = (
|
|
54
|
+
<cx>
|
|
55
|
+
<div>
|
|
56
|
+
<ContentResolver
|
|
57
|
+
params={{ value: bind("value") }}
|
|
58
|
+
onResolve={(params) => {
|
|
59
|
+
resolveCount++;
|
|
60
|
+
return <span text={String(params.value)} />;
|
|
61
|
+
}}
|
|
62
|
+
/>
|
|
63
|
+
</div>
|
|
64
|
+
</cx>
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
let store = new Store({
|
|
68
|
+
data: {
|
|
69
|
+
value: 1,
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const component = await createTestRenderer(store, widget);
|
|
74
|
+
|
|
75
|
+
let tree = component.toJSON();
|
|
76
|
+
assert.deepEqual(tree, {
|
|
77
|
+
type: "div",
|
|
78
|
+
props: {},
|
|
79
|
+
children: [
|
|
80
|
+
{
|
|
81
|
+
type: "span",
|
|
82
|
+
props: {},
|
|
83
|
+
children: ["1"],
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
});
|
|
87
|
+
assert.equal(resolveCount, 1);
|
|
88
|
+
|
|
89
|
+
await act(async () => {
|
|
90
|
+
store.set("value", 2);
|
|
91
|
+
});
|
|
92
|
+
tree = component.toJSON();
|
|
93
|
+
assert.deepEqual(tree, {
|
|
94
|
+
type: "div",
|
|
95
|
+
props: {},
|
|
96
|
+
children: [
|
|
97
|
+
{
|
|
98
|
+
type: "span",
|
|
99
|
+
props: {},
|
|
100
|
+
children: ["2"],
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
});
|
|
104
|
+
assert.equal(resolveCount, 2);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("does not re-resolve if params are unchanged", async () => {
|
|
108
|
+
let resolveCount = 0;
|
|
109
|
+
|
|
110
|
+
let widget = (
|
|
111
|
+
<cx>
|
|
112
|
+
<div>
|
|
113
|
+
<ContentResolver
|
|
114
|
+
params={{ value: bind("value") }}
|
|
115
|
+
onResolve={(params) => {
|
|
116
|
+
resolveCount++;
|
|
117
|
+
return <span text={String(params.value)} />;
|
|
118
|
+
}}
|
|
119
|
+
/>
|
|
120
|
+
</div>
|
|
121
|
+
</cx>
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
let store = new Store({
|
|
125
|
+
data: {
|
|
126
|
+
value: 1,
|
|
127
|
+
other: "a",
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
const component = await createTestRenderer(store, widget);
|
|
132
|
+
assert.equal(resolveCount, 1);
|
|
133
|
+
|
|
134
|
+
// Change unrelated data
|
|
135
|
+
store.set("other", "b");
|
|
136
|
+
component.toJSON();
|
|
137
|
+
assert.equal(resolveCount, 1);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("supports literal values in params", async () => {
|
|
141
|
+
let receivedParams: any = null;
|
|
142
|
+
|
|
143
|
+
let widget = (
|
|
144
|
+
<cx>
|
|
145
|
+
<div>
|
|
146
|
+
<ContentResolver
|
|
147
|
+
params={{ count: 42, name: "test" }}
|
|
148
|
+
onResolve={(params) => {
|
|
149
|
+
receivedParams = params;
|
|
150
|
+
return <span text={`${params.name}: ${params.count}`} />;
|
|
151
|
+
}}
|
|
152
|
+
/>
|
|
153
|
+
</div>
|
|
154
|
+
</cx>
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
let store = new Store();
|
|
158
|
+
|
|
159
|
+
const component = await createTestRenderer(store, widget);
|
|
160
|
+
|
|
161
|
+
let tree = component.toJSON();
|
|
162
|
+
assert.deepEqual(tree, {
|
|
163
|
+
type: "div",
|
|
164
|
+
props: {},
|
|
165
|
+
children: [
|
|
166
|
+
{
|
|
167
|
+
type: "span",
|
|
168
|
+
props: {},
|
|
169
|
+
children: ["test: 42"],
|
|
170
|
+
},
|
|
171
|
+
],
|
|
172
|
+
});
|
|
173
|
+
assert.equal(receivedParams.count, 42);
|
|
174
|
+
assert.equal(receivedParams.name, "test");
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it("supports mode=prepend", async () => {
|
|
178
|
+
let widget = (
|
|
179
|
+
<cx>
|
|
180
|
+
<div>
|
|
181
|
+
<ContentResolver mode="prepend" params={{ show: true }} onResolve={() => <span>Prepended</span>}>
|
|
182
|
+
<span>Original</span>
|
|
183
|
+
</ContentResolver>
|
|
184
|
+
</div>
|
|
185
|
+
</cx>
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
let store = new Store();
|
|
189
|
+
|
|
190
|
+
const component = await createTestRenderer(store, widget);
|
|
191
|
+
|
|
192
|
+
let tree = component.toJSON();
|
|
193
|
+
assert.deepEqual(tree, {
|
|
194
|
+
type: "div",
|
|
195
|
+
props: {},
|
|
196
|
+
children: [
|
|
197
|
+
{
|
|
198
|
+
type: "span",
|
|
199
|
+
props: {},
|
|
200
|
+
children: ["Prepended"],
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
type: "span",
|
|
204
|
+
props: {},
|
|
205
|
+
children: ["Original"],
|
|
206
|
+
},
|
|
207
|
+
],
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it("supports mode=append", async () => {
|
|
212
|
+
let widget = (
|
|
213
|
+
<cx>
|
|
214
|
+
<div>
|
|
215
|
+
<ContentResolver mode="append" params={{ show: true }} onResolve={() => <span>Appended</span>}>
|
|
216
|
+
<span>Original</span>
|
|
217
|
+
</ContentResolver>
|
|
218
|
+
</div>
|
|
219
|
+
</cx>
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
let store = new Store();
|
|
223
|
+
|
|
224
|
+
const component = await createTestRenderer(store, widget);
|
|
225
|
+
|
|
226
|
+
let tree = component.toJSON();
|
|
227
|
+
assert.deepEqual(tree, {
|
|
228
|
+
type: "div",
|
|
229
|
+
props: {},
|
|
230
|
+
children: [
|
|
231
|
+
{
|
|
232
|
+
type: "span",
|
|
233
|
+
props: {},
|
|
234
|
+
children: ["Original"],
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
type: "span",
|
|
238
|
+
props: {},
|
|
239
|
+
children: ["Appended"],
|
|
240
|
+
},
|
|
241
|
+
],
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it("supports mode=replace (default)", async () => {
|
|
246
|
+
let widget = (
|
|
247
|
+
<cx>
|
|
248
|
+
<div>
|
|
249
|
+
<ContentResolver params={{ show: true }} onResolve={() => <span>Replaced</span>}>
|
|
250
|
+
<span>Original</span>
|
|
251
|
+
</ContentResolver>
|
|
252
|
+
</div>
|
|
253
|
+
</cx>
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
let store = new Store();
|
|
257
|
+
|
|
258
|
+
const component = await createTestRenderer(store, widget);
|
|
259
|
+
|
|
260
|
+
let tree = component.toJSON();
|
|
261
|
+
assert.deepEqual(tree, {
|
|
262
|
+
type: "div",
|
|
263
|
+
props: {},
|
|
264
|
+
children: [
|
|
265
|
+
{
|
|
266
|
+
type: "span",
|
|
267
|
+
props: {},
|
|
268
|
+
children: ["Replaced"],
|
|
269
|
+
},
|
|
270
|
+
],
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it("handles async resolution with Promise", async () => {
|
|
275
|
+
let resolvePromise: ((value: any) => void) | null = null;
|
|
276
|
+
|
|
277
|
+
let widget = (
|
|
278
|
+
<cx>
|
|
279
|
+
<div>
|
|
280
|
+
<ContentResolver
|
|
281
|
+
params={{ id: bind("id") }}
|
|
282
|
+
loading={bind("loading")}
|
|
283
|
+
onResolve={(params) => {
|
|
284
|
+
return new Promise((resolve) => {
|
|
285
|
+
resolvePromise = resolve;
|
|
286
|
+
});
|
|
287
|
+
}}
|
|
288
|
+
/>
|
|
289
|
+
</div>
|
|
290
|
+
</cx>
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
let store = new Store({
|
|
294
|
+
data: {
|
|
295
|
+
id: 1,
|
|
296
|
+
loading: false,
|
|
297
|
+
},
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
const component = await createTestRenderer(store, widget);
|
|
301
|
+
|
|
302
|
+
// Initially loading should be true
|
|
303
|
+
assert.equal(store.get("loading"), true);
|
|
304
|
+
|
|
305
|
+
// Resolve the promise
|
|
306
|
+
resolvePromise!(<span>Loaded Content</span>);
|
|
307
|
+
|
|
308
|
+
// Wait for promise resolution
|
|
309
|
+
await new Promise((r) => setTimeout(r, 10));
|
|
310
|
+
|
|
311
|
+
// After resolution, loading should be false
|
|
312
|
+
assert.equal(store.get("loading"), false);
|
|
313
|
+
|
|
314
|
+
let tree = component.toJSON();
|
|
315
|
+
assert.deepEqual(tree, {
|
|
316
|
+
type: "div",
|
|
317
|
+
props: {},
|
|
318
|
+
children: [
|
|
319
|
+
{
|
|
320
|
+
type: "span",
|
|
321
|
+
props: {},
|
|
322
|
+
children: ["Loaded Content"],
|
|
323
|
+
},
|
|
324
|
+
],
|
|
325
|
+
});
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it("supports typed params with accessor chains", async () => {
|
|
329
|
+
const model = createAccessorModelProxy<TestModel>();
|
|
330
|
+
|
|
331
|
+
// This test verifies type inference works correctly.
|
|
332
|
+
// AccessorChain<T> resolves to T in the onResolve params.
|
|
333
|
+
let widget = (
|
|
334
|
+
<cx>
|
|
335
|
+
<div>
|
|
336
|
+
<ContentResolver
|
|
337
|
+
params={{
|
|
338
|
+
name: model.user.name,
|
|
339
|
+
age: model.user.age,
|
|
340
|
+
active: model.user.active,
|
|
341
|
+
}}
|
|
342
|
+
onResolve={(params) => {
|
|
343
|
+
// TypeScript infers types from AccessorChain<T>:
|
|
344
|
+
// params.name is string (from AccessorChain<string>)
|
|
345
|
+
// params.age is number (from AccessorChain<number>)
|
|
346
|
+
// params.active is boolean (from AccessorChain<boolean>)
|
|
347
|
+
const name: string = params.name;
|
|
348
|
+
const age: number = params.age;
|
|
349
|
+
const active: boolean = params.active;
|
|
350
|
+
return <span text={`${name} is ${age} years old and ${active ? "active" : "inactive"}`} />;
|
|
351
|
+
}}
|
|
352
|
+
/>
|
|
353
|
+
</div>
|
|
354
|
+
</cx>
|
|
355
|
+
);
|
|
356
|
+
|
|
357
|
+
let store = new Store({
|
|
358
|
+
data: {
|
|
359
|
+
user: {
|
|
360
|
+
name: "John",
|
|
361
|
+
age: 30,
|
|
362
|
+
active: true,
|
|
363
|
+
},
|
|
364
|
+
},
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
const component = await createTestRenderer(store, widget);
|
|
368
|
+
|
|
369
|
+
let tree = component.toJSON();
|
|
370
|
+
assert.deepEqual(tree, {
|
|
371
|
+
type: "div",
|
|
372
|
+
props: {},
|
|
373
|
+
children: [
|
|
374
|
+
{
|
|
375
|
+
type: "span",
|
|
376
|
+
props: {},
|
|
377
|
+
children: ["John is 30 years old and active"],
|
|
378
|
+
},
|
|
379
|
+
],
|
|
380
|
+
});
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
it("infers types from literal params", async () => {
|
|
384
|
+
// When using literal values, TypeScript can infer their types
|
|
385
|
+
let widget = (
|
|
386
|
+
<cx>
|
|
387
|
+
<div>
|
|
388
|
+
<ContentResolver
|
|
389
|
+
params={{ count: 42, label: "items" }}
|
|
390
|
+
onResolve={(params) => {
|
|
391
|
+
// params.count is inferred as number
|
|
392
|
+
// params.label is inferred as string
|
|
393
|
+
const count: number = params.count;
|
|
394
|
+
const label: string = params.label;
|
|
395
|
+
return <span text={`${count} ${label}`} />;
|
|
396
|
+
}}
|
|
397
|
+
/>
|
|
398
|
+
</div>
|
|
399
|
+
</cx>
|
|
400
|
+
);
|
|
401
|
+
|
|
402
|
+
let store = new Store();
|
|
403
|
+
|
|
404
|
+
const component = await createTestRenderer(store, widget);
|
|
405
|
+
|
|
406
|
+
let tree = component.toJSON();
|
|
407
|
+
assert.deepEqual(tree, {
|
|
408
|
+
type: "div",
|
|
409
|
+
props: {},
|
|
410
|
+
children: [
|
|
411
|
+
{
|
|
412
|
+
type: "span",
|
|
413
|
+
props: {},
|
|
414
|
+
children: ["42 items"],
|
|
415
|
+
},
|
|
416
|
+
],
|
|
417
|
+
});
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
it("supports simple Prop params (non-structured)", async () => {
|
|
421
|
+
const model = createAccessorModelProxy<TestModel>();
|
|
422
|
+
|
|
423
|
+
// Test using a single AccessorChain as params instead of an object
|
|
424
|
+
let widget = (
|
|
425
|
+
<cx>
|
|
426
|
+
<div>
|
|
427
|
+
<ContentResolver
|
|
428
|
+
params={model.user.name}
|
|
429
|
+
onResolve={(name) => {
|
|
430
|
+
// name should be typed as string (from AccessorChain<string>)
|
|
431
|
+
const typedName: string = name;
|
|
432
|
+
return <span text={`Hello, ${typedName}!`} />;
|
|
433
|
+
}}
|
|
434
|
+
/>
|
|
435
|
+
</div>
|
|
436
|
+
</cx>
|
|
437
|
+
);
|
|
438
|
+
|
|
439
|
+
let store = new Store({
|
|
440
|
+
data: {
|
|
441
|
+
user: {
|
|
442
|
+
name: "Alice",
|
|
443
|
+
age: 25,
|
|
444
|
+
active: true,
|
|
445
|
+
},
|
|
446
|
+
},
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
const component = await createTestRenderer(store, widget);
|
|
450
|
+
|
|
451
|
+
let tree = component.toJSON();
|
|
452
|
+
assert.deepEqual(tree, {
|
|
453
|
+
type: "div",
|
|
454
|
+
props: {},
|
|
455
|
+
children: [
|
|
456
|
+
{
|
|
457
|
+
type: "span",
|
|
458
|
+
props: {},
|
|
459
|
+
children: ["Hello, Alice!"],
|
|
460
|
+
},
|
|
461
|
+
],
|
|
462
|
+
});
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
it("supports simple bind() as params", async () => {
|
|
466
|
+
let widget = (
|
|
467
|
+
<cx>
|
|
468
|
+
<div>
|
|
469
|
+
<ContentResolver
|
|
470
|
+
params={bind("count")}
|
|
471
|
+
onResolve={(count) => {
|
|
472
|
+
return <span text={`Count: ${count}`} />;
|
|
473
|
+
}}
|
|
474
|
+
/>
|
|
475
|
+
</div>
|
|
476
|
+
</cx>
|
|
477
|
+
);
|
|
478
|
+
|
|
479
|
+
let store = new Store({
|
|
480
|
+
data: {
|
|
481
|
+
count: 42,
|
|
482
|
+
},
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
const component = await createTestRenderer(store, widget);
|
|
486
|
+
|
|
487
|
+
let tree = component.toJSON();
|
|
488
|
+
assert.deepEqual(tree, {
|
|
489
|
+
type: "div",
|
|
490
|
+
props: {},
|
|
491
|
+
children: [
|
|
492
|
+
{
|
|
493
|
+
type: "span",
|
|
494
|
+
props: {},
|
|
495
|
+
children: ["Count: 42"],
|
|
496
|
+
},
|
|
497
|
+
],
|
|
498
|
+
});
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
it("resolves Selector<string> | null to string | null", async () => {
|
|
502
|
+
const text: string | null = "Hello {user.name}";
|
|
503
|
+
|
|
504
|
+
let widget = (
|
|
505
|
+
<cx>
|
|
506
|
+
<div>
|
|
507
|
+
<ContentResolver
|
|
508
|
+
params={{
|
|
509
|
+
text: text ? stringTemplate(text) : null,
|
|
510
|
+
}}
|
|
511
|
+
onResolve={(params) => {
|
|
512
|
+
// params.text should be string | null
|
|
513
|
+
const typedText: string | null = params.text;
|
|
514
|
+
// @ts-expect-error - params.text should not be 'any' or broader type
|
|
515
|
+
const shouldFail: number = params.text;
|
|
516
|
+
return <span text={typedText ?? "no text"} />;
|
|
517
|
+
}}
|
|
518
|
+
/>
|
|
519
|
+
</div>
|
|
520
|
+
</cx>
|
|
521
|
+
);
|
|
522
|
+
|
|
523
|
+
let store = new Store({
|
|
524
|
+
data: {
|
|
525
|
+
user: { name: "World", age: 30, active: true },
|
|
526
|
+
},
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
const component = await createTestRenderer(store, widget);
|
|
530
|
+
let tree = component.toJSON();
|
|
531
|
+
assert.deepEqual(tree, {
|
|
532
|
+
type: "div",
|
|
533
|
+
props: {},
|
|
534
|
+
children: [{ type: "span", props: {}, children: ["Hello World"] }],
|
|
535
|
+
});
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
it("resolves Prop<string> to string (not any)", async () => {
|
|
539
|
+
const model = createAccessorModelProxy<TestModel>();
|
|
540
|
+
|
|
541
|
+
// When params is typed as Prop<string>, onResolve should receive string (not any)
|
|
542
|
+
// This tests that the union type Prop<T> correctly extracts T
|
|
543
|
+
const typedParam: Prop<string> = model.user.name;
|
|
544
|
+
|
|
545
|
+
let widget = (
|
|
546
|
+
<cx>
|
|
547
|
+
<div>
|
|
548
|
+
<ContentResolver
|
|
549
|
+
params={typedParam}
|
|
550
|
+
onResolve={(name) => {
|
|
551
|
+
// name should be typed as string, not any
|
|
552
|
+
const typedName: string = name;
|
|
553
|
+
return <span text={`Hello, ${typedName}!`} />;
|
|
554
|
+
}}
|
|
555
|
+
/>
|
|
556
|
+
</div>
|
|
557
|
+
</cx>
|
|
558
|
+
);
|
|
559
|
+
|
|
560
|
+
let store = new Store({
|
|
561
|
+
data: {
|
|
562
|
+
user: {
|
|
563
|
+
name: "Bob",
|
|
564
|
+
age: 35,
|
|
565
|
+
active: false,
|
|
566
|
+
},
|
|
567
|
+
},
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
const component = await createTestRenderer(store, widget);
|
|
571
|
+
|
|
572
|
+
let tree = component.toJSON();
|
|
573
|
+
assert.deepEqual(tree, {
|
|
574
|
+
type: "div",
|
|
575
|
+
props: {},
|
|
576
|
+
children: [
|
|
577
|
+
{
|
|
578
|
+
type: "span",
|
|
579
|
+
props: {},
|
|
580
|
+
children: ["Hello, Bob!"],
|
|
581
|
+
},
|
|
582
|
+
],
|
|
583
|
+
});
|
|
584
|
+
});
|
|
585
|
+
});
|