From c310637879a1e45f4df01fb87247cd3607bae7e2 Mon Sep 17 00:00:00 2001 From: rkyle35242 Date: Tue, 9 Apr 2024 17:17:57 -0400 Subject: [PATCH] support scaling the display scale of arc segments (#70) * support scaling the display scale of arc segments * Update snapshots --------- Co-authored-by: Jakhongir Khusanov <25942541+jkhusanov@users.noreply.github.com> --- src/SegmentedArc.js | 37 ++- src/__tests__/SegmentedArc.spec.js | 58 +++++ .../__snapshots__/SegmentedArc.spec.js.snap | 218 ++++++++++++++++++ 3 files changed, 302 insertions(+), 11 deletions(-) diff --git a/src/SegmentedArc.js b/src/SegmentedArc.js index 2006544..c06f972 100644 --- a/src/SegmentedArc.js +++ b/src/SegmentedArc.js @@ -67,23 +67,38 @@ export const SegmentedArc = ({ }; const _ensureDefaultSegmentScale = () => { - let segmentsWithoutScale = segments.filter(segment => !segment.scale); - let allocatedScale = segments.reduce((total, current) => total + (current.scale || 0), 0); - let defaultArcScale = (1 - allocatedScale) / segmentsWithoutScale.length; + const segmentsWithoutScale = segments.filter(segment => !segment.scale); + const allocatedScale = segments.reduce((total, current) => total + (current.scale || 0), 0); + const defaultArcScale = (1 - allocatedScale) / segmentsWithoutScale.length; segmentsWithoutScale.forEach(segment => (segment.scale = defaultArcScale)); }; + const _ensureDefaultArcScale = () => { + const segmentsWithoutArcDegreeScaleLength = segments.filter( + segment => typeof segment.arcDegreeScale !== 'number' + ).length; + const totalProvidedArcDegreeScale = segments.reduce((acc, segment) => acc + (segment.arcDegreeScale ?? 0), 0); + segments.forEach(segment => { + if (typeof segment.arcDegreeScale !== 'number') + segment.arcDegreeScale = (1 - totalProvidedArcDegreeScale) / segmentsWithoutArcDegreeScaleLength; + }); + }; + let remainingValue = fillValue; _ensureDefaultSegmentScale(); + _ensureDefaultArcScale(); + + let arcs = []; + segments.forEach((segment, index) => { + const arcDegreeScale = segment.arcDegreeScale; + const previousSegmentEnd = !!index ? arcs[index - 1].end : arcsStart; + const start = previousSegmentEnd + (!!index ? spaceBetweenSegments : 0); + const end = (arcDegree - totalSpacing) * arcDegreeScale + start; - const arcs = segments.map((segment, index) => { - const scale = segment.scale; - const start = arcsStart + index * (arcSegmentDegree + spaceBetweenSegments); - const end = arcSegmentDegree + start; - const valueMax = 100 * scale; + const valueMax = 100 * segment.scale; const effectiveScaledValue = Math.min(remainingValue, valueMax); - const scaledPercentage = effectiveScaledValue / (scale * 100); + const scaledPercentage = effectiveScaledValue / valueMax; const filled = start + scaledPercentage * (end - start); remainingValue -= effectiveScaledValue; @@ -99,7 +114,7 @@ export const SegmentedArc = ({ data: segment.data }; - return newArc; + arcs.push(newArc); }); const lastFilledSegment = arcs.find(a => a.filled !== a.end) || arcs[arcs.length - 1]; @@ -109,7 +124,7 @@ export const SegmentedArc = ({ if (animationRunning.current) return; if (!isAnimated) return; animationRunning.current = true; - new Animated.timing(arcAnimatedValue, { + Animated.timing(arcAnimatedValue, { toValue: lastFilledSegment.filled, duration: animationDuration, delay: animationDelay, diff --git a/src/__tests__/SegmentedArc.spec.js b/src/__tests__/SegmentedArc.spec.js index 515c582..f6fe786 100644 --- a/src/__tests__/SegmentedArc.spec.js +++ b/src/__tests__/SegmentedArc.spec.js @@ -96,4 +96,62 @@ describe('SegmentedArc', () => { wrapper = getWrapper(props); expect(wrapper.getByTestId(testId).props).toMatchSnapshot(); }); + + it('renders segments with arc degree scales', () => { + const segments = [ + { + scale: 0.25, + arcDegreeScale: 0.5, + filledColor: '#FF0000', + emptyColor: '#F2F3F5', + data: { label: 'First Segment' } + }, + { + scale: 0.02, + arcDegreeScale: 0.3, + filledColor: '#FFA500', + emptyColor: '#F2F3F5', + data: { label: 'Second Segment' } + }, + { + scale: 0.02, + arcDegreeScale: 0.2, + filledColor: '#FFA500', + emptyColor: '#F2F3F5', + data: { label: 'Third Segment' } + } + ]; + wrapper = getWrapper({ ...props, segments }); + expect(wrapper.getByTestId(testId).props).toMatchSnapshot(); + expect(Animated.timing).toHaveBeenCalledTimes(1); + expect(Easing.out).toHaveBeenCalledWith(Easing.ease); + }); + + it('renders with ensured arc degree scales when missing from segments', () => { + const segments = [ + { + scale: 0.25, + arcDegreeScale: 0.5, + filledColor: '#FF0000', + emptyColor: '#F2F3F5', + data: { label: 'First Segment' } + }, + { + scale: 0.02, + filledColor: '#FFA500', + emptyColor: '#F2F3F5', + data: { label: 'Second Segment' } + }, + { + scale: 0.02, + filledColor: '#FFA500', + emptyColor: '#F2F3F5', + data: { label: 'Third Segment' } + } + ]; + wrapper = getWrapper({ ...props, segments }); + expect(wrapper.getByTestId(testId).props).toMatchSnapshot(); + expect(Animated.timing).toHaveBeenCalledTimes(1); + expect(Easing.out).toHaveBeenCalledWith(Easing.ease); + }); }); diff --git a/src/__tests__/__snapshots__/SegmentedArc.spec.js.snap b/src/__tests__/__snapshots__/SegmentedArc.spec.js.snap index 822d644..21e4c7b 100644 --- a/src/__tests__/__snapshots__/SegmentedArc.spec.js.snap +++ b/src/__tests__/__snapshots__/SegmentedArc.spec.js.snap @@ -384,6 +384,224 @@ exports[`SegmentedArc renders default 1`] = ` } `; +exports[`SegmentedArc renders segments with arc degree scales 1`] = ` +{ + "children": [ + + + + + + + + , + undefined, + ], + "style": { + "alignItems": "center", + }, + "testID": "container", +} +`; + +exports[`SegmentedArc renders with ensured arc degree scales when missing from segments 1`] = ` +{ + "children": [ + + + + + + + + , + undefined, + ], + "style": { + "alignItems": "center", + }, + "testID": "container", +} +`; + exports[`SegmentedArc renders with middle content 1`] = ` { "children": [