From 03285c0bb20534c9a909026c78eab082abbe4c36 Mon Sep 17 00:00:00 2001 From: huan-qiu Date: Wed, 27 Sep 2023 01:00:57 +1300 Subject: [PATCH 1/7] fix: render full list for responsive mode with width set to 0, then calc display --- src/Overflow.tsx | 52 ++++++++++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/src/Overflow.tsx b/src/Overflow.tsx index de6241b..e049858 100644 --- a/src/Overflow.tsx +++ b/src/Overflow.tsx @@ -39,9 +39,6 @@ export interface OverflowProps extends React.HTMLAttributes { /** @private This API may be refactor since not well design */ onVisibleChange?: (visibleCount: number) => void; - - /** When set to `full`, ssr will render full items by default and remove at client side */ - ssr?: 'full'; } function defaultRenderRest(omittedItems: ItemType[]) { @@ -59,7 +56,6 @@ function Overflow( renderRawItem, itemKey, itemWidth = 10, - ssr, style, className, maxCount, @@ -72,8 +68,6 @@ function Overflow( ...restProps } = props; - const fullySSR = ssr === 'full'; - const notifyEffectUpdate = useBatcher(); const [containerWidth, setContainerWidth] = useEffectState( @@ -104,7 +98,7 @@ function Overflow( const [displayCount, setDisplayCount] = useState(null); const mergedDisplayCount = React.useMemo(() => { - if (displayCount === null && fullySSR) { + if (displayCount === null) { return Number.MAX_SAFE_INTEGER; } @@ -134,9 +128,7 @@ function Overflow( let items = data; if (shouldResponsive) { - if (containerWidth === null && fullySSR) { - items = data; - } else { + if (containerWidth !== null) { items = data.slice( 0, Math.min(data.length, mergedContainerWidth / itemWidth), @@ -147,7 +139,14 @@ function Overflow( } return items; - }, [data, itemWidth, containerWidth, maxCount, shouldResponsive]); + }, [ + data, + itemWidth, + containerWidth, + maxCount, + shouldResponsive, + mergedContainerWidth, + ]); const omittedItems = useMemo(() => { if (shouldResponsive) { @@ -250,12 +249,7 @@ function Overflow( } for (let i = 0; i < len; i += 1) { - let currentItemWidth = getItemWidth(i); - - // Fully will always render - if (fullySSR) { - currentItemWidth = currentItemWidth || 0; - } + const currentItemWidth = getItemWidth(i); // Break since data not ready if (currentItemWidth === undefined) { @@ -301,6 +295,7 @@ function Overflow( // ================================ Render ================================ const displayRest = restReady && !!omittedItems.length; + const isResponsiveAndFirstRender = isResponsive && containerWidth === null; let suffixStyle: React.CSSProperties = {}; if (suffixFixedStart !== null && shouldResponsive) { @@ -316,12 +311,26 @@ function Overflow( responsive: shouldResponsive, component: itemComponent, invalidate, + style: isResponsiveAndFirstRender + ? { + maxWidth: '0px', + padding: '0px', + margin: '0px', + borderWidth: '0px', + overflowX: 'hidden', + } + : undefined, }; // >>>>> Choice render fun by `renderRawItem` const internalRenderItemNode = renderRawItem ? (item: ItemType, index: number) => { const key = getKey(item, index); + const isIdxCheckPass = index <= mergedDisplayCount; + // in responsive case, item's `display` can be set to true only if its corresponding width is valid and pass the index check + const shouldDisplay = isResponsive + ? isIdxCheckPass && getItemWidth(index) > 0 + : isIdxCheckPass; return ( ( item, itemKey: key, registerSize, - display: index <= mergedDisplayCount, + display: shouldDisplay, }} > {renderRawItem(item, index)} @@ -341,6 +350,11 @@ function Overflow( } : (item: ItemType, index: number) => { const key = getKey(item, index); + const isIdxCheckPass = index <= mergedDisplayCount; + + const shouldDisplay = isResponsive + ? isIdxCheckPass && getItemWidth(index) > 0 + : isIdxCheckPass; return ( ( renderItem={mergedRenderItem} itemKey={key} registerSize={registerSize} - display={index <= mergedDisplayCount} + display={shouldDisplay} /> ); }; From 43c5a0790298e70a40d7c7febb632372a2084b47 Mon Sep 17 00:00:00 2001 From: huan-qiu Date: Wed, 27 Sep 2023 12:25:16 +1300 Subject: [PATCH 2/7] fix: render but hide all items by max-width 0 when on first render under responsive mode --- src/Overflow.tsx | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/Overflow.tsx b/src/Overflow.tsx index e049858..da4d1ae 100644 --- a/src/Overflow.tsx +++ b/src/Overflow.tsx @@ -313,11 +313,11 @@ function Overflow( invalidate, style: isResponsiveAndFirstRender ? { - maxWidth: '0px', - padding: '0px', - margin: '0px', - borderWidth: '0px', - overflowX: 'hidden', + maxWidth: 0, + padding: 0, + margin: 0, + borderWidth: 0, + 'overflow-x': 'hidden', } : undefined, }; @@ -327,9 +327,11 @@ function Overflow( ? (item: ItemType, index: number) => { const key = getKey(item, index); const isIdxCheckPass = index <= mergedDisplayCount; - // in responsive case, item's `display` can be set to true only if its corresponding width is valid and pass the index check + // in responsive case, item's `display` can be set to `true` when: + // 1) at initial render; 2) its corresponding width is valid and pass the index check const shouldDisplay = isResponsive - ? isIdxCheckPass && getItemWidth(index) > 0 + ? isResponsiveAndFirstRender || + (isIdxCheckPass && getItemWidth(index) > 0) : isIdxCheckPass; return ( @@ -353,7 +355,8 @@ function Overflow( const isIdxCheckPass = index <= mergedDisplayCount; const shouldDisplay = isResponsive - ? isIdxCheckPass && getItemWidth(index) > 0 + ? isResponsiveAndFirstRender || + (isIdxCheckPass && getItemWidth(index) > 0) : isIdxCheckPass; return ( From f52194bdcfac8d758d98bdf267d33b5d1241fc07 Mon Sep 17 00:00:00 2001 From: huan-qiu Date: Wed, 27 Sep 2023 12:27:17 +1300 Subject: [PATCH 3/7] test: update ssr test case --- tests/__snapshots__/ssr.spec.tsx.snap | 6 +++--- tests/ssr.spec.tsx | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/__snapshots__/ssr.spec.tsx.snap b/tests/__snapshots__/ssr.spec.tsx.snap index 494b349..e7cc6f9 100644 --- a/tests/__snapshots__/ssr.spec.tsx.snap +++ b/tests/__snapshots__/ssr.spec.tsx.snap @@ -6,20 +6,20 @@ exports[`Overflow.SSR basic 1`] = ` >
Label 0
Label 1
diff --git a/tests/ssr.spec.tsx b/tests/ssr.spec.tsx index 75e5038..c7a850c 100644 --- a/tests/ssr.spec.tsx +++ b/tests/ssr.spec.tsx @@ -34,7 +34,6 @@ describe('Overflow.SSR', () => { data={getData(2)} renderItem={renderItem} maxCount="responsive" - ssr="full" />, ); From 709c9f3e282cb099e876d1a4b34799f39696cf15 Mon Sep 17 00:00:00 2001 From: huan-qiu Date: Wed, 27 Sep 2023 12:28:20 +1300 Subject: [PATCH 4/7] chore: rename ssr.spec to seo.spec --- tests/{ssr.spec.tsx => seo.spec.tsx} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/{ssr.spec.tsx => seo.spec.tsx} (100%) diff --git a/tests/ssr.spec.tsx b/tests/seo.spec.tsx similarity index 100% rename from tests/ssr.spec.tsx rename to tests/seo.spec.tsx From 1bf28539fd86d962778776b67790a7b721b00848 Mon Sep 17 00:00:00 2001 From: huan-qiu Date: Wed, 27 Sep 2023 14:52:12 +1300 Subject: [PATCH 5/7] fix: should also render suffix item if any invisibly --- src/Overflow.tsx | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/Overflow.tsx b/src/Overflow.tsx index da4d1ae..80fe872 100644 --- a/src/Overflow.tsx +++ b/src/Overflow.tsx @@ -296,8 +296,17 @@ function Overflow( // ================================ Render ================================ const displayRest = restReady && !!omittedItems.length; const isResponsiveAndFirstRender = isResponsive && containerWidth === null; + const responsiveAndFirstRenderStyle: React.CSSProperties = { + maxWidth: 0, + padding: 0, + margin: 0, + borderWidth: 0, + overflowX: 'hidden', + }; - let suffixStyle: React.CSSProperties = {}; + let suffixStyle: React.CSSProperties = isResponsiveAndFirstRender + ? responsiveAndFirstRenderStyle + : {}; if (suffixFixedStart !== null && shouldResponsive) { suffixStyle = { position: 'absolute', @@ -312,13 +321,7 @@ function Overflow( component: itemComponent, invalidate, style: isResponsiveAndFirstRender - ? { - maxWidth: 0, - padding: 0, - margin: 0, - borderWidth: 0, - 'overflow-x': 'hidden', - } + ? responsiveAndFirstRenderStyle : undefined, }; From 68bde93985481f5b67017d1f79d421bfc04209e4 Mon Sep 17 00:00:00 2001 From: huan-qiu Date: Wed, 27 Sep 2023 14:55:30 +1300 Subject: [PATCH 6/7] test: add snapshot target suffix --- tests/__snapshots__/seo.spec.tsx.snap | 61 +++++++++++++++++++++++++++ tests/__snapshots__/ssr.spec.tsx.snap | 27 ------------ tests/seo.spec.tsx | 13 ++++++ 3 files changed, 74 insertions(+), 27 deletions(-) create mode 100644 tests/__snapshots__/seo.spec.tsx.snap delete mode 100644 tests/__snapshots__/ssr.spec.tsx.snap diff --git a/tests/__snapshots__/seo.spec.tsx.snap b/tests/__snapshots__/seo.spec.tsx.snap new file mode 100644 index 0000000..8abed1c --- /dev/null +++ b/tests/__snapshots__/seo.spec.tsx.snap @@ -0,0 +1,61 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Overflow.SSR basic 1`] = ` +
+
+ Label 0 +
+
+ Label 1 +
+ +
+`; + +exports[`Overflow.SSR with suffix 1`] = ` +
+
+ Label 0 +
+
+ Label 1 +
+ +
+ + I am a suffix + +
+
+`; diff --git a/tests/__snapshots__/ssr.spec.tsx.snap b/tests/__snapshots__/ssr.spec.tsx.snap deleted file mode 100644 index e7cc6f9..0000000 --- a/tests/__snapshots__/ssr.spec.tsx.snap +++ /dev/null @@ -1,27 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Overflow.SSR basic 1`] = ` -
-
- Label 0 -
-
- Label 1 -
- -
-`; diff --git a/tests/seo.spec.tsx b/tests/seo.spec.tsx index c7a850c..4e0508d 100644 --- a/tests/seo.spec.tsx +++ b/tests/seo.spec.tsx @@ -39,4 +39,17 @@ describe('Overflow.SSR', () => { expect(wrapper).toMatchSnapshot(); }); + + it('with suffix', () => { + const wrapper = render( + + data={getData(2)} + renderItem={renderItem} + maxCount="responsive" + suffix={I am a suffix} + />, + ); + + expect(wrapper).toMatchSnapshot(); + }); }); From cbf13ad788fd89b5996ffb3460f99215208a28c4 Mon Sep 17 00:00:00 2001 From: huan-qiu Date: Wed, 27 Sep 2023 15:27:23 +1300 Subject: [PATCH 7/7] test: add cases for maxCount number and invalidate --- tests/__snapshots__/seo.spec.tsx.snap | 85 ++++++++++++++++++++++++++- tests/seo.spec.tsx | 69 ++++++++++++++-------- 2 files changed, 128 insertions(+), 26 deletions(-) diff --git a/tests/__snapshots__/seo.spec.tsx.snap b/tests/__snapshots__/seo.spec.tsx.snap index 8abed1c..160b928 100644 --- a/tests/__snapshots__/seo.spec.tsx.snap +++ b/tests/__snapshots__/seo.spec.tsx.snap @@ -1,6 +1,85 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Overflow.SSR basic 1`] = ` +exports[`Overflow.SEO invalidate number with suffix 1`] = ` +
+
+ Label 0 +
+
+ Label 1 +
+
+ Label 2 +
+
+ Label 3 +
+
+ + I am a suffix + +
+
+`; + +exports[`Overflow.SEO maxCount number with suffix 1`] = ` +
+
+ Label 0 +
+
+ Label 1 +
+
+ Label 2 +
+
+ Label 3 +
+
+ + 2 ... +
+
+ + I am a suffix + +
+
+`; + +exports[`Overflow.SEO responsive 1`] = `
@@ -26,7 +105,7 @@ exports[`Overflow.SSR basic 1`] = `
`; -exports[`Overflow.SSR with suffix 1`] = ` +exports[`Overflow.SEO responsive with suffix 1`] = `
@@ -54,7 +133,7 @@ exports[`Overflow.SSR with suffix 1`] = ` style="opacity:1;order:9007199254740991;max-width:0;padding:0;margin:0;border-width:0;overflow-x:hidden" > - I am a suffix + I am a suffix
diff --git a/tests/seo.spec.tsx b/tests/seo.spec.tsx index 4e0508d..96f5970 100644 --- a/tests/seo.spec.tsx +++ b/tests/seo.spec.tsx @@ -1,18 +1,25 @@ import React from 'react'; import { render } from 'enzyme'; -import { act } from 'react-dom/test-utils'; import Overflow from '../src'; +import type { OverflowProps } from '../src'; interface ItemType { label: React.ReactNode; key: React.Key; } +interface CaseConf { + name: string; + dataLength: number; + maxCount: OverflowProps['maxCount']; + suffix?: boolean; +} + function renderItem(item: ItemType) { return item.label; } -describe('Overflow.SSR', () => { +describe('Overflow.SEO', () => { function getData(count: number) { return new Array(count).fill(undefined).map((_, index) => ({ label: `Label ${index}`, @@ -28,28 +35,44 @@ describe('Overflow.SSR', () => { jest.useRealTimers(); }); - it('basic', () => { - const wrapper = render( - - data={getData(2)} - renderItem={renderItem} - maxCount="responsive" - />, - ); + const testCases: CaseConf[] = [ + { + name: 'responsive', + dataLength: 2, + maxCount: 'responsive', + }, + { + name: 'responsive with suffix', + dataLength: 2, + maxCount: 'responsive', + suffix: true, + }, + { + name: 'maxCount number with suffix', + dataLength: 6, + maxCount: 4, + suffix: true, + }, + { + name: 'invalidate number with suffix', + dataLength: 4, + maxCount: 'invalidate', + suffix: true, + }, + ]; - expect(wrapper).toMatchSnapshot(); - }); + testCases.forEach(({ name, dataLength, maxCount: maxCountVal, suffix }) => { + it(`${name}`, () => { + const wrapper = render( + + data={getData(dataLength)} + renderItem={renderItem} + maxCount={maxCountVal} + suffix={suffix && I am a suffix } + />, + ); - it('with suffix', () => { - const wrapper = render( - - data={getData(2)} - renderItem={renderItem} - maxCount="responsive" - suffix={I am a suffix} - />, - ); - - expect(wrapper).toMatchSnapshot(); + expect(wrapper).toMatchSnapshot(); + }); }); });